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

# Ground Extracted Fields

> Map extracted fields to the blocks they were quoted from, and get page numbers and bounding boxes for each value.

export const dpt3verity = 'DPT-3 Verity';

export const dpt3pro = 'DPT-3 Pro';

The [Ground API](https://docs.landing.ai/api-reference/ground/ade-ground) maps each field extracted by [Extract](./extract) back to the blocks it was quoted from in the [Parse](./parse) response, and returns each one with its id, page number, and bounding box. A **block** is one piece of content Parse detected on a page, such as a paragraph, a table, a single table cell, or a figure.

Extract already locates every value in the Markdown text through [range grounding](./extract-response#grounding-with-ranges). Ground turns those text locations into visual ones, so you can highlight values on a rendering of the page, build review interfaces where a person clicks a field and sees where it came from, attach citations to extracted data, or crop the region a value appears in.

Ground runs synchronously and [consumes no credits](./credit-consumption#ground).

## How Ground Works

Both objects you send describe positions in the same Markdown string. A field's `ranges` say which characters the value was quoted from. A block's `grounding.range` says which characters the block covers. Ground overlaps the two and returns every block a field's ranges fall inside.

The join is stateless. Nothing is stored server-side, and the block ids in the response resolve only against the `structure` you send in the same request.

You send two objects you already have from earlier responses:

* `extraction_metadata`: the per-field metadata returned by [Extract](./extract-response#per-field-metadata-extraction_metadata), whose leaves are `{value, ranges}` objects.
* `structure`: the block tree returned by [Parse](./parse-response#structure-pages-and-blocks), whose blocks each carry a `grounding` object.

## Pair the Extraction with Its Own Parse

The `extraction_metadata` must come from an Extract call that ran on the `markdown` of the same Parse response the `structure` came from. Re-parsing invalidates an older extraction, because block ranges shift between parses even of the same file. If the locations come back wrong, see [Troubleshoot Grounding](./ground-troubleshoot#the-locations-point-at-the-wrong-content).

Ground cannot detect a mismatched pair, so check it in code. The Extract response's `metadata.doc_id` carries the `job_id` of the parse whose `markdown` it read:

```python theme={null}
assert extract_response.metadata.doc_id == parse_response.metadata.job_id
```

## Call the Ground API

Send both objects to the ground endpoint with a POST request. The endpoint accepts a JSON body (used by the libraries below) or multipart form data with each field JSON-serialized (used in the cURL example). The cURL example assumes you saved the Parse response to `parse.json` and the Extract response to `extract.json`.

<CodeGroup>
  ```bash cURL theme={null}
  jq '.structure' parse.json > structure.json
  jq '.extraction_metadata' extract.json > extraction-metadata.json

  curl -X POST 'https://api.ade.landing.ai/v2/ground' \
    -H 'Authorization: Bearer YOUR_API_KEY' \
    -F 'extraction_metadata=<extraction-metadata.json' \
    -F 'structure=<structure.json'
  ```

  ```python Python theme={null}
  from pathlib import Path
  from landingai_ade import LandingAIADE

  client = LandingAIADE()

  parse_response = client.v2.parse(
      document=Path("document.pdf"),
      model="dpt-3-pro-latest",
  )

  extract_response = client.v2.extract(
      markdown=parse_response.markdown,
      schema={
          "type": "object",
          "properties": {
              "report_number": {"type": "string", "description": "The report number"},
              "serial_number": {"type": "string", "description": "The serial number of the instrument under test"},
              "purchase_order": {"type": "string", "description": "The purchase order number"},
          },
      },
  )

  ground_response = client.v2.ground(
      extraction_metadata=extract_response.extraction_metadata,
      structure=parse_response.structure,
  )
  print(ground_response.grounding)
  ```

  ```typescript TypeScript theme={null}
  import fs from "fs";
  import LandingAIADE from "landingai-ade";

  const client = new LandingAIADE();

  const parseResponse = await client.v2.parse({
    document: fs.createReadStream("document.pdf"),
    model: "dpt-3-pro-latest",
  });

  const extractResponse = await client.v2.extract({
    markdown: parseResponse.markdown,
    schema: {
      type: "object",
      properties: {
        report_number: { type: "string", description: "The report number" },
        serial_number: { type: "string", description: "The serial number of the instrument under test" },
        purchase_order: { type: "string", description: "The purchase order number" },
      },
    },
  });

  const groundResponse = await client.v2.ground({
    extraction_metadata: extractResponse.extraction_metadata,
    structure: parseResponse.structure,
  });
  console.log(groundResponse.grounding);
  ```
</CodeGroup>

<Info>
  For the full request and response contract, see the [API reference](https://docs.landing.ai/api-reference/ground/ade-ground).
</Info>

## The Grounding Tree

The response's `grounding` field mirrors your `extraction_metadata` tree: nested objects and arrays keep their shape, so a nested field like `issuer.name` resolves to `grounding.issuer.name`. Each `{value, ranges}` leaf is replaced by one of three values:

| Leaf value       | What it means                                                                                                                                                                                                                 |
| ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| A list of blocks | The blocks the field's ranges overlap, in reading order.                                                                                                                                                                      |
| `null`           | The field's `ranges` was `null`, so nothing in the document was quoted for it. Either the model synthesized the value or it did not find one. There is no source passage to look up.                                          |
| `[]`             | The ranges were valid but overlapped no block. This usually means the `extraction_metadata` and `structure` are from different parses. See [Pair the Extraction with Its Own Parse](#pair-the-extraction-with-its-own-parse). |

### Matching Blocks

Only content blocks are returned. The `page` nodes in the `structure` never appear, because pages are containers rather than content. Each returned block reports its own page number in `grounding.page` instead.

A field with multiple ranges pools the blocks from all of them, without duplicates. A single range can still match more than one block: a value inside a table matches both the `table` and the `table_cell` that contains it, so you can highlight the whole table or just the cell.

Each entry in the list identifies one block from the `structure` you sent:

| Field              | Description                                                                                                                                                                                                                 |
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `block_id`         | The block's `id` in the `structure` tree, such as `text-1` or `table_cell-3`. Use it to look the block up. Block ids resolve only against the `structure` sent in the same request.                                         |
| `type`             | The block's type. See [Block Types](./parse-response#block-types).                                                                                                                                                          |
| `parent_id`        | The id of the enclosing block. Present only on nested blocks: a `table_cell` names its `table`.                                                                                                                             |
| `grounding`        | The block's own `{page, range, box}` object, copied unchanged from the `structure`. The `box` is in normalized page coordinates. See [Working with Boxes](./parse-response#working-with-boxes) for converting it to pixels. |
| `atomic_grounding` | The matching subset of the block's own [`atomic_grounding`](./parse-response#atomic-grounding) entries. See [Matches Inside a Block](#matches-inside-a-block).                                                              |

### Matches Inside a Block

The `grounding` on a returned block covers the whole block. When the block also carries [`atomic_grounding`](./parse-response#atomic-grounding), the response narrows the location to the individual entries the field's ranges overlap, each with its own, tighter box.

Each entry is an `{index, page, range, box}` object, and carries a [`confidence`](./parse-response#word-confidence-scores-dpt-3-verity) score as well on a {dpt3verity} parse. The `index` is the entry's position in the block's own `atomic_grounding` array, so you can match it back to the block's full list.

A returned block can also carry no entries at all:

| Value                     | What it means                                                                                                                                                                                              |
| ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `[]`                      | The block's range matched, but no individual entry did. Highlight the block's own `box` instead. A `table_cell` reports this on a {dpt3pro} parse, which gives cells no finer detail than the cell itself. |
| No `atomic_grounding` key | The block has no `atomic_grounding` of its own. A `table` never does.                                                                                                                                      |

## Example Response

This response grounds three fields extracted from a one-page PDF parsed with {dpt3pro}. The `report_number` was quoted from a text block, so the returned block carries the matching line entry. The `serial_number` sits in a table, so its range matches both the `table` and the `table_cell` that contains it. The report has no purchase order, so `purchase_order` has no source passage and its grounding is `null`.

```json expandable theme={null}
{
  "grounding": {
    "report_number": [
      {
        "block_id": "text-0",
        "type": "text",
        "grounding": {
          "page": 1,
          "range": { "start": 43, "end": 143 },
          "box": { "xmin": 0.7558, "ymin": 0.06102, "xmax": 0.91343, "ymax": 0.11799 }
        },
        "atomic_grounding": [
          {
            "index": 0,
            "page": 1,
            "range": { "start": 43, "end": 67 },
            "box": { "xmin": 0.77067, "ymin": 0.06525, "xmax": 0.90643, "ymax": 0.07522 }
          }
        ]
      }
    ],
    "serial_number": [
      {
        "block_id": "table-0",
        "type": "table",
        "grounding": {
          "page": 1,
          "range": { "start": 298, "end": 719 },
          "box": { "xmin": 0.09481, "ymin": 0.23333, "xmax": 0.90513, "ymax": 0.34713 }
        }
      },
      {
        "block_id": "table_cell-7",
        "type": "table_cell",
        "parent_id": "table-0",
        "grounding": {
          "page": 1,
          "range": { "start": 482, "end": 493 },
          "box": { "xmin": 0.7532, "ymin": 0.26184, "xmax": 0.90513, "ymax": 0.29022 }
        },
        "atomic_grounding": []
      }
    ],
    "purchase_order": null
  },
  "metadata": {
    "job_id": "ground-01m1n7d2apjktw530s7bnfmnst",
    "duration_ms": 118,
    "openapi_spec": "https://api.ade.landing.ai/openapi.json",
    "billing": {
      "service_tier": "priority",
      "total_credits": 0.0
    }
  }
}
```

## Metadata

The `metadata` field provides information about the request:

| Field          | Type   | Description                                                                                                                                            |
| -------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `job_id`       | string | The unique identifier for this request, prefixed `ground-`.                                                                                            |
| `duration_ms`  | number | The end-to-end request duration in milliseconds.                                                                                                       |
| `openapi_spec` | string | The URL of the OpenAPI spec that describes this API. Use it for inspection or client generation.                                                       |
| `billing`      | object | Billing details: `service_tier` reports `priority`, and `total_credits` is always `0.0`. Grounding [consumes no credits](./credit-consumption#ground). |

## Limits

* The Ground API is synchronous only. There is no Ground Jobs API, because the join completes quickly and the response arrives inline.
* There is no `model` parameter. The join is a deterministic overlap computation, so there is no model version to pin.
* The Ground API is not available for organizations with [Zero Data Retention](../ade/zdr) enabled. Requests from ZDR-enabled organizations return a 501 error. See [Troubleshoot Grounding](./ground-troubleshoot#status-501-not-implemented).
