Skip to main content
Extract pulls specific fields out of a document as structured JSON. You describe the data you need with a JSON schema — field names and, crucially, descriptions guide the extraction — and NDI returns values matching it, handling parsing, long documents, and repeating data (arrays) internally. Long documents are processed in content batches well below the model’s context window: scalar fields take the first value the document states; top-level array fields (line items, transactions) are extracted batch-by-batch and concatenated, so long lists don’t get truncated.
EndpointBehavior
POST /extractStarts the job and waits for the result (bounded by timeout_seconds).
POST /extract_asyncStarts the job and returns 202 {"job_id": …} immediately.

Schema rules

NDI accepts a restricted subset of JSON Schema:
  • The root must be {"type": "object", "properties": {…}}.
  • Allowed keywords: type, properties, items, required, description, enum, title.
  • Allowed types: object, array, string, number, integer, boolean, null.
  • Nesting is limited to 5 levels; at most 100 properties total.
  • $ref, $defs, anyOf/oneOf/allOf/not, formats, and defaults are not supported — encode such constraints in the field description instead.
Schemas outside the subset are rejected with a 422 before the job starts. Fields the document does not contain come back as null (scalars) or [] (arrays) — a required field missing from the document does not fail the job. Extract never returns partial data silently: a document that exceeds the extraction capacity (~1,300 standard pages) fails with an extract_content_exceeds_limit error rather than truncating — split such files and extract the parts separately.

Request

timeout_seconds
number
default:"120"
Sync endpoint only. Max seconds to wait, between 1 and 300.
file
object
required
The document to extract from. Same shape as everywhere else — source_url (+ file_name for s3:///https://). See Files & sources. Supported extensions are the same as parse, plus .md/.mdx.
schema
object
required
The JSON schema describing what to extract (see the rules above). Write each field’s description as if instructing a careful analyst — it directly drives accuracy.
markdown_url
string
markdown_url from a prior parse of the same file (an s3:// URL returned by NDI). When present, extract skips its internal parse — faster and cheaper if you already parsed the document. Ignored for spreadsheets.
idempotency_key
string
Up to 128 characters. See Idempotency.

Result

result_type
"extract"
Discriminator — always "extract".
status
"success" | "failed"
Outcome of the extraction run; error carries detail on failure.
data
object | null
The extracted values, shaped exactly like your schema. Scalars the document does not contain are null; arrays with no matches are [].
json_url
string | null
The same content as data, persisted as a JSON artifact (time-limited download URL).
page_count
integer
Document pages processed (0 for spreadsheets).
sheet_count
integer
Spreadsheet sheets processed (0 for documents).

Examples

curl -s "$NDI_BASE/extract" \
  -H "X-API-Key: $NDI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "file": { "source_url": "ndi://file/7f4d2a10-3b7e-4c25-9f61-8f0f4f0a1b2c" },
    "schema": {
      "type": "object",
      "properties": {
        "invoice_number": { "type": "string", "description": "The invoice identifier, e.g. INV-2026-0042." },
        "total_amount": { "type": "number", "description": "Grand total due, without currency symbols." },
        "currency": { "type": "string", "description": "ISO 4217 currency code of the total." },
        "line_items": {
          "type": "array",
          "description": "Every billed line item on the invoice.",
          "items": {
            "type": "object",
            "properties": {
              "description": { "type": "string" },
              "quantity": { "type": "number" },
              "amount": { "type": "number", "description": "Line total, without currency symbols." }
            },
            "required": ["description", "amount"]
          }
        }
      },
      "required": ["invoice_number", "total_amount"]
    }
  }'
curl -s "$NDI_BASE/extract_async" \
  -H "X-API-Key: $NDI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "file": {
      "source_url": "https://example.com/annual-report.pdf",
      "file_name": "annual-report.pdf"
    },
    "markdown_url": "s3://…/jobs/…/parse/annual-report/document.md",
    "schema": {
      "type": "object",
      "properties": {
        "company_name": { "type": "string", "description": "Legal name of the reporting entity." },
        "fiscal_year": { "type": "integer", "description": "Fiscal year the report covers." },
        "revenue": { "type": "number", "description": "Total revenue for the fiscal year in USD." }
      }
    }
  }'
Result
{
  "job_id": "e2f3a4b5-6c7d-4e8f-9a0b-1c2d3e4f5a6b",
  "action": "extract",
  "status": "succeeded",
  "result": {
    "result_type": "extract",
    "status": "success",
    "error": null,
    "data": {
      "invoice_number": "INV-2026-0042",
      "total_amount": 18500.0,
      "currency": "USD",
      "line_items": [
        { "description": "Q4 maintenance services", "quantity": 1, "amount": 17000.0 },
        { "description": "Emergency call-out fee", "quantity": 2, "amount": 1500.0 }
      ]
    },
    "json_url": "https://…(time-limited)…/invoice_extraction.json",
    "page_count": 3,
    "sheet_count": 0
  },
  "error": null,
  "created_at": "2026-07-14T10:20:05Z",
  "started_at": "2026-07-14T10:20:06Z",
  "completed_at": "2026-07-14T10:20:41Z"
}