> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ndi.nace.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Extract

> Fill a JSON Schema from a document

Exactly one of `json_schema` or `schema_id` is required. `not_found` on a
field is an answer about the document, not an HTTP error.

```python theme={"dark"}
from ndi_sdk import NdiClient, ParseResultSource

schema = {
    "type": "object",
    "properties": {
        "invoice_number": {"type": "string"},
        "total": {"type": "number"},
    },
    "required": ["invoice_number", "total"],
}

with NdiClient() as client:
    upload = client.documents.create_upload("invoice.pdf")
    parsed = client.jobs.wait(client.documents.parse(upload, wait_seconds=30).job_id)
    job = client.jobs.wait(
        client.documents.extract(
            ParseResultSource(job_id=parsed.job_id),
            json_schema=schema,
            wait_seconds=30,
        ).job_id
    )
    print(job.result.data)
    for field in job.result.fields:
        print(field.path, field.status, field.value)
```

## Method signature

```text theme={"dark"}
def extract(
    source: DocumentSource | UploadResponse,
    *,
    json_schema: JsonSchema | None = None,
    schema_id: str | None = None,
    instructions: str | None = None,
    page_ranges: list[PageRange] | None = None,
    citations: ExtractCitationOptions | None = None,
    wait_seconds: int = 0,
    idempotency_key: str | None = None,
) -> Job
```

### Parameters

| Parameter      | Type                                 | Required | Description                              |
| -------------- | ------------------------------------ | -------- | ---------------------------------------- |
| `source`       | `DocumentSource` or `UploadResponse` | Yes      | File or prior parse                      |
| `json_schema`  | JSON Schema object                   | One of   | Inline schema                            |
| `schema_id`    | `str`                                | One of   | Saved schema id                          |
| `instructions` | `str`                                | No       | Extra extraction guidance                |
| `page_ranges`  | `list[PageRange]`                    | No       | Not available on a `parse_result` source |
| `citations`    | `ExtractCitationOptions`             | No       | Default on                               |
| `wait_seconds` | `int`                                | No       | Hold the HTTP response open (max 300)    |

### Returns

A `Job`. On success, `result.result_type == "extract"`. `result.data` is the
filled object; `result.fields` is the per-path status list.

## Validate a schema

```python theme={"dark"}
from ndi_sdk import NdiClient

schema = {"type": "object", "properties": {"total": {"type": "number"}}}

with NdiClient() as client:
    check = client.documents.validate_extract_schema(json_schema=schema)
    print(check.valid, check.errors)
```

Register reusable schemas over HTTP (`POST /v1/extraction-schemas`) and pass
`schema_id`. There is no SDK registry namespace.

See [Extract guide](/guides/extract) and [Extract response](/guides/extract-response).
