---
title: "Server capability discovery"
method: GET
path: "/v1/capabilities"
tags: ["capabilities"]
---

# Server capability discovery

`GET /v1/capabilities`

## Request

- Base URL: `https://api.fastcrw.com`
- URL: `https://api.fastcrw.com/v1/capabilities`
- Auth: one of:
  - HTTP bearer
  - none

Returns what this opencore instance actually supports: LLM providers, output formats, search features, screenshot/renderer availability, document parsers and enforced limits. Every boolean is derived from the real build features and effective config — a capability is true only when the instance can perform it for a request that supplies no extra credentials. SaaS frontends call this on boot to validate a minimum capability set before sending traffic.

## Response `200`

Capability report for this opencore instance

- object
  - `version` string, required: opencore semver string
  - `llm` object, required
    - `providers` string[], required
    - `supportsBaseUrl` boolean, required: The LLM dispatcher accepts a custom baseUrl, but only as part of a BYOK request: the per-request LLM config that carries it is only built when the request also supplies llmApiKey. Sending baseUrl without a key leaves the server's own endpoint in force (the server never points its own key at a caller-chosen host). POST /v1/extract rejects baseUrl outright — configure [extraction.llm.base_url] server-side for that route. Build-invariant: no config turns this off.
    - `serverKeyConfigured` boolean, required: True when a server-side LLM API key is present. False on BYOK-only deploys (CRW_DISABLE_SERVER_LLM_KEY=1), where a self-hosted instance still accepts a per-request llmApiKey. The managed cloud does not take a caller-supplied key.
    - `maxConcurrency` integer, required: Server-configured LLM fan-out cap; 0 when no server LLM config is present
    - `requireByokHeader` string: Header name required on LLM-touching requests; omitted when no header guard is configured
  - `formats` object, required
    - `supported` string[], required: Output formats this build/config can actually produce. `screenshot` is listed only when a screenshot-capable renderer (chrome / chrome_proxy / playwright) is compiled in and configured.
    - `changeTrackingModes` string[], required
    - `llmRequired` string[], required: Formats that additionally need an LLM: a server key (llm.serverKeyConfigured) or a per-request llmApiKey. Requesting them without one is a hard error, never a silent downgrade. When llm.requireByokHeader is set the server key is NOT sufficient: the scrape and extract paths reject these formats unless the request carries llmApiKey.
    - `changeTrackingModesLlmRequired` string[], required: Change-tracking modes that need an LLM. gitDiff is deterministic; json mode calls an LLM.
  - `search` object, required
    - `supported` boolean, required: POST /v1/search is usable: [search].enabled and search.search_backend_url are set. Configured, not health-probed.
    - `answer` boolean, required: Answer synthesis works WITHOUT a caller-supplied LLM key (search configured AND a server LLM key present). When false but supported is true, a self-hosted instance still answers a request that carries llmApiKey; the managed cloud does not take a caller-supplied key.
    - `summarizeResults` boolean, required: Same gate as answer.
  - `screenshot` object, required
    - `supported` boolean, required: A screenshot-capable renderer (chrome / chrome_proxy / playwright) is compiled in AND configured. LightPanda and Camoufox cannot capture, so an instance with only those reports false and the scrape path fails closed on a screenshot request.
    - `fullPage` boolean, required: Full-page capture (screenshot@fullPage / screenshotFullPage). Same gate as supported.
  - `renderers` object, required
    - `available` string[], required: Renderer tiers this instance constructed, in fallback order, reflecting both the build features and the config. These are the values the per-request `renderer` pin accepts. Mostly JS tiers, plus the JS-less impersonated-http tier when that feature is built in.
    - `mode` 'auto' | 'none' | 'lightpanda' | 'chrome' | 'playwright' | 'camoufox', required: Effective [renderer].mode. Note chrome_proxy is a renderer TIER (it can appear in renderers.available) but not a mode.
    - `renderJsDefault` boolean: Effective [renderer].render_js_default; omitted when unset (auto-detect).
  - `extract` object, required
    - `supported` boolean, required: POST /v1/extract works for a request that carries no credentials of its own. The route is ALWAYS mounted, but it needs an LLM: false means a server LLM key is absent, or llm.requireByokHeader is set (which makes the server key insufficient). false does NOT mean 'not implemented' — a request carrying llmApiKey still extracts on a self-hosted instance.
    - `maxUrls` integer, required: Max URLs accepted per extract request (crawler.max_extract_urls).
    - `perFieldAttribution` boolean, required: Per-field `basis` attribution. This build ships `basis`, so it reports true; the flag reflects build capability, not whether an LLM is configured.
  - `documents` object, required
    - `parsers` string[], required
    - `fileUpload` object, required
      - `supported` boolean, required
      - `endpoint` string, required: Upload path to join onto your API base. /v2/parse is the one path served by every surface: the engine mounts it at the root and under /firecrawl, and the hosted API proxies /v1/* and /v2/* only.
      - `maxBytes` integer, required: The ENFORCED body cap ([document].max_upload_bytes, clamped by the hard ceiling) — the same value the body-limit layer applies.
      - `types` string[], required
      - `ocr` boolean, required
  - `limits` object, required
    - `maxBatchUrls` integer, required: Max URLs per batch-scrape submission (crawler.max_batch_urls).
    - `maxExtractUrls` integer, required: Max URLs per /v1/extract request (crawler.max_extract_urls).
    - `searchDefaultLimit` integer, required: Default /v1/search limit when the request omits it.
    - `searchMaxLimit` integer, required: Hard cap on the /v1/search limit.
    - `maxUploadBytes` integer, required: Enforced upload cap on the parse endpoint, in bytes.

---

[API](https://skmtc.dev/fastcrw/apis/crw-rest-api.md) · [All operations](https://skmtc.dev/fastcrw/apis/crw-rest-api/llms.txt) · [OpenAPI document](https://skmtc.dev/fastcrw/apis/crw-rest-api/revisions/9bc1842aa048?raw)
