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

# Bounding Boxes

> Understanding layout information from document extraction

## Overview

When extracting content with layout information, Pulse API returns bounding box coordinates for text, tables, and images. This spatial data enables precise document understanding and region-based extraction.

## Bounding Box Format

Bounding boxes are returned as normalized coordinates (0-1 range) in an 8-point format:

```
[x1, y1, x2, y2, x3, y3, x4, y4]
```

Where:

* **(x1, y1)** = Top-left corner
* **(x2, y2)** = Top-right corner
* **(x3, y3)** = Bottom-right corner
* **(x4, y4)** = Bottom-left corner

<Note>
  Coordinates are normalized to 0-1 range, making them resolution-independent. To convert to pixels, multiply by the page width/height.
</Note>

## Response Structure

The `bounding_boxes` object groups every detected layout element by its public category. Categories with no detected elements may be omitted.

```json theme={null}
{
  "bounding_boxes": {
    "Title": [{"id": "txt-1", "original_content": "Statement"}],
    "Header": [],
    "Text": [],
    "List Items": [],
    "Footer": [],
    "caption": [],
    "Page Number": [],
    "Formulas": [],
    "Tables": [{"table_info": {}, "cell_data": []}],
    "Images": [],
    "Words": [],
    "SelectionMarks": [],
    "markdown_with_ids": "..."
  }
}
```

<Note>
  Every detected layout element is routed into a public grouped category; list items, captions, footers, page numbers, formulas, and selection marks are not discarded. Clients do not need to parse provider-specific category names.
</Note>

### Markdown Fields

| Field               | Location                | Description                                                                     |
| ------------------- | ----------------------- | ------------------------------------------------------------------------------- |
| `markdown`          | Top-level response      | Clean markdown content without any ID attributes                                |
| `markdown_with_ids` | Inside `bounding_boxes` | Markdown with `data-bb-*` ID attributes that link text to bounding box elements |

Use `bounding_boxes.markdown_with_ids` when you need to correlate text positions with bounding boxes. Use the top-level `markdown` for clean content display or export.

## Example Response

Here's a real example of the `bounding_boxes` object from a workbook with an embedded chart, with `figure_processing.show_images: true`:

```json theme={null}
{
  "Images": [
    {
      "id": "excel_image_1_1",
      "visual_type": "chart",
      "page_number": 1,
      "bounding_box": [],
      "image_url": "https://api.runpulse.com/results/13e3e75f-a89a-4d33-a391-e1a17127ab38/images/excel_image_1_1.png",
      "sheet_name": "Charts",
      "excel_range": "D2",
      "chart_type": "BarChart",
      "chart_title": "Revenue",
      "source_ranges": ["'Charts'!$A$2:$A$5", "'Charts'!$B$2:$B$5"],
      "description": "Bar chart showing revenue by quarter."
    }
  ],
  "Tables": [],
  "Text": [
    {
      "id": "txt-2",
      "content": "0a-NCRI",
      "original_content": "NCRI",
      "bounding_box": [0.0267, 0.0872, 0.0689, 0.0789, 0.0743, 0.0908, 0.0321, 0.0996],
      "page_number": 1,
      "average_word_confidence": 0.973
    }
  ],
  "Title": [
    {
      "id": "txt-1",
      "content": "0a-Doctor Prescription",
      "original_content": "Doctor Prescription",
      "bounding_box": [0.2196, 0.1225, 0.4578, 0.1348, 0.4557, 0.1537, 0.2174, 0.1417],
      "page_number": 1,
      "average_word_confidence": 0.995
    }
  ]
}
```

## Field Descriptions

<Note>
  All `page_number` and `location.page` values are 1-indexed **original document** page numbers. This holds even when the request used a `pages=` subset: extracting `pages="10-20"` produces items labeled pages 10 through 20 across text items, tables, `Words`, and extension output.
</Note>

### Text Array

Each text element contains:

