API Reference
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:
filefileRequiredThe PDF (/documents/extract) or the image — JPEG, PNG, or WEBP
(/images/extract).
schemastringRequiredA JSON Schema (as a string) describing the fields to extract.
systemstringOptionalReplaces your stack's configured system prompt for this request. Omit it to keep your stack's configured behavior.
userstringOptionalExtra 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_tokensintegerOptionalDefault: 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,
"ocr_used": false,
"usage": { "prompt_tokens": 2104, "completion_tokens": 48 }
}dataobjectOptionalYour JSON, already validated against schema.
pagesintegerOptionalPage count for a PDF. Always 1 for /images/extract — there's no
"page" concept for a loose image.
ocr_usedbooleanOptionaltrue 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.
Errors
| Status | Meaning |
|---|---|
400 | Unreadable/corrupt file, no extractable text, invalid schema, or content too large for the plan's context window |
413 | File, page count/resolution, or schema over the plan's limit |
422 | max_tokens outside the accepted range |
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 |
|---|---|---|---|---|---|
| File size | /documents/extract | 8 MB | 15 MB | 25 MB | Custom |
| Pages per request | /documents/extract | 15 | 30 | 50 | Custom |
| File size | /images/extract | 5 MB | 10 MB | 15 MB | Custom |
| Resolution | /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.

