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

# Extract

> Use Mercury Decide to pull exact spans of text out of your input, with character offsets and confidence.

`extract` is the fourth question type in [Mercury Decide](/capabilities/decisions), alongside `noul`, `choice`, and `score`. It marks the parts of your text that match fields you describe in plain language, such as names, order numbers, or addresses. Every span is an exact substring of your input, with character offsets and a confidence score. Nothing is generated, so the model cannot return a value that isn't in the text.

Send extract questions to `POST /v1/decisions` with the model `mercury-decide`, either on their own or mixed with other question types in the same request. Requests without extract questions work exactly as before.

## Send an extract request

Set `INCEPTION_API_KEY` to your [API key](https://platform.inceptionlabs.ai/dashboard/api-keys). Describe each field you want in `criteria`, and use `instructions` for framing shared by all fields.

```bash cURL theme={null}
curl https://api.inceptionlabs.ai/v1/decisions \
  -H "Authorization: Bearer $INCEPTION_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "mercury-decide",
    "state": "Hi, this is Maria Lopez. My order #A-1042 arrived damaged, please call me at 415-555-0199.",
    "questions": {
      "customer": {
        "type": "extract",
        "instructions": "Extract the customer details.",
        "criteria": {
          "name": "The customer full name",
          "order_id": "The order number",
          "phone": "A phone number",
          "email": "An email address"
        }
      },
      "team": {
        "type": "choice",
        "criteria": {
          "returns": "Refunds, exchanges, and damaged items",
          "billing": "Charges and invoices"
        }
      }
    }
  }'
```

```python Python theme={null}
import json
import os
import urllib.request

payload = {
    "model": "mercury-decide",
    "state": "Hi, this is Maria Lopez. My order #A-1042 arrived damaged, please call me at 415-555-0199.",
    "questions": {
        "customer": {
            "type": "extract",
            "instructions": "Extract the customer details.",
            "criteria": {
                "name": "The customer full name",
                "order_id": "The order number",
                "phone": "A phone number",
                "email": "An email address",
            },
        },
        "team": {
            "type": "choice",
            "criteria": {
                "returns": "Refunds, exchanges, and damaged items",
                "billing": "Charges and invoices",
            },
        },
    },
}
request = urllib.request.Request(
    "https://api.inceptionlabs.ai/v1/decisions",
    data=json.dumps(payload).encode(),
    headers={
        "Authorization": f"Bearer {os.environ['INCEPTION_API_KEY']}",
        "Content-Type": "application/json",
    },
    method="POST",
)
with urllib.request.urlopen(request, timeout=60) as response:
    decision = json.load(response)
print(decision["answers"]["customer"]["spans"])
```

```typescript TypeScript theme={null}
const response = await fetch('https://api.inceptionlabs.ai/v1/decisions', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.INCEPTION_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    model: 'mercury-decide',
    state: 'Hi, this is Maria Lopez. My order #A-1042 arrived damaged, please call me at 415-555-0199.',
    questions: {
      customer: {
        type: 'extract',
        instructions: 'Extract the customer details.',
        criteria: {
          name: 'The customer full name',
          order_id: 'The order number',
          phone: 'A phone number',
          email: 'An email address',
        },
      },
      team: {
        type: 'choice',
        criteria: {
          returns: 'Refunds, exchanges, and damaged items',
          billing: 'Charges and invoices',
        },
      },
    },
  }),
});
if (!response.ok) {
  throw new Error(`Decision request failed (${response.status}): ${await response.text()}`);
}
const decision = await response.json();
console.log(decision.answers.customer.spans);
```

## Read the response

Each extract answer has a `spans` map with one list per field, and a `not_stated` map. The request above returns:

```json theme={null}
{
  "model": "mercury-decide",
  "answers": {
    "customer": {
      "type": "extract",
      "spans": {
        "name": [{"text": "Maria Lopez", "start": 12, "end": 23, "confidence": 0.9998}],
        "order_id": [{"text": "#A-1042", "start": 34, "end": 41, "confidence": 0.9795}],
        "phone": [{"text": "415-555-0199", "start": 77, "end": 89, "confidence": 1.0}],
        "email": []
      },
      "not_stated": {"name": 0.0002, "order_id": 0.0205, "phone": 0.0, "email": 0.9999}
    },
    "team": {
      "type": "choice",
      "choice": "returns",
      "probabilities": {"returns": 0.9998, "billing": 0.0002},
      "confidence": 0.9996
    }
  },
  "usage": {"input_tokens": 216, "output_tokens": 0}
}
```

* `start` and `end` are character offsets into the source text, with `end` exclusive. `text` always equals `source[start:end]` in Python, or `source.slice(start, end)` in JavaScript.
* A field can return several spans, or an empty list when nothing matches.
* `not_stated` is the probability that the text contains nothing for that field.
* A span's `confidence` is the confidence of its least certain token.
* Without `criteria`, the question has a single field named after the question. For example, a question named `person` is read at `answers.person.spans.person`.

## Question fields

| Field | | Description |
| - | - | - |
| `instructions` | Required if there are no `criteria` | What to mark. With `criteria`, it becomes framing shared by every field. |
| `criteria` | Required if there are no `instructions` | A map of 1–26 field names to descriptions. All fields are extracted in one pass. |
| `source` | Required when `state` is an object | The key of the string field in `state` to extract from. Offsets then refer to `state[source]`. |
| `threshold` | Optional, default `0` | Drops spans with a confidence below this value. |
| `snap_to_words` | Optional, default `true` | Widens spans that end inside a word to cover the whole word. |
| `debug` | Optional, default `false` | Adds a `tokens` list with each token's text, offsets, BIO `tag`, and probability `p`. |

Plain-language constraints work in descriptions, for example "the first phone number mentioned" or "mark each person only once".

## Extract from an object state

When `state` is an object, every question still sees all of its fields, but an extract question needs `source` to say which string field to mark:

```json theme={null}
{
  "model": "mercury-decide",
  "state": {
    "subject": "Damaged order",
    "body": "Order #A-1042 arrived broken. Ship a replacement to 12 Oak St, Austin TX."
  },
  "questions": {
    "address": {
      "type": "extract",
      "instructions": "The shipping address",
      "source": "body",
      "threshold": 0.5
    }
  }
}
```

The span `12 Oak St, Austin TX` comes back with `start: 52` and `end: 72`, which are offsets into `state.body`.

## Tips

* **Batch questions about the same text in one request.** The text is encoded once and shared across questions, so one request with several questions costs far less than several requests.
* **Each token belongs to at most one field within a question.** If two fields need the same words, such as `city` and `address`, only one of them gets the words. Put overlapping fields in separate extract questions.
* **Prefer more fields over more extract questions.** Each extract question reads its own copy of the text, so it counts against the request's token limit. Fields within one question share a single copy.
* **Extract the phrase, then parse it in code.** For numbers and dates, extract the text that mentions them and convert it yourself.
* **Use a `noul` or `choice` for the verdict, and `extract` for the evidence.** For example, ask whether a message is a complaint with a `noul`, and extract the sentences that show it.

## Pricing and limits

Extract questions are billed like any Mercury Decide request: input tokens only, at the price on [Models & Pricing](/get-started/models). Output tokens are not charged.

* An extract question can have up to 26 fields in `criteria`.
* A request can be up to 32,768 tokens, including the internal copy of the text for each extract question. Split longer documents into sections, and add each section's starting offset to its spans.
* Requests that contain an extract question do not support [images](/capabilities/decisions#send-images).
* Validation errors, such as a missing `source` or too many fields, return a `422` error that names the problem. See [Error Codes](/resources/error-codes) for other errors.


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