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

# Extract

> Fill your JSON Schema with values read from a parse, each with its source and a confidence score.

Extract fills a JSON Schema that you write with values read from a completed parse. Use it when you know which fields you want, such as an invoice's number and total. Each value comes back with a confidence score and a citation of the parse elements it was read from:

```mermaid theme={null}
%%{init: {"themeVariables": {"fontSize": "14px"}, "flowchart": {"nodeSpacing": 24, "rankSpacing": 32, "padding": 8}}}%%
flowchart TD
    parse["POST /parse"] -->|parse_id| extract["POST /extract + schema"]
    extract -->|extract_id| leaf["value, citation, confidence"]
    leaf -.->|element_id| el["parse element: page, bbox"]
```

## Extract fields from a document

This example reads the account holder, closing balance and transactions from a [sample bank statement](https://platform.runpulse.com/dashboard/examples/637e5678-30b1-45fa-acc4-877f2d636419).

**1. Write the schema**

List the fields you want, each with a type, and save the schema as `statement_schema.json`:

```json theme={null}
{
  "type": "object",
  "properties": {
    "account_holder": { "type": "string", "description": "The person or business that owns the account" },
    "closing_balance": { "type": "number", "description": "Balance at the end of the statement period" },
    "transactions": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "date": { "type": "string", "description": "The date as printed, such as 10/02" },
          "description": { "type": "string" },
          "amount": { "type": "number" },
          "type": { "type": "string", "enum": ["credit", "debit"] }
        }
      }
    }
  },
  "required": ["account_holder", "closing_balance"]
}
```

**2. Parse the document, then extract**

This code parses the statement, then fills the schema from that parse, waiting for each job to finish.

```python Python theme={null}
import json
import os
from pulse import Pulse

client = Pulse(api_key=os.environ["PULSE_API_KEY"])

parse_id = client.parse.run(
    file_url="https://platform.runpulse.com/api/examples/637e5678-30b1-45fa-acc4-877f2d636419/pdf",
    config={"model": "ultra-2"},
).parse_id

with open("statement_schema.json") as f:
    schema = json.load(f)

job = client.extract.run(
    parse_id=parse_id,
    config={
        "input_schema": schema,
        "prompt": "Amounts are numbers without currency symbols. Withdrawals and fees are debits.",
    },
)
values = client.extract.get_result(job.extract_id).extraction.enriched
print(values["closing_balance"].value, values["closing_balance"].citation)
```

```typescript TypeScript theme={null}
import { readFileSync } from "node:fs";
import { PulseClient } from "@pulse-software/sdk";

const client = new PulseClient({ apiKey: process.env.PULSE_API_KEY! });

const { parseId } = await client.parse.run({
  fileUrl: "https://platform.runpulse.com/api/examples/637e5678-30b1-45fa-acc4-877f2d636419/pdf",
  config: { model: "ultra-2" },
});

const { extractId } = await client.extract.run({
  parseId,
  config: {
    inputSchema: JSON.parse(readFileSync("statement_schema.json", "utf8")),
    prompt: "Amounts are numbers without currency symbols. Withdrawals and fees are debits.",
  },
});
const { extraction } = await client.extract.getResult({ extractId });
console.log(extraction.enriched);
```

`prompt` holds conventions the schema can't express, such as units or how to classify an entry. For a long parse, send `async: true` and poll the job instead: see [Jobs and results](https://docs.runpulse.com/v2/jobs.md).

The calls in order:

```mermaid theme={null}
%%{init: {"themeVariables": {"fontSize": "14px"}, "sequence": {"mirrorActors": false, "messageMargin": 28, "height": 44, "boxMargin": 6}}}%%
sequenceDiagram
    participant App
    participant Pulse
    App->>Pulse: POST /parse
    Pulse-->>App: parse_id
    App->>Pulse: POST /extract (parse_id, config)
    Pulse-->>App: extract_id
    App->>Pulse: GET /extract/{extract_id}/result
    Pulse-->>App: extraction.enriched
```

An expired or deleted parse is refused with a `410` (`RESULT_EXPIRED`), so run Extract before an unsaved parse's `expires_at`.

***

## Read the result

`GET /extract/{extract_id}/result` (`client.extract.get_result`) returns the values, shown here with one transaction:

```json theme={null}
{
  "extract_id": "extract-8c2d4e1f-5a3b-4c6d-9e7f-1a2b3c4d5e6f",
  "extraction": {
    "enriched": {
      "account_holder": {
        "value": "JAMES C. MORRISON",
        "citation": [{ "element_id": "Text-2" }],
        "confidence": 0.98
      },
      "closing_balance": {
        "value": 586.71,
        "citation": [{ "element_id": "Table-5", "sub_element_id": "Table-5-r5c1" }],
        "confidence": 0.96
      },
      "transactions": [
        {
          "date": {
            "value": "10/03",
            "citation": [{ "element_id": "Table-9", "sub_element_id": "Table-9-r2c0" }],
            "confidence": 0.97
          },
          "description": {
            "value": "PREAUTHORIZED CREDIT",
            "citation": [{ "element_id": "Table-9", "sub_element_id": "Table-9-r2c1" }],
            "confidence": 0.97
          },
          "amount": {
            "value": 763.01,
            "citation": [{ "element_id": "Table-9", "sub_element_id": "Table-9-r2c3" }],
            "confidence": 0.95
          },
          "type": {
            "value": "credit",
            "citation": [{ "element_id": "Table-9", "sub_element_id": "Table-9-r2c3" }],
            "confidence": 0.93
          }
        }
      ]
    }
  },
  "usage": {
    "request": { "pages_processed": 2, "credits_used": 2.0 }
  },
  "history": {
    "file_name": "statement.pdf",
    "previous_step": "parse-3f6c1a2e-8d4b-4f7a-9c2e-5b1d0e7a4c93"
  }
}
```

`extraction.enriched` has your schema's shape. Each single value becomes an object with three fields:

| Field | What it holds |
| - | - |
| `value` | The value read from the document, or `null` when the document doesn't have it. |
| `citation` | The elements the value was read from: `element_id` is an `id` in the parse's `elements`, and `sub_element_id`, when present, names the table cell. It's empty when the source isn't known. |
| `confidence` | How sure Pulse is of the value, from 0 to 1, or `null` when there's no score. |

To have a person check a low-confidence value, [highlight its source](https://docs.runpulse.com/v2/cookbooks/highlight-sources.md) on its page.

`format` defaults to `["enriched"]`. Send `["raw"]` for the values alone, in `extraction.raw`, shaped exactly like your schema, or `"all"` for both.

## Write the schema

`input_schema` is a JSON Schema (Draft 7) whose root is an object with at least one property. It can also use `$ref` to `$defs` or `definitions` in the same schema, `anyOf`, `oneOf` and `allOf`, nullable types such as `["string", "null"]`, and `if`, `then` and `else`. An object returns only the properties it declares, unless its `additionalProperties` allows more. A schema that breaks these rules is refused with a `400` before any work starts, and costs nothing.

For more reliable values:

* Give a `description` to each field that could be confused with another, such as `remit_to`: "Address payments are sent to, often in the footer".
* If a value is missing, read the parse's `markdown`. Extract finds only what the parse found.

> [accordion] Schema limits and refused strings
>
> A schema can declare at most 5,000 properties, counting nested objects, definitions and branches. `format`, such as `"format": "date"`, is a hint and isn't enforced.
>
> Enum values, `const` values, defaults and property names are copied into the output exactly, so they can't contain quotation marks or apostrophes, straight or curly, or start or end with whitespace: `owner's_name` is refused. No string in the schema, descriptions and titles included, can contain a control character other than a tab or a newline (`\n`).

## Choose pages, effort and confidence

These options sit in `config` beside `input_schema`:

| Option | Default | What it does |
| - | - | - |
| `page_range` | Every page of the parse | Reads only these pages, numbered as in the parse's result. A range past its last page is refused. |
| `effort` | `"low"` | `"high"` reads more carefully, for values that need reasoning across pages. |
| `confidence` | `"standard"` | `"agentic"` scores each value more reliably. It must be enabled for your organization; otherwise the request is refused with a `400`. |

Extract costs 1 credit per page it reads, 6 credits per page with `effort: "high"`, and 3 more credits per page with `confidence: "agentic"` ([Pricing](https://docs.runpulse.com/v2/pricing.md)).

## Next steps

> [card] [Split, then extract](https://docs.runpulse.com/v2/cookbooks/split-then-extract.md)
>
> Find each section's pages with Split, then run a small schema on each.

> [card] [Saved configs](https://docs.runpulse.com/v2/saved-configs.md)
>
> Save a schema, then send its `config_id`.

> [card] [Extract API reference](https://docs.runpulse.com/api-reference/v2/extract/run-extract.md)
>
> Every option, field and limit.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.