Referência da API

Structured extraction

PDF or image in — schema-validated JSON out. OCR runs automatically when needed.

Two endpoints, same idea: you describe the shape you want as a JSON Schema, and the response is guaranteed to validate against it. You don't extract text yourself — OCR runs automatically wherever it's needed.

POST/v1/documents/extract
POST/v1/images/extract

Use /documents/extract for a PDF (OCR only kicks in for scanned pages), and /images/extract for a loose image — a phone photo, a screenshot, a standalone scan — where every image goes through OCR.

Request

Both take multipart/form-data with the same fields:

filefileObrigatório

The PDF (/documents/extract) or the image — JPEG, PNG, or WEBP (/images/extract). At least one file is required, in file or in files.

filesfile[]Opcional

Several files in one request — repeat the field once per file. Available from Pro; on Go a second file returns 413 (it is never silently dropped). file and files combine, in the order sent. See Several files in one request.

schemastringObrigatório

A JSON Schema (as a string) describing the fields to extract.

systemstringOpcional

Replaces your stack's configured system prompt for this request. Omit it to keep your stack's configured behavior.

userstringOpcional

Extra context about this specific file — added on top of the extraction instruction, not a replacement for it. Use it to say what kind of document or image this is when the same stack handles several.

max_tokensintegerOpcionalPadrão: 4000

Ceiling on the response. Must be greater than 0, up to 16000.

Example

curl -X POST "https://api.trystac.com/v1/documents/extract" \
  -H "Authorization: Bearer $STAC_API_KEY" \
  -F file=@invoice.pdf \
  -F 'schema={
        "type": "object",
        "properties": {
          "invoice_number": {"type": "string"},
          "vendor_tax_id":  {"type": ["string", "null"]},
          "total":          {"type": "number"}
        },
        "required": ["invoice_number", "vendor_tax_id", "total"]
      }'
curl -X POST "https://api.trystac.com/v1/images/extract" \
  -H "Authorization: Bearer $STAC_API_KEY" \
  -F file=@invoice.jpg \
  -F 'schema={"type": "object", "properties": {"total": {"type": "number"}}, "required": ["total"]}'

Response

200 OKjson
{
  "data": { "invoice_number": "12345", "vendor_tax_id": "11-2223334", "total": 1500.0 },
  "pages": 3,
  "files": 1,
  "ocr_used": false,
  "usage": { "prompt_tokens": 2104, "completion_tokens": 48 }
}
dataobjectOpcional

Your JSON, already validated against schema.

pagesintegerOpcional

Page count for a PDF, summed across files when there is more than one. For /images/extract it's the number of images (1 in the common case) — there's no "page" concept for a loose image.

filesintegerOpcional

How many files went into this extraction. Always present (1 in the common case).

ocr_usedbooleanOpcional

true if OCR ran. For /images/extract it's always true — every image goes through OCR. For /documents/extract it's true only for scanned pages; worth reviewing the result more carefully in that case.

The most important rule: mark fields nullable if they can be missing

Output is grammar-constrained to match your schema token by token — that's what guarantees valid JSON, but it has a consequence that decides the quality of your results:

Declare any field that might legitimately be absent as nullable:

json
"vendor_tax_id": { "type": ["string", "null"] }

There's a second trap in the opposite direction: a field left out of required is optional under JSON Schema, so the model can simply omit the key instead of looking for the value — you get a 200 with valid JSON and a missing field, no warning. The combination that avoids both traps is required on every field, with nullable types on the ones that can legitimately be missing:

json
{
  "type": "object",
  "properties": {
    "invoice_number": { "type": "string" },
    "vendor_tax_id":  { "type": ["string", "null"] },
    "total":          { "type": "number" }
  },
  "required": ["invoice_number", "vendor_tax_id", "total"]
}

This applies inside nested arrays and objects too — each object in a list needs its own required, or items come back with fields silently missing.

Several files in one request

When the fields you want are spread across documents — the invoice and the receipt, the contract and its amendment — send them all in one request through files and get one JSON back. Available from Pro.

curl -X POST "https://api.trystac.com/v1/documents/extract" \
  -H "Authorization: Bearer $STAC_API_KEY" \
  -F files=@invoice.pdf \
  -F files=@receipt.pdf \
  -F 'schema={
        "type": "object",
        "properties": {
          "invoice_number": {"type": "string"},
          "amount_paid":    {"type": ["number", "null"]}
        },
        "required": ["invoice_number", "amount_paid"]
      }' \
  -F 'user=The invoice number is on the invoice; the amount paid is on the receipt.'

Each file reaches the model in its own numbered block, labeled with the file name (--- DOCUMENTO 1 DE 2 (invoice.pdf) ---), so user can say where to look for each field.

  • Go accepts 1 file per request. A second file — in files, or by repeating file — returns 413 with a message saying so. For several documents on Go, send one request per file.
  • Plan limits apply to the sum. File size and page count are added up across all files in the request, and the request is rejected before any OCR runs.
  • An empty document aborts the whole request (400, naming the file). Continuing without it would let the model fill the schema from the other files with no way for you to know.
  • Want one JSON per file? Keep sending one request per file — multi-file is for fields that live in different documents.

Errors

StatusMeaning
400Unreadable/corrupt file, no extractable text, invalid schema, or content too large for the plan's context window
413File size, number of files, page count/resolution, or schema over the plan's limit
422max_tokens outside the accepted range, or no file sent
502Model output didn't validate against schema (response includes raw_output for debugging)

A truncated raw_output on a 502 usually means the response didn't fit the remaining context — the file is large and little room was left for the JSON. Split the PDF or trim the schema.

Limits

LimitEndpointGoProMaxEnterprise
Files per requestboth1510Custom
File size (sum of all files)/documents/extract8 MB15 MB25 MBCustom
Pages per request (sum of all files)/documents/extract153050Custom
File size (sum of all files)/images/extract5 MB10 MB15 MBCustom
Resolution (per image)/images/extract20 megapixels20 megapixels20 megapixels20 megapixels

Schema up to 64 KB on both. Server timeout: 240s — set your client's timeout above that; a multi-page scanned document can take minutes.