* `id`: Unique identifier (e.g., `txt-1`) that links to `markdown_with_ids` via `data-bb-text-id`
* `content`: The extracted text with prefix (e.g., `0a-NCRI`)
* `original_content`: The clean extracted text without prefix
* `bounding_box`: 8-point coordinate array (may be `null` for some document types)
* `page_number`: Page where the text appears
* `average_word_confidence`: OCR confidence score (0-1)
* `selected`: Selection state when `detect_selections` was enabled and the item represents a detected form control or marked-choice region

### Title Array

Each title element contains:

* `id`: Unique identifier linking to markdown
* `content`: The title text with prefix
* `original_content`: The clean title text
* `bounding_box`: 8-point coordinate array
* `page_number`: Page where the title appears
* `average_word_confidence`: OCR confidence score (0-1)

### Header Array

Each header element contains:

* `id`: Unique identifier linking to markdown
* `content`: The header text with prefix
* `original_content`: The clean header text
* `bounding_box`: 8-point coordinate array
* `page_number`: Page where the header appears
* `average_word_confidence`: OCR confidence score (0-1)

### Footer Array

Each footer element contains:

* `id`: Unique identifier linking to markdown
* `content`: The footer text with prefix
* `original_content`: The clean footer text
* `bounding_box`: 8-point coordinate array
* `page_number`: Page where the footer appears
* `average_word_confidence`: OCR confidence score (0-1)

### Images Array

Each image element represents a detected chart or embedded image. For PDFs and image inputs, entries are populated when figure detection runs. For spreadsheets, entries are populated for embedded charts and images directly read from the workbook.

| Field                  | When populated                             | Description                                                                                                                                                      |
| ---------------------- | ------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                   | always                                     | Stable visual identifier (e.g. `excel_image_1_1`, `fig-3`). Joins to the `data-bb-image-id` attribute in `markdown_with_ids`.                                    |
| `visual_type`          | always                                     | `"chart"` (data visualization) or `"image"` (non-chart embedded/detected visual).                                                                                |
| `page_number`          | always                                     | 1-indexed page or sheet number.                                                                                                                                  |
| `bounding_box`         | PDFs/images                                | 8-point coordinate polygon. Empty array for spreadsheets — use `excel_range` instead.                                                                            |
| `image_url`            | when `figure_processing.show_images: true` | Pulse-hosted URL for the visual image bytes. Fetch via [`results.getImage`](/current/api-reference/endpoint/results-image) or any HTTP client with your API key. |
| `description`          | when `figure_processing.description: true` | LLM-generated 1–2 sentence caption.                                                                                                                              |
| `content`              | usually for spreadsheets                   | Short caption (e.g. `Chart: Revenue`).                                                                                                                           |
| `sheet_name`           | spreadsheets                               | Workbook sheet the visual lives on.                                                                                                                              |
| `sheet_index`          | spreadsheets                               | Parsed sheet index after hidden-sheet filtering.                                                                                                                 |
| `workbook_sheet_index` | spreadsheets                               | Original workbook sheet index.                                                                                                                                   |
| `excel_range`          | spreadsheets                               | Anchor cell or covered cell range (e.g. `D2:K18`).                                                                                                               |
| `chart_type`           | spreadsheet charts                         | Chart class name (e.g. `BarChart`, `LineChart`, `PieChart`).                                                                                                     |
| `chart_title`          | spreadsheet charts                         | Detected chart title text.                                                                                                                                       |
| `source_ranges`        | spreadsheet charts                         | Cell ranges feeding the chart (e.g. `["Charts!$B$1:$B$3"]`).                                                                                                     |
| `classification`       | optional                                   | `{confidence, model, error}` when classification ran.                                                                                                            |
| `render_error`         | optional                                   | Non-fatal rendering error for spreadsheet visuals. When set, the entry is still returned but `image_url` may be omitted.                                         |
| `description_error`    | optional                                   | Non-fatal description-generation error.                                                                                                                          |

## Fetching Visual Image Bytes

When you set `figure_processing.show_images: true` on `/extract`, every chart/image entry comes back with an `image_url` pointing at [`GET /results/{jobId}/images/{filename}`](/current/api-reference/endpoint/results-image). Fetch it with your API key to get the raw PNG/JPEG bytes:

<CodeGroup>
  ```python Python theme={null}
  import re
  from pulse import Pulse
  from pulse.types import ExtractRequestFigureProcessing

  client = Pulse(api_key="YOUR_API_KEY")
  response = client.extract(
      file=open("financials.xlsx", "rb"),
      figure_processing=ExtractRequestFigureProcessing(show_images=True),
  )

  for img in response.bounding_boxes.images or []:
      m = re.search(r"/results/([^/]+)/images/([^/?#]+)", img.image_url)
      job_id, filename = m.group(1), m.group(2)
      chunks = list(client.results.get_image(job_id=job_id, filename=filename))
      with open(filename, "wb") as f:
          f.write(b"".join(chunks))
  ```

  ```typescript TypeScript theme={null}
  import { PulseClient } from "pulse-ts-sdk";
  const client = new PulseClient({ apiKey: "YOUR_API_KEY" });

  const response = await client.extract({
      file: fs.createReadStream("financials.xlsx"),
      figureProcessing: { showImages: true },
  });

  for (const img of response.boundingBoxes?.Images ?? []) {
      const m = img.imageUrl?.match(/\/results\/([^/]+)\/images\/([^/?#]+)/);
      if (!m) continue;
      const [, jobId, filename] = m;
      const image = await client.results.getImage({ jobId, filename });
      // Persist `image` per your runtime.
  }
  ```
</CodeGroup>

See [Get Result Image](/current/api-reference/endpoint/results-image) for the full auth contract — visual image fetches always require same-org `x-api-key` authentication; there is no anonymous access.

### Tables Array

The extraction engine decides which table regions exist. Pulse then runs CPU-only cell geometry detection inside those exact table crops. It never creates extra tables from regions the extraction engine did not identify.

Each `Tables[]` entry contains:

| Field                             | Type           | Description                                                                  |
| --------------------------------- | -------------- | ---------------------------------------------------------------------------- |
| `table_info.id`                   | string         | Stable table ID such as `tbl-1`.                                             |
| `table_info.dimensions.rows`      | integer        | Occupied grid rows after applying row spans.                                 |
| `table_info.dimensions.columns`   | integer        | Occupied grid columns after applying column spans.                           |
| `table_info.location.coordinates` | number\[]      | Normalized 8-number table polygon.                                           |
| `table_info.location.page`        | integer        | 1-indexed page number.                                                       |
| `table_info.confidence`           | number or null | Confidence derived from words in the table when available.                   |
| `cell_data`                       | object\[]      | Every cell authored by the extracted table structure, including empty cells. |

Each `cell_data[]` item contains:

| Field                      | Type           | Description                                                                                       |
| -------------------------- | -------------- | ------------------------------------------------------------------------------------------------- |
| `id`                       | string         | Stable cell ID such as `tbl-1-r0c0`; it also appears as `data-bb-cell-id` in `markdown_with_ids`. |
| `position.row`             | integer        | Zero-indexed starting row.                                                                        |
| `position.column`          | integer        | Zero-indexed starting column after occupied span slots are accounted for.                         |
| `text`                     | string         | Indexed cell text. Empty cells remain present with an empty string.                               |
| `location.coordinates`     | number\[]      | Normalized cell polygon from crop detection, or a deterministic span-aware grid fallback.         |
| `location.page`            | integer        | 1-indexed page number.                                                                            |
| `confidence`               | number or null | Cell/table confidence when word evidence is available.                                            |
| `properties.type`          | string         | `header` for header cells; omitted for data cells.                                                |
| `properties.spans_rows`    | integer        | Present only when the cell spans more than one row.                                               |
| `properties.spans_columns` | integer        | Present only when the cell spans more than one column.                                            |

```json theme={null}
{
  "table_info": {
    "id": "tbl-1",
    "dimensions": {"rows": 3, "columns": 2},
    "location": {
      "coordinates": [0.1, 0.3, 0.9, 0.3, 0.9, 0.6, 0.1, 0.6],
      "page": 1
    },
    "confidence": 0.96
  },
  "cell_data": [
    {
      "id": "tbl-1-r0c0",
      "position": {"row": 0, "column": 0},
      "text": "0t-Account",
      "location": {
        "coordinates": [0.1, 0.3, 0.5, 0.3, 0.5, 0.4, 0.1, 0.4],
        "page": 1
      },
      "confidence": 0.97,
      "properties": {"type": "header", "spans_columns": 2}
    }
  ]
}
```

### Words Array

`Words[]` is the standard word geometry collection used to preserve the extraction response contract. Each item contains `content`, `page_number`, `confidence`, and a polygon represented as `[{"x": number, "y": number}, ...]`.

```json theme={null}
{
  "content": "Account",
  "page_number": 1,
  "bounding_box": [
    {"x": 0.1, "y": 0.3},
    {"x": 0.2, "y": 0.3},
    {"x": 0.2, "y": 0.34},
    {"x": 0.1, "y": 0.34}
  ],
  "confidence": 0.99
}
```

### Selection Marks Array

When selection detection finds marked controls, `SelectionMarks[]` contains `page_number`, `state` (`selected` or `unselected`), `confidence`, and a normalized polygon.

### Page Number Array

Each page number element contains:

* `id`: Unique identifier
* `content`: The page number text
* `original_content`: The clean page number text
* `bounding_box`: 8-point coordinate array
* `page_number`: Page where it appears
* `average_word_confidence`: OCR confidence score (0-1)

<Note>
  The `id` field allows you to link bounding box elements to specific locations in the `markdown_with_ids` field via `data-bb-text-id` attributes.
</Note>

## Footnote References

When you enable `extensions.footnote_references` in your extract request, the response includes an `extensions.footnoteReferences` array that uses bounding box IDs to link footnote markers to their in-text references.

Each entry contains:

* `symbol` — the footnote marker (e.g. `*`, `†`, `‡`, `1`)
* `footnoteTextId` — the `id` of the footnote explanation, typically found in the `Footer` array
* `referenceTextIds` — an array of `id` values from the `Text`, `Title`, or `Header` arrays identifying body paragraphs that contain the marker

```json theme={null}
{
  "extensions": {
    "footnoteReferences": [
      {
        "symbol": "*",
        "footnoteTextId": "txt-42",
        "referenceTextIds": ["txt-5", "txt-12"]
      }
    ]
  }
}
```

Use `footnoteTextId` to look up the footnote's position and content in `bounding_boxes.Footer` (or `bounding_boxes.Text`), and each entry in `referenceTextIds` to locate the citing paragraphs in `bounding_boxes.Text`, `bounding_boxes.Title`, or `bounding_boxes.Header`. This allows you to spatially highlight both the footnote and every place in the document that references it.

<Note>
  Footnote references are only available for PDF documents. See the [Extract endpoint](/current/api-reference/endpoint/extract#footnote-references) for usage examples.
</Note>

## Converting Coordinates

To convert normalized coordinates to pixel coordinates:

```python theme={null}
def normalize_to_pixels(bbox, page_width, page_height):
    """Convert normalized bounding box to pixel coordinates."""
    return [
        bbox[0] * page_width,   # x1
        bbox[1] * page_height,  # y1
        bbox[2] * page_width,   # x2
        bbox[3] * page_height,  # y2
        bbox[4] * page_width,   # x3
        bbox[5] * page_height,  # y3
        bbox[6] * page_width,   # x4
        bbox[7] * page_height   # y4
    ]

# Example: Convert for a standard letter-size page at 72 DPI
page_width = 612  # 8.5 inches * 72 DPI
page_height = 792  # 11 inches * 72 DPI

normalized_bbox = [0.1, 0.1, 0.3, 0.1, 0.3, 0.15, 0.1, 0.15]
pixel_bbox = normalize_to_pixels(normalized_bbox, page_width, page_height)
```

## Next Steps

<CardGroup cols={2}>
  <Card title="Extract Endpoint" icon="code" href="/current/api-reference/endpoint/extract">
    Enable bounding box extraction
  </Card>

  <Card title="Structured Output" icon="table" href="/current/api-reference/structured-output-guidelines">
    Combine with structured data
  </Card>
</CardGroup>
