Perception
Extract
Fill a JSON schema from a document, with a status and citations for every field.
POST /v1/documents/extract fills a JSON Schema from a document. Send the schema inline as schema, or name a saved schema with schema_id, but not both.
curl "https://console.nace.ai/v1/documents/extract?wait_seconds=60" \
-H "Authorization: Bearer $NACE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"source": { "type": "parse_result", "job_id": "'"$PARSE_JOB_ID"'" },
"schema": {
"type": "object",
"properties": {
"invoice_number": { "type": "string" },
"total": { "type": "number" }
},
"required": ["invoice_number", "total"]
}
}'Pointing Extract at a finished Parse job, as above, means the document isn't parsed and paid for twice. A url source requires type, url, and file_name, for example {"type":"url","url":"https://example.com/invoice.pdf","file_name":"invoice.pdf"}. A workspace_file source also works. See Sources.
Configuration
POST /v1/documents/extract has several groups of options. Each example is a whole request with that option set, for the file you uploaded above. For the SDKs and the CLI, see the Python, TypeScript and CLI pages.
Schema
The fields to extract, using the supported subset of JSON Schema, sent inline or named by the id of a saved schema. Send one, not both.
Supported schema keywords
Extract supports a limited subset of JSON Schema. The root must have type: "object" and a non-empty properties object.
- Supported keywords:
type,properties,items,required,description,enum,title,additionalProperties,maxItemsanduniqueItems. additionalPropertiesis supported only asfalse.maxItemsanduniqueItemsapply only to array properties directly under the root object.- Other keywords, including
format,$refandanyOf, are rejected with422 invalid_schemabefore a job starts.
For a date, use a string and describe the representation you want:
{
"type": "object",
"properties": {
"transactionDate": {
"type": "string",
"description": "Transaction date in YYYY-MM-DD format."
}
}
}The word "format" is allowed inside a description. Descriptions guide extraction but do not enforce date validation. Validate the returned date in your application if you need a strict date constraint.
curl https://console.nace.ai/v1/documents/extract \
-H "Authorization: Bearer $NACE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"source": {
"type": "workspace_file",
"workspace_id": "'"$WORKSPACE_ID"'",
"file_id": "'"$FILE_ID"'"
},
"schema": {
"type": "object",
"properties": { "total": { "type": "number" } }
}
}'| Option | Default | What it does |
|---|---|---|
schema | Required, or schema_id | The fields to extract, using the supported schema keywords. |
schema_id | Required, or schema | A saved schema's id (sch_...). |
schema_version | Latest | The version of a saved schema to use. Only with schema_id: with an inline schema it's refused with 422. |
Instructions
Extra guidance for filling the fields.
curl https://console.nace.ai/v1/documents/extract \
-H "Authorization: Bearer $NACE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"source": {
"type": "workspace_file",
"workspace_id": "'"$WORKSPACE_ID"'",
"file_id": "'"$FILE_ID"'"
},
"schema": {
"type": "object",
"properties": { "total": { "type": "number" } }
},
"instructions": "Amounts are in euros"
}'| Option | Default | What it does |
|---|---|---|
instructions | None | Extra guidance for filling the declared fields. It can't add fields the schema doesn't list. |
Citations
Each field says where in the document it was read. Turn that off, or keep it without the quoted text.
curl https://console.nace.ai/v1/documents/extract \
-H "Authorization: Bearer $NACE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"source": {
"type": "workspace_file",
"workspace_id": "'"$WORKSPACE_ID"'",
"file_id": "'"$FILE_ID"'"
},
"schema": {
"type": "object",
"properties": { "total": { "type": "number" } }
},
"citations": { "include_source_text": false }
}'| Option | Default | What it does |
|---|---|---|
citations.enabled | true | When true, each field says where in the document it was read. |
citations.include_source_text | true | When true, each citation includes a short quote. |
Pages
Parse only some pages of the file. Leave it out to read every page. Not with a parse_result source.
curl https://console.nace.ai/v1/documents/extract \
-H "Authorization: Bearer $NACE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"source": {
"type": "workspace_file",
"workspace_id": "'"$WORKSPACE_ID"'",
"file_id": "'"$FILE_ID"'"
},
"schema": {
"type": "object",
"properties": { "total": { "type": "number" } }
},
"page_ranges": [{ "start": 1, "end": 3 }]
}'| Option | Default | What it does |
|---|---|---|
page_ranges | All pages | One-based, inclusive ranges, up to 200 ranges and 500 pages in all. Not with a parse_result source (422). |
Parse mode
See measured latency by mode for the parsing stage. This operation adds its own work, so a Parse measurement is not its total latency.
How hard to work when Extract reads the file itself. Parse has the full description of each mode.
curl https://console.nace.ai/v1/documents/extract \
-H "Authorization: Bearer $NACE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"source": {
"type": "workspace_file",
"workspace_id": "'"$WORKSPACE_ID"'",
"file_id": "'"$FILE_ID"'"
},
"schema": {
"type": "object",
"properties": { "total": { "type": "number" } }
},
"parse_mode": "high"
}'| Option | Default | What it does |
|---|---|---|
parse_mode | low | How hard to work when Extract parses the file itself: low, medium or high, as on Parse. Only low with a parse_result source. |
Result
A finished extract has result.result_type: "extract":
{
"result_type": "extract",
"data": { "invoice_number": "INV-1042", "total": null },
"fields": [
{
"path": "/invoice_number",
"value": "INV-1042",
"status": "found",
"confidence": null,
"citations": [
{ "location": { "kind": "page_region", "page": 1, "polygons": [] }, "source_text": "Invoice INV-1042" }
]
},
{ "path": "/total", "value": null, "status": "not_found", "citations": [] }
],
"schema_id": null,
"schema_version": null,
"units": 1,
"warnings": []
}| Field | What it holds |
|---|---|
result_type | Always "extract". |
data | The extracted values, shaped by your schema. A value that wasn't found is null, never left out. |
fields | One entry per schema field, plus one per item of a top-level array (/line_items/0, /line_items/1, …). Join it to data on path. |
fields[].path | A JSON Pointer to the field, such as /invoice_number. |
fields[].value | The extracted value, or null. |
fields[].status | found, not_found or ambiguous (a value is there but couldn't be chosen uniquely). not_found is an answer about the document: the job still succeeded. |
fields[].confidence | Scores the evidence for a found top-level field, and for each item of a top-level array, when the runtime produced one. null for fields inside nested objects and for fields that weren't found. |
fields[].citations | Where the value was read. Each citation has a location (see Parse blocks) and an optional source_text quote. |
schema_id | The saved schema's id, when you extracted with schema_id. Otherwise null. |
schema_version | The saved schema's version that was used. Otherwise null. |
units | The job's standard-rate usage. See Usage fields. |
warnings | Non-fatal notes, such as instructions that no declared field can use. |
Saved schemas
Save a schema once, then name it by id instead of sending it with every extract:
curl https://console.nace.ai/v1/extraction-schemas \
-H "Authorization: Bearer $NACE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "invoice",
"description": "Invoice header fields",
"schema": {
"type": "object",
"properties": {
"invoice_number": { "type": "string" },
"total": { "type": "number" }
}
}
}'
# 201 {"schema_id":"sch_...","name":"invoice","version":1,"owner":"account","schema":{...},...}Send "schema_id": "sch_..." in place of schema. The extract uses the latest version, or the one you pin with "schema_version": 1, and is billed like any other.
curl https://console.nace.ai/v1/documents/extract \
-H "Authorization: Bearer $NACE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"source": {
"type": "workspace_file",
"workspace_id": "'"$WORKSPACE_ID"'",
"file_id": "'"$FILE_ID"'"
},
"schema_id": "sch_...",
"schema_version": 1
}'The examples use the uploaded file from the examples above.
| Route | What it does |
|---|---|
POST /v1/extraction-schemas/{id}/versions | Adds the next version. name and description carry over unless you send them. |
GET /v1/extraction-schemas | Lists the latest version of each schema, sorted by schema_id (case-insensitively, with - and _ ignored at first; not byte order), without the JSON Schema itself. |
GET /v1/extraction-schemas/{id} | Reads a schema's latest version. |
GET /v1/extraction-schemas/{id}/versions | Lists a schema's versions, oldest first. |
GET /v1/extraction-schemas/{id}/versions/{version} | Reads one version. |
DELETE /v1/extraction-schemas/{id} | Deletes the schema with every version. Answers 204, also when it's already deleted. |
- Versions never change. A schema can't be edited; add a version instead, so an extract that pins
schema_versionalways gets the same schema. - Retention: a saved schema, with every version, is kept until you delete it or close your account.
- Deleting removes every version at once. An extract already accepted keeps the copy of the schema it took and finishes, and its result still names the
schema_idandschema_versionit used. Afterwards, reading the schema or naming itsschema_idon an extract answers404 schema_not_found. Nace's schemas can't be deleted. - Lists take
limit(1 to 200, default 50) andcursor, the previous page'snext_cursor. Keep going until it'snull. - Nace's schemas: the list can also hold schemas Nace provides, with
owner: "platform". You can extract with them, but not add versions to them. Yours haveowner: "account". - Only your account can see or use your schemas. Another account's
schema_idanswers404 schema_not_found, and the extract isn't billed. - A schema Extract can't use is refused with
422 invalid_schema. - Retries: creating a schema or a version takes no
Idempotency-Key, so a retried create saves a second one. The SDKs send these creates once, without automatic retries, unless you pass a retry policy. - Cost: schema routes are free and work at any balance. They count toward the per-minute limit like the other document routes.
From the SDKs and the CLI
Each route has an SDK method and a CLI command:
| Route | Python | TypeScript | CLI |
|---|---|---|---|
| Create | extraction_schemas.create | extractionSchemas.create | nace schema-create |
| Add a version | extraction_schemas.create_version | extractionSchemas.createVersion | nace schema-version-add |
| List | extraction_schemas.list, extraction_schemas.iter | extractionSchemas.list, extractionSchemas.iter | nace schemas |
| Read | extraction_schemas.get | extractionSchemas.get | nace schema |
| List versions | extraction_schemas.list_versions, extraction_schemas.iter_versions | extractionSchemas.listVersions, extractionSchemas.iterVersions | nace schema-versions |
| Read a version | extraction_schemas.get_version | extractionSchemas.getVersion | nace schema-version |
A list comes back one page at a time. To read everything:
- Python and TypeScript:
listreturns a single page.iterloops over every schema and fetches the next page whenever it needs one. For a schema's versions, useiter_versions(iterVersionsin TypeScript). - CLI:
nace schemasandnace schema-versionsprint one page. Add--allto print everything, or pass--cursorwith the previous page'snext_cursorto get the next page.