For the complete documentation index, see llms.txt. This page is also available as Markdown.
Extractions
Run a field schema over many documents or papers and get a table back, with a citation per cell.
Endpoints
| Method | Path | Scope | Credits | Description |
|---|---|---|---|---|
| POST | /v1/extraction-schemas | extractions:write | 0 | Create a reusable schema |
| GET | /v1/extraction-schemas/{id} | extractions:read | 0 | Retrieve |
| POST | /v1/extractions | extractions:write | 2 or 5 per source | Run a schema (async) |
| GET | /v1/extractions/{id} | extractions:read | 0 | Retrieve rows |
| POST | /v1/extractions/{id}/export | extractions:read | 0 | csv, xlsx, ris → File |
This endpoint is asynchronous
Large runs finish per-document. Partial results are readable while status: running.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
schema_id or schema | string / object | yes | Reusable ID, or an inline schema |
document_ids | string[] | one of | |
paper_ids | string[] | one of | |
depth | enum | no |
The extraction object
{ "object": "extraction", "id": "extr_4c8pn0wsz2", "status": "succeeded",
"schema_id": "exsc_2hq7bnv6ld", "row_count": 512,
"rows": [ { "source": { "type": "document", "id": "doc_…" },
"values": { "sample_size": { "value": 4500000,
"citation": { "quote": "…", "locations": [ { "page": 7, "bbox": {} } ] } },
"intervention": { "value": null, "citation": null } },
"error": null } ] }Every value is
{ value, citation }. A field that could not be found isnullwithcitation: null— the extractor does not guess.Per-row
errorisolates failures: one unparseable PDF does not fail the run.
Partial failure
A run does not fail as a whole. Each source succeeds or fails on its own, and the extraction reaches
succeeded with per-row status — so always read rows[].status, never just the top-level one.
{
"object": "extraction",
"id": "extr_4c8pn0wsz2",
"status": "succeeded",
"schema_id": "exsc_2hq7bnv6ld",
"row_count": 3,
"succeeded_count": 2,
"failed_count": 1,
"credits_cost": 4,
"rows": [
{
"source": { "type": "document", "id": "doc_8ba2f01c47" },
"status": "succeeded",
"fields": {
"sample_size": 384,
"study_design": "randomised controlled trial",
"primary_outcome": "30-day mortality"
},
"citations": [
{ "field": "sample_size", "quote": "384 patients were randomised", "locations": [ { "page": 3, "bbox": {} } ] }
],
"error": null
},
{
"source": { "type": "document", "id": "doc_1f7cba9e05" },
"status": "succeeded",
"fields": { "sample_size": null, "study_design": "cohort", "primary_outcome": "readmission" },
"citations": [],
"error": null
},
{
"source": { "type": "document", "id": "doc_5aa30bd812" },
"status": "failed",
"fields": null,
"citations": [],
"error": {
"type": "invalid_request_error",
"code": "document_not_parseable",
"message": "Document has no extractable text layer."
}
}
]
}Note the difference between the two succeeded rows: a null field means the model looked and did
not find it, which is a finding. A failed row means nothing was read at all, which is a retry.
Do not collapse the two.
Reconciling a run
Retry only the failed rows, and only for causes that can change. document_not_parseable will fail
identically forever — send it to a human.
RETRYABLE = {"extraction_timeout", "model_overloaded"}
def reconcile(extraction, schema_id, max_rounds=3):
results = {r.source.id: r for r in extraction.rows if r.status == "succeeded"}
pending = [r.source.id for r in extraction.rows if r.status == "failed"
and r.error.code in RETRYABLE]
quarantine = [r for r in extraction.rows if r.status == "failed"
and r.error.code not in RETRYABLE]
for _ in range(max_rounds):
if not pending:
break
run = client.extractions.create(
schema_id=schema_id,
sources=[{"type": "document", "id": i} for i in pending],
).wait(timeout=600)
for row in run.rows:
if row.status == "succeeded":
results[row.source.id] = row
pending = [r.source.id for r in run.rows if r.status == "failed"
and r.error.code in RETRYABLE]
return results, quarantine + [{"id": i, "reason": "retries_exhausted"} for i in pending]Retries are charged
Each retried source costs its per-source price again. Cap the rounds, and never retry a permanent error — see cost-control.
Errors
| Status | code | When |
|---|---|---|
| 400 | schema_invalid | unsupported field type or empty fields |
| 413 | too_many_sources | more than 500 sources in one run |
Limits and cost
| Limit | Value |
|---|---|
| Credits | 2 per source at standard, 5 at high_quality — independent of field count |
| Sources per run | 500 |
| Fields per schema | 50 |
| Field types | string, integer, number, boolean, date, enum, string[] |
| Concurrency | one run holds one async slot; see rate-limits |
| Retention | rows kept until you delete the extraction or its documents |
Cost is per source, not per field, so a 50-field schema costs the same as a 5-field one. Write the schema you actually want.
Related
Last updated