# 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 |

> [!info] 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

```json
{
  "object": "extraction",
  "id": "extr_4c8pn0wsz2",
  "status": "succeeded",
  "schema_id": "exsc_2hq7bnv6ld",
  "row_count": 512,
  "rows": [
    {
      "source": {
        "type": "document",
        "id": "doc_8ba2f01c47"
      },
      "values": {
        "sample_size": {
          "value": 4500000,
          "citation": {
            "quote": "We trained on the standard WMT 2014 English-German dataset consisting of about 4.5 million sentence pairs.",
            "locations": [
              {
                "page": 7,
                "bbox": {
                  "x": 0.252,
                  "y": 0.426,
                  "width": 0.495,
                  "height": 0.041
                }
              }
            ]
          }
        },
        "intervention": {
          "value": null,
          "citation": null
        }
      },
      "error": null
    }
  ]
}
```

- Every value is `{ value, citation }`. A field that could not be found is `null` with `citation: null` — the extractor does not guess.
- Per-row `error` isolates 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.

```json
{
  "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.

```python
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]
```

> [!warning] 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

extraction-schema · batch-extraction · citation · files
