Extraction

Return document fields as schema-validated JSON.

Extraction is an additional step on top of parsing:

  1. OpenParser parses the source into an openparser@1 document.
  2. The selected language model extracts fields from that parsed document.
  3. OpenParser validates the output against your JSON Schema.

When you send file or file_id, the extraction endpoint runs both parsing and extraction. When you send the parse_job_id of a succeeded parse job, it reuses that job's ParsedDocument and starts at step 2.

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-5.6-terra","schema":{"type":"object","properties":{"invoice_number":{"type":"string"},"total":{"type":"number"}},"required":["invoice_number","total"],"additionalProperties":false}};type=application/json' \
  -F 'file=@./invoice.pdf'
openparser extract sync ./invoice.pdf \
  --ocr-model paddleocr-vl-1.6 \
  --llm-model openai/gpt-5.6-terra \
  --schema-json '{"type":"object","properties":{"invoice_number":{"type":"string"},"total":{"type":"number"}},"required":["invoice_number","total"],"additionalProperties":false}'
const result = await client.extract.sync(
  {
    ocr_model: 'paddleocr-vl-1.6',
    llm_model: 'openai/gpt-5.6-terra',
    schema: {
      type: 'object',
      properties: {
        invoice_number: { type: 'string' },
        total: { type: 'number' },
      },
      required: ['invoice_number', 'total'],
      additionalProperties: false,
    },
  },
  file,
);
from pathlib import Path

result = client.extract.sync(
    {
        "ocr_model": "paddleocr-vl-1.6",
        "llm_model": "openai/gpt-5.6-terra",
        "schema": {
            "type": "object",
            "properties": {
                "invoice_number": {"type": "string"},
                "total": {"type": "number"},
            },
            "required": ["invoice_number", "total"],
            "additionalProperties": False,
        },
    },
    file=Path("invoice.pdf"),
)

The output field in a successful response matches the schema you sent.

Extracted values keep the document's original wording, spelling, casing, and punctuation. OpenParser does not rewrite dates, addresses, names, or phrasing into a canonical form unless the field's JSON Schema type or description asks for that format. An integer or number field is an explicit format: the source text 4 year(s) becomes 4. Apply your own formatting, calculations, or review as downstream lineage steps.

Choose a source

A single extraction accepts exactly one source:

  • file for a direct upload
  • file_id for a file uploaded with POST /files
  • parse_job_id for the result of a succeeded parse job

Reusing a parse job avoids running OCR again and does not add another page charge. Language-model usage is still billed. Batch extraction accepts uploaded files and file_id, but not parse_job_id.

Sync vs async

EndpointBehavior
POST /extractWait for the terminal result. Returns 202 with a job if the wait window expires first.
POST /extract/asyncReturn 202 as soon as the durable job is accepted.
POST /extract/batchAccept 1–100 child jobs and return a batch job.

All three use the same extraction workflow. File-backed requests parse first; the single-document endpoints can skip that step with parse_job_id. The endpoint choice only changes how work is admitted and returned.

Use the synchronous endpoint when holding the connection open is convenient. Use the async endpoint when your application prefers to poll GET /jobs/{id}.

Choose a model

Call GET /models/llm for the current catalog. The default response contains suggested models; use mode=search and q to search the full list.

Any compatible model can perform ordinary extraction. Field grounding requires a certified model.

Control validation

  • Set repair_attempts from 0 to 2 to allow another model attempt when the first result does not satisfy the schema.
  • Set grounding to field to request source locations for extracted values. The selected model must support grounding.

OpenParser does not silently switch models or exceed the configured repair limit.

Reuse the configuration

Create a pipeline when several requests share the same OCR model, language model, schema, and options. Then send pipeline_id instead of the inline configuration.

Do not combine pipeline_id with inline model, schema, or extraction options.

Billing

Extraction is billed by input and output tokens at the customer rates for the selected language model. Current rates are published by GET /models/llm.

Each provider-accepted attempt is billable, including configured repair attempts. File-backed extraction also incurs the normal OCR page charge. Reusing a succeeded parse with parse_job_id does not add another page charge.

When usage is available, the terminal extraction result includes:

  • usage.input_tokens and usage.output_tokens for totals across all attempts
  • usage.cost_usd for the total customer language-model charge
  • attempts[].input_tokens, attempts[].output_tokens, and attempts[].cost_usd for per-attempt detail
type ExtractionUsage = {
  input_tokens?: number;
  output_tokens?: number;
  cost_usd?: number;
};

type ExtractionAttemptUsage = ExtractionUsage & {
  index: number;
  kind: 'primary' | 'repair';
  llm_model: string;
  status: 'succeeded' | 'failed' | 'indeterminate';
};

These fields are returned by a successful synchronous POST /extract response and inside result when GET /jobs/{id} returns a succeeded extraction job.

The cost is the customer charge, not the provider's raw list price, and does not include the separate OCR page charge.

Results

A successful extraction includes:

  • output, validated against your schema
  • parsed_document
  • the model that ran
  • attempts and aggregate token usage
  • field grounding, when requested and available
  • lineage, a portable lineage@1 derivation DAG, when grounding is field

POST /extract/async always returns a job. The synchronous endpoint also returns a job when processing exceeds its wait window. Poll GET /jobs/{id} for the final result.

Grounded lineage

Set grounding to field to receive verified citations and a complete derivation DAG. The lineage contains:

  • artifact entities for the source and parsed documents, collection entities for cited regions, and a field entity for every output leaf
  • OCR and extraction activities with the exact models that ran
  • one derivation for a verbatim field, or an extraction plus transform derivation when the returned value rewrites the source text
  • source text, page geometry, canonical element ids, and table-cell ids
  • the closest recognition confidence supplied by the OCR provider

OpenParser retains word or symbol confidence when the OCR provider supplies it. Otherwise it uses confidence from the cited table cell, text span, element, or page. Each assertion keeps its scope, granularity, source scale, and calibration status. A summary across a cited source region describes OCR recognition in that evidence—not the probability that the extracted value is correct. OpenParser does not invent extraction confidence when the language model does not report a calibrated field probability.

Evidence locators point into the parsed_document returned beside the lineage. Your application can render the source directly and append new value entities, activities, and derivations for normalization, calculations, inference, and human review. See lineage@1 for the protocol and @openparser/lineage helpers.

Lineage always carries region-level aggregates; per-word text and confidence stay in parsed_document, where region selectors address them without copying them into the graph. If lineage would push the terminal result over its size limit, extraction still succeeds with grounding and omits lineage.

Each grounded field may include reason and transform_claim. reason explains why the quoted passage is the source for the value — what in the document identifies it — and does not explain formatting. transform_claim is the model's bounded, untrusted report of how the quote became the returned value and is omitted when the value is the quote as written. It never authorizes a validator. For format-only JSON Schema dates, source_format may only prioritize a member of the trusted built-in source catalog; it cannot add formats or authorize a score of 1. target_format and other claim parameters cannot change trusted policy.

{
  "mode": "field",
  "fields": [
    {
      "path": "lease_start_date",
      "quote": "20 day of May 2025",
      "reason": "The commencement clause states the lease begins on the 20 day of May 2025.",
      "transform_claim": {
        "operation": "date_time_format",
        "parameters": { "source_format": "d 'day of' MMMM yyyy", "target_format": "yyyy-MM-dd" },
        "reason": "The schema asks for an ISO 8601 date, so the written date was reformatted to 2025-05-20.",
        "confidence": "high"
      }
    }
  ]
}

In lineage, a justification is an attribute of the entity its step produced (openparser:justification). A rewritten field's read reason lives on the intermediate verbatim-text entity; its transform reason lives on the field entity. See extraction and transforms.

Human review API

Every succeeded extraction job has a versioned review resource:

  • GET /jobs/{id}/review returns immutable machine output, corrected output, status, and audit events
  • PATCH /jobs/{id}/review applies confirmations and retractions addressed by JSON Pointer with expected_version. A confirmation is the single reviewer act: this field should say value, signed by the caller. When that value differs from extraction, the confirmation is also the correction. A retraction takes back the caller's own current tip on that path, while nothing else depends on it
  • POST /jobs/{id}/review/complete approves or rejects the review
  • POST /jobs/{id}/review/reopen returns a completed review to pending; only the reviewer who signed the run off may reopen it

Confirmations never overwrite result.output. Concurrent stale writes return 409, and each confirmation records its actor, timestamp, previous value when the field moved, and confirmed value. Quorum rules are yours to define; the platform records signed confirmations rather than enforcing a policy. This lets OpenParser Studio provide a lightweight review UI while clients build their own workflow-specific interfaces from the same API.

For grounded extractions, the review resource also returns the machine lineage@1 graph extended with append-only review.confirm activities, reviewer agents, corrected value entities when a confirmation changes a field, who stands behind the current values, and the final approval or rejection decision. The original machine entities remain unchanged. See OpenParser human review.

On this page