# OCR API

> Extract text from images and documents with AnyRouter's OCR endpoint.


# OCR API

`POST /api/v1/ocr/extract` extracts text from an image or document and returns the content as Markdown. It is powered by Cloudflare Workers AI's native document-to-markdown conversion.

> **Authentication:** use an **LLM API key** (`sk-ar-…`) from [/dashboard/keys](/dashboard/keys). Management keys (`ak_…`) are not accepted.

## Request

The endpoint accepts two content types.

### JSON body

```http
POST /api/v1/ocr/extract HTTP/1.1
Authorization: Bearer sk-ar-your-key
Content-Type: application/json

{
  "image": "data:image/png;base64,iVBORw0KGgoAAAANS...",
  "filename": "receipt.png"
}
```

### Multipart upload

```http
POST /api/v1/ocr/extract HTTP/1.1
Authorization: Bearer sk-ar-your-key
Content-Type: multipart/form-data; boundary=----boundary

------boundary
Content-Disposition: form-data; name="image"; filename="receipt.png"
Content-Type: image/png

<binary file bytes>
------boundary--
```

## Request body

| Field | Type | Required | Description |
|---|---|---|---|
| `image` | string | yes (JSON only) | Base64-encoded image bytes, or a data URL (`data:<mime>;base64,<data>`). |
| `filename` | string | no (JSON only) | Optional filename hint. Helps the extractor detect the file type. Defaults to `image.png` when not set. |

For **multipart/form-data**, include the file in a field named `image`. The filename is taken from the `Content-Disposition` header.

### Supported formats

The endpoint accepts any image or document format that Cloudflare Workers AI's `toMarkdown` conversion supports, including PNG, JPEG, GIF, WebP, BMP, TIFF, and PDF.

### Size limit

The maximum accepted file size is **5 MiB**. Requests that exceed this limit receive a `413` response.

## Response

```json
{
  "text": "# Invoice #1042\n\n**Date:** 2026-06-15  \n**Amount:** $120.00\n\n## Line items\n\n| Description | Price |\n|---|---|\n| API credits | $120.00 |",
  "format": "markdown",
  "model": "cloudflare/ai-to-markdown",
  "tokens": 68
}
```

| Field | Type | Description |
|---|---|---|
| `text` | string | Extracted content in Markdown format. |
| `format` | string | Always `"markdown"`. |
| `model` | string | Always `"cloudflare/ai-to-markdown"`. |
| `tokens` | number | Token count consumed by the extraction. |

## cURL

```bash
# JSON — base64 data URL
curl https://anyrouter.dev/api/v1/ocr/extract \
  -H "Authorization: Bearer $ANYROUTER_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{\"image\": \"$(base64 -i receipt.png | tr -d '\n')\"}"

# Multipart — raw file upload
curl https://anyrouter.dev/api/v1/ocr/extract \
  -H "Authorization: Bearer $ANYROUTER_API_KEY" \
  -F "image=@receipt.png"
```

## TypeScript

```typescript
// JSON path — base64 encode the image first
async function extractText(imageBuffer: ArrayBuffer): Promise<string> {
  const base64 = btoa(String.fromCharCode(...new Uint8Array(imageBuffer)))

  const response = await fetch("https://anyrouter.dev/api/v1/ocr/extract", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.ANYROUTER_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      image: `data:image/png;base64,${base64}`,
      filename: "document.png",
    }),
  })

  const result = await response.json()
  return result.text
}

// Multipart path — stream the file directly
async function extractTextFromFile(filePath: string): Promise<string> {
  const { createReadStream } = await import("node:fs")
  const form = new FormData()
  form.append("image", new Blob([createReadStream(filePath)]), "document.png")

  const response = await fetch("https://anyrouter.dev/api/v1/ocr/extract", {
    method: "POST",
    headers: { Authorization: `Bearer ${process.env.ANYROUTER_API_KEY}` },
    body: form,
  })

  const result = await response.json()
  return result.text
}
```

## Error responses

| Status | Code | Description |
|---|---|---|
| 400 | `missing_required_fields` | The `image` field is absent or empty. |
| 400 | `invalid_request_body` | The request body is not valid JSON or is not parseable multipart. |
| 400 | `invalid_image_encoding` | The `image` value is not valid base64 or a valid data URL. |
| 400 | `invalid_image_field` | The multipart `image` field is a string, not a file. |
| 401 | `invalid_api_key` | The API key is missing, expired, or invalid. |
| 413 | `image_too_large` | The image exceeds the 5 MiB limit. |
| 500 | `missing_workers_ai_binding` | The Workers AI binding is not configured (server misconfiguration). |
| 502 | `ocr_upstream_failed` | The extraction call failed. Retry the request. |
| 502 | `ocr_extraction_error` | Workers AI returned an error for this file (for example: unsupported format or corrupt data). |

## Related

- [Images API](/docs/api-reference/images.md)
- [Models API](/docs/api-reference/models.md)
