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.
/v1/documents/extract/v1/images/extractUse /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órioThe PDF (/documents/extract) or the image — JPEG, PNG, or WEBP
(/images/extract). At least one file is required, in file or in
files.
filesfile[]OpcionalSeveral 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órioA JSON Schema (as a string) describing the fields to extract.
systemstringOpcionalReplaces your stack's configured system prompt for this request. Omit it to keep your stack's configured behavior.
userstringOpcionalExtra 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: 4000Ceiling on the response. Must be greater than 0, up to 16000.
The knowledge base (RAG) is not used on either endpoint, on purpose — the relevant context is the file you sent, and chunks from other documents would only raise the risk of a field being filled from the wrong source.
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
{
"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 }
}dataobjectOpcionalYour JSON, already validated against schema.
pagesintegerOpcionalPage 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.
filesintegerOpcionalHow many files went into this extraction. Always present (1 in the
common case).
ocr_usedbooleanOpcionaltrue 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:
A field declared { "type": "string" } cannot come back null. If the
information isn't in the file, the model is forced to emit some string —
and it will invent one.
Declare any field that might legitimately be absent as nullable:
"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:
{
"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 repeatingfile— returns413with 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
| Status | Meaning |
|---|---|
400 | Unreadable/corrupt file, no extractable text, invalid schema, or content too large for the plan's context window |
413 | File size, number of files, page count/resolution, or schema over the plan's limit |
422 | max_tokens outside the accepted range, or no file sent |
502 | Model 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
| Limit | Endpoint | Go | Pro | Max | Enterprise |
|---|---|---|---|---|---|
| Files per request | both | 1 | 5 | 10 | Custom |
| File size (sum of all files) | /documents/extract | 8 MB | 15 MB | 25 MB | Custom |
| Pages per request (sum of all files) | /documents/extract | 15 | 30 | 50 | Custom |
| File size (sum of all files) | /images/extract | 5 MB | 10 MB | 15 MB | Custom |
| Resolution (per image) | /images/extract | 20 megapixels | 20 megapixels | 20 megapixels | 20 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.

