Quickstart

Parse your first document with the API.

Before you start, create an API key in OpenParser Studio and choose a PDF, PNG, or JPEG file.

Parse a document

curl -X POST 'https://api.openparser.dev/parse' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H "Idempotency-Key: $(uuidgen 2>/dev/null || openssl rand -hex 16)" \
  -F 'request={"ocr_model":"paddleocr-vl-1.6","output_format":"openparser@1"};type=application/json' \
  -F 'file=@./document.pdf'
openparser parse sync ./document.pdf
const parsed = await client.parse.sync(
  { ocr_model: 'paddleocr-vl-1.6', output_format: 'openparser@1' },
  file,
);
from pathlib import Path

parsed = client.parse.sync(
    {"ocr_model": "paddleocr-vl-1.6", "output_format": "openparser@1"},
    file=Path("document.pdf"),
)

Replace YOUR_API_KEY and ./document.pdf. Direct uploads can be up to 50 MiB.

The request is multipart:

  • request is the parse configuration, sent as application/json.
  • file is the document.

ocr_model is required. The openparser@1 output format is versioned, so its response shape remains stable as the underlying OCR model changes.

Handle the response

Most requests finish with 200 OK and a ParsedDocument:

{
  "output_format": "openparser@1",
  "document_id": "doc_01h2example",
  "provenance": {
    "provider": "paddle",
    "model": "paddleocr-vl-1.6"
  },
  "text": "# Invoice\n\nTotal due: $1,234.56",
  "markdown": "# Invoice\n\nTotal due: $1,234.56\n",
  "pages": [
    {
      "number": 1,
      "width": 1700,
      "height": 2200,
      "unit": "pixel",
      "element_ids": ["elem-title", "elem-total"],
      "reading_order": ["elem-title", "elem-total"]
    }
  ],
  "elements": [
    {
      "id": "elem-title",
      "kind": "text",
      "role": "heading",
      "text": "# Invoice",
      "spans": [{ "start": 0, "end": 9 }]
    },
    {
      "id": "elem-total",
      "kind": "text",
      "role": "paragraph",
      "text": "Total due: $1,234.56",
      "spans": [{ "start": 11, "end": 31 }]
    }
  ],
  "relations": [],
  "text_annotations": [],
  "assets": []
}

Longer requests return 202 Accepted instead. Read the job ID from the response body or follow the Location header:

curl -s 'https://api.openparser.dev/jobs/JOB_ID' \
  -H 'Authorization: Bearer YOUR_API_KEY'
openparser jobs get JOB_ID --json
const job = await client.jobs.get('JOB_ID');
job = client.jobs.get("JOB_ID")

Polling does not control the job. Once accepted, it continues if your client disconnects.

Extract fields

Use /extract when you want a specific JSON shape instead of the full parsed document:

curl -X POST 'https://api.openparser.dev/extract' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H "Idempotency-Key: $(uuidgen 2>/dev/null || openssl rand -hex 16)" \
  -F 'request={"ocr_model":"paddleocr-vl-1.6","llm_model":"openai/gpt-6-luna","schema":{"type":"object","properties":{"invoice_number":{"type":"string"}},"required":["invoice_number"],"additionalProperties":false}};type=application/json' \
  -F 'file=@./document.pdf'
openparser extract sync ./document.pdf \
  --ocr-model paddleocr-vl-1.6 \
  --llm-model openai/gpt-6-luna \
  --schema-json '{"type":"object","properties":{"invoice_number":{"type":"string"}},"required":["invoice_number"],"additionalProperties":false}'
const extracted = await client.extract.sync(
  {
    ocr_model: 'paddleocr-vl-1.6',
    llm_model: 'openai/gpt-6-luna',
    schema: {
      type: 'object',
      properties: {
        invoice_number: { type: 'string' },
      },
      required: ['invoice_number'],
      additionalProperties: false,
    },
  },
  file,
);
from pathlib import Path

extracted = client.extract.sync(
    {
        "ocr_model": "paddleocr-vl-1.6",
        "llm_model": "openai/gpt-6-luna",
        "schema": {
            "type": "object",
            "properties": {
                "invoice_number": {"type": "string"},
            },
            "required": ["invoice_number"],
            "additionalProperties": False,
        },
    },
    file=Path("document.pdf"),
)

Call GET /models/llm to choose a current extraction model. The default response is a short list of suggested models.

Retry safely

Generate a new Idempotency-Key for each new request. When a network error makes the outcome unclear, retry the same request with the same key. OpenParser returns the original job instead of starting duplicate work.

Do not reuse a key with different input. The API rejects that request with 409 Conflict.

Next steps

On this page