# Document Parse API

> POST /api/v1/documents/parse — turn a PDF, Word file, spreadsheet or image into Markdown, text, layout blocks and chunks with your own Nace key.


# Document Parse API

Turn a file into Markdown, plain text, layout blocks and optional chunks for retrieval. OCR runs when the file needs it.

::endpoint{method="POST" path="/api/v1/documents/parse"}

:::note
Document parsing is **bring-your-own-key for now**. It runs on your own [Nace](https://console.nace.ai) API key (`nace_sk_…`) and your own Nace credit, and AnyRouter charges nothing for it. Add the key under Provider keys in the dashboard first. Without one the call returns `400` with code `byok_required`.
:::

Authenticate with an **LLM API key** (`sk-ar-v1-…`) from [/dashboard/keys](https://anyrouter.dev/dashboard/keys), sent as `Authorization: Bearer …`.

## Request

The body is JSON and is sent to Nace unchanged, so every parse option in the [Nace Parse reference](https://console.nace.ai/docs/perception/parse) works here.

| Field | Type | Required | Description |
|---|---|---|---|
| `source` | object | yes | The file. `{"type":"url","url":"https://…","file_name":"report.pdf"}`, a `workspace_file`, or a finished `parse_result`. A `url` must start with `https://` and `file_name` needs an extension. |
| `page_ranges` | `{start,end}[]` | no | One-based, inclusive page ranges. Defaults to all pages. |
| `parse_mode` | `low` \| `medium` \| `high` | no | Effort. Defaults to `low`. |
| `output.formats` | string[] | no | Any of `markdown`, `text`, `blocks`. Defaults to `markdown` and `blocks`. |
| `chunking.strategy` | `none` \| `page` \| `section` | no | Returns retrieval-sized `chunks`. Defaults to `none`. |

The request body can be up to 256 KiB. Send the file by `https://` URL, or upload it to Nace and name it as a `workspace_file`.

Query parameter `wait_seconds` (integer, 0 to 60) holds the call open until the parse finishes. An optional `Idempotency-Key` header makes a retry return the same job instead of a second one.

```bash
curl "https://anyrouter.dev/api/v1/documents/parse?wait_seconds=60" \
  -H "Authorization: Bearer $ANYROUTER_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: report-2026-10-01" \
  -d '{
    "source": {
      "type": "url",
      "url": "https://example.com/report.pdf",
      "file_name": "report.pdf"
    },
    "page_ranges": [{ "start": 1, "end": 3 }],
    "output": { "formats": ["markdown"] }
  }'
```

## Response

**200** when the parse finished within `wait_seconds`:

```json
{
  "result_type": "parse",
  "file_name": "report.pdf",
  "lane": "pdf",
  "document": { "page_count": 3, "markdown": "# Annual Report\n\n...", "text": null, "chunks": [] },
  "ocr_applied": false,
  "units": 3,
  "disclosures": []
}
```

**202** when it is still running (or no `wait_seconds` was sent). The body is the job, for example `{"job_id":"…","status":"queued"}`. Read it from Nace with the same key, or send the same request again with the same `Idempotency-Key` and a `wait_seconds` to pick it up.

The answer is relayed as Nace sends it. `X-AnyRouter-Documents-Pages` carries the page count when it is known.

:::warning
A parse result larger than 8 MiB is refused with `502` and code `upstream_response_too_large`. Narrow `page_ranges`, or drop `blocks`, `chunks` and OCR data from `output`.
:::

## Errors

| Status | Code | Meaning |
|---|---|---|
| 400 | `byok_required` | No Nace key on your account. Add one, then retry. |
| 400 | `missing_required_fields`, `invalid_request` | Missing or invalid `source`, or `wait_seconds` outside 0 to 60. |
| 413 | `request_too_large` | Body over 256 KiB. |
| 422, 429, … | Nace's own | Nace's error, with its message, for a bad file type, a rate limit or an empty balance. |
| 502 | `upstream_response_too_large` | Result over 8 MiB. |
| 504 | `upstream_timeout` | No answer in time. Retry with the same `Idempotency-Key`. |

## Pricing

Nace bills pages to your own Nace account at its rate (parse is one credit per page). AnyRouter adds no charge; the request shows in [Request Logs](/features/request-logs) with no cost.
