---
title: Read spans
description: Read the content of 1 to 50 spans that Search, Answer, or Research returned, with one result for each span, in order.
doc_version: 0.1.0-preview
last_updated: 2026-09-14
---

# Read spans

`POST /namespaces/{namespace}/collections/{collection}:read-spans`

Read the content of 1 to 50 spans that Search, Answer, or Research returned, with one result for each span, in order.

**Permission:** Cluster key, or a scoped key with search on this collection. raw and file in include also need read.

[API key types and authentication](/engine/reference/authentication.md).

include names the parts, and each part fills the result field of the same name. text (the default) is the text of a document, or the speech of a video or audio file as lines with float start and end seconds. text of a video or audio file also holds the words of its lines, one line each. raw is the span cut from the file in its own format: a PDF of only the pages, the text of a text file or a mail, the image, or a clip. file is the whole stored file.

raw and file return original content, so they need the read action. Without it, the call returns forbidden (403) and reads nothing. text needs only search.

raw and file are binary parts: content_type, size_bytes, and either data or url. With delivery inline (the default), a part comes back as base64 data, up to 20 MiB of decoded data a call, in span order. A part past that comes back as a url that works for 15 minutes. With delivery url, every binary part comes back as a url. The raw part of a text file is its text and always comes back inline.

All parts of one result show the same place: the result span. With text, raw is cut to the span of the text. A part that the file has no form for, such as the text of an image, is left out of the result.

One part stops at 50,000 characters of text, 500 pages, or 600 seconds. The result span is the part returned, in the unit of the content, which can differ from the unit of the request: the text of a pages span comes back as chars with pages. next is the body of the next request: the rest of each span that did not fit, with the same include and delivery. Send it with principals until next is null. A span past the end of the file is cut to the file first.

A span that cannot be read has error on its own result, and the other spans still read: not_found (no such file, a deleted file, or a file that the principals cannot read), invalid_span (the span does not fit the file), or content_unavailable (the stored content is missing or cannot be read, and a retry gets the same answer). Pages or a file that hold no text return an empty span with text "". A silent part of a video returns lines [].

Send principals only when access_control is enforced.

## Example request

```http
POST /namespaces/acme/collections/incident-logs:read-spans HTTP/1.1
Host: engine.example.com
Authorization: Bearer <GRAPHON_ENGINE_API_KEY>
Content-Type: application/json

{
  "spans": [
    {
      "file_id": "fil_01K6Q6N7D8E9F0G1H2J3K4M5N6",
      "unit": "pages",
      "start": 1,
      "end": 3
    }
  ],
  "include": [
    "text"
  ]
}
```

## Path parameters

### `namespace`

**string · required**

Namespace name or ns_ ID. It must be visible to this key.

### `collection`

**string · required**

Collection name or col_ ID within the namespace. It must be visible to this key.

## Request body

### `delivery`

**string · optional**

How binary parts come back: inline (the default) or url. The raw part of a text file is always inline.

Default: `"inline"`.

- `inline`: Each binary part as base64 data, up to 20 MiB of decoded data a call, in span order. A part past that comes back as a url. The default.
- `url`: Every binary part as a url.

### `include`

**array<string> · optional**

The parts to return for each span. Each part fills the result field of the same name: text (the default), raw, or file. raw and file need the read action.

- Minimum items: 1

- `text`: The extracted text: the text of a document, or the speech of a video or audio file, with its lines. The default.
- `raw`: The span cut from the file in its own format: a PDF of the pages, the text of a text file or a mail, the image, or a clip.
- `file`: The whole stored file.

### `principals`

**array<string> · optional · nullable**

End-user and group identifiers supplied by your server. Required on enforced collections and invalid when access control is off. An empty list gives no results. Maximum 4,096 entries, each at most 256 UTF-8 bytes.

### `spans`

**array<object> · required**

The spans to read, as Search, Answer, or Research returned them. Each result follows the order of the spans.

- Minimum items: 1
- Maximum items: 50

#### `spans[].bbox`

**object · optional · nullable**

Not returned yet. The box of a figure on one page. Read spans accepts it and reads the whole page.

##### `spans[].bbox.bottom`

**number · required**

The bottom edge, as a fraction of the page height from the top.

- Minimum: 0
- Maximum: 1

##### `spans[].bbox.left`

**number · required**

The left edge, as a fraction of the page width from the left.

- Minimum: 0
- Maximum: 1

##### `spans[].bbox.right`

**number · required**

The right edge, as a fraction of the page width from the left.

- Minimum: 0
- Maximum: 1

##### `spans[].bbox.top`

**number · required**

The top edge, as a fraction of the page height from the top.

- Minimum: 0
- Maximum: 1

#### `spans[].end`

**integer · optional · nullable**

First unit after the span. Absent when unit is file.

- Minimum: 0
- Maximum: 2147483647

#### `spans[].file_id`

**string · required**

ID of the file within the collection.

#### `spans[].pages`

**object · optional · nullable**

The pages that a chars span of a PDF or Office file covers, from 1, so a client can show a page number. Absent on every other span. Read spans refuses pages that do not match the chars.

##### `spans[].pages.end`

**integer · required**

First page after the range.

- Minimum: 1

##### `spans[].pages.start`

**integer · required**

First page of the range, from 1.

- Minimum: 1

#### `spans[].start`

**integer · optional · nullable**

First unit of the span. Pages count from 1. Chars and seconds count from 0. Absent when unit is file.

- Minimum: 0
- Maximum: 2147483647

#### `spans[].unit`

**string · required**

pages (a section or a figure of a PDF or Office file), chars (Unicode code points of the text form), seconds (whole seconds), or file (the whole file).

- `pages`: Pages of a PDF or an office file, from 1. Page 7 is start 7, end 8.
- `chars`: Unicode code points of the text form of a file, from 0: a txt, md, or mail file, or a PDF or office file.
- `seconds`: Whole seconds of a video or an audio file, from 0.
- `file`: The whole file, with no start or end.

## Responses

### 200 · Success

The text of pages 1 and 2, as chars with pages. The span fits in one read, so next is null. A larger span returns next: the body of the next read.

Content type: `application/json`.

#### Response headers

##### `x-engine-request-id`

**string · required**

The Engine request ID. Retain it when investigating a failed or unexpected request.

##### `x-request-id`

**string · required**

The X-Request-Id you sent, echoed when it is a valid ID. Otherwise, the Engine request ID.

```json
{
  "request_id": "req_example",
  "results": [
    {
      "span": {
        "file_id": "fil_01K6Q6N7D8E9F0G1H2J3K4M5N6",
        "unit": "chars",
        "start": 0,
        "end": 3920,
        "pages": {
          "start": 1,
          "end": 3
        }
      },
      "content_type": "application/pdf",
      "text": "Incident summary. The checkout service ran out of database connections at 09:12 UTC."
    }
  ],
  "next": null
}
```

#### Response fields

##### `next`

**object · required · nullable**

The body of the next request: the rest of each span that did not fit, with the same include and delivery, or null. Send it with principals until next is null.

###### `next.delivery`

**string · required**

The delivery of the request that returned this next.

- `inline`: Each binary part as base64 data, up to 20 MiB of decoded data a call, in span order. A part past that comes back as a url. The default.
- `url`: Every binary part as a url.

###### `next.include`

**array<string> · required**

The parts of the request that returned this next.

- `text`: The extracted text: the text of a document, or the speech of a video or audio file, with its lines. The default.
- `raw`: The span cut from the file in its own format: a PDF of the pages, the text of a text file or a mail, the image, or a clip.
- `file`: The whole stored file.

###### `next.spans`

**array<object> · required**

The rest of each span that did not fit, in the unit of the content.

###### `next.spans[].bbox`

**object · optional · nullable**

Not returned yet. The box of a figure on one page. Read spans accepts it and reads the whole page.

###### `next.spans[].bbox.bottom`

**number · required**

The bottom edge, as a fraction of the page height from the top.

- Minimum: 0
- Maximum: 1

###### `next.spans[].bbox.left`

**number · required**

The left edge, as a fraction of the page width from the left.

- Minimum: 0
- Maximum: 1

###### `next.spans[].bbox.right`

**number · required**

The right edge, as a fraction of the page width from the left.

- Minimum: 0
- Maximum: 1

###### `next.spans[].bbox.top`

**number · required**

The top edge, as a fraction of the page height from the top.

- Minimum: 0
- Maximum: 1

###### `next.spans[].end`

**integer · optional · nullable**

First unit after the span. Absent when unit is file.

- Minimum: 0
- Maximum: 2147483647

###### `next.spans[].file_id`

**string · required**

ID of the file within the collection.

###### `next.spans[].pages`

**object · optional · nullable**

The pages that a chars span of a PDF or Office file covers, from 1, so a client can show a page number. Absent on every other span. Read spans refuses pages that do not match the chars.

###### `next.spans[].pages.end`

**integer · required**

First page after the range.

- Minimum: 1

###### `next.spans[].pages.start`

**integer · required**

First page of the range, from 1.

- Minimum: 1

###### `next.spans[].start`

**integer · optional · nullable**

First unit of the span. Pages count from 1. Chars and seconds count from 0. Absent when unit is file.

- Minimum: 0
- Maximum: 2147483647

###### `next.spans[].unit`

**string · required**

pages (a section or a figure of a PDF or Office file), chars (Unicode code points of the text form), seconds (whole seconds), or file (the whole file).

- `pages`: Pages of a PDF or an office file, from 1. Page 7 is start 7, end 8.
- `chars`: Unicode code points of the text form of a file, from 0: a txt, md, or mail file, or a PDF or office file.
- `seconds`: Whole seconds of a video or an audio file, from 0.
- `file`: The whole file, with no start or end.

##### `request_id`

**string · required**

The Engine request ID, also returned in the x-engine-request-id response header.

##### `results`

**array<object> · required**

One result for each request span, in the same order.

###### `results[].content_type`

**string · optional · nullable**

Content type of the file. Absent with error.

###### `results[].error`

**object · optional · nullable**

Why this span has no content. The other spans still read.

###### `results[].error.code`

**string · required**

not_found (no such file, a deleted file, or a file that the principals cannot read), invalid_span (the span does not fit the file), or content_unavailable (the stored content is missing or cannot be read, and a retry gets the same answer).

###### `results[].error.message`

**string · required**

Human-readable explanation of the failure. Do not parse this text to decide how to retry.

###### `results[].file`

**object · optional · nullable**

The whole stored file, when include has file.

###### `results[].file.content_type`

**string · required**

Content type of the part.

###### `results[].file.data`

**string · optional · nullable**

The bytes, as base64. Present when the part is inline.

###### `results[].file.expires_at`

**string (date-time) · optional · nullable**

ISO 8601 UTC time after which the URL expires.

###### `results[].file.page`

**integer · optional · nullable**

Not returned yet. The page of a page screenshot, from 1.

###### `results[].file.seconds`

**number · optional · nullable**

Not returned yet. The time of a video frame, in seconds from the start.

###### `results[].file.size_bytes`

**integer · required**

Size of the part in bytes, before base64.

###### `results[].file.url`

**string · optional · nullable**

Signed URL to the bytes. It works for 15 minutes. On a client deployment it is a gs:// URI. Present when the part did not fit inline, or delivery is url.

###### `results[].lines`

**array<object> · optional · nullable**

The lines of speech of a video or audio file that overlap span, in start order, when include has text. A line that crosses the end of span comes in the next part. Empty when nobody speaks in span.

###### `results[].lines[].end`

**number · required**

End of the line, in seconds on the file timeline, with a fraction.

###### `results[].lines[].start`

**number · required**

Start of the line, in seconds on the file timeline, with a fraction. To clip the line, send a seconds span from floor(start) to ceil(end).

###### `results[].lines[].text`

**string · required**

The words of the line.

###### `results[].raw`

**object · optional · nullable**

The span cut from the file in its own format, when include has raw: a PDF of the pages, at most 500 pages, a clip of at most 600 seconds, the image, or the text of a text file or a mail.

###### `results[].raw.content_type`

**string · required**

Content type of the part.

###### `results[].raw.data`

**string · optional · nullable**

The bytes, as base64. Present when the part is inline.

###### `results[].raw.expires_at`

**string (date-time) · optional · nullable**

ISO 8601 UTC time after which the URL expires.

###### `results[].raw.page`

**integer · optional · nullable**

Not returned yet. The page of a page screenshot, from 1.

###### `results[].raw.seconds`

**number · optional · nullable**

Not returned yet. The time of a video frame, in seconds from the start.

###### `results[].raw.size_bytes`

**integer · required**

Size of the part in bytes, before base64.

###### `results[].raw.url`

**string · optional · nullable**

Signed URL to the bytes. It works for 15 minutes. On a client deployment it is a gs:// URI. Present when the part did not fit inline, or delivery is url.

###### `results[].screenshots`

**array<object> · optional · nullable**

Not returned yet.

###### `results[].screenshots[].content_type`

**string · required**

Content type of the part.

###### `results[].screenshots[].data`

**string · optional · nullable**

The bytes, as base64. Present when the part is inline.

###### `results[].screenshots[].expires_at`

**string (date-time) · optional · nullable**

ISO 8601 UTC time after which the URL expires.

###### `results[].screenshots[].page`

**integer · optional · nullable**

Not returned yet. The page of a page screenshot, from 1.

###### `results[].screenshots[].seconds`

**number · optional · nullable**

Not returned yet. The time of a video frame, in seconds from the start.

###### `results[].screenshots[].size_bytes`

**integer · required**

Size of the part in bytes, before base64.

###### `results[].screenshots[].url`

**string · optional · nullable**

Signed URL to the bytes. It works for 15 minutes. On a client deployment it is a gs:// URI. Present when the part did not fit inline, or delivery is url.

###### `results[].span`

**object · required**

The place that the parts show, cut to the file, in the unit of the content. It can have another unit than the request span. The span of a result with error is the request span.

###### `results[].span.bbox`

**object · optional · nullable**

Not returned yet. The box of a figure on one page. Read spans accepts it and reads the whole page.

###### `results[].span.bbox.bottom`

**number · required**

The bottom edge, as a fraction of the page height from the top.

- Minimum: 0
- Maximum: 1

###### `results[].span.bbox.left`

**number · required**

The left edge, as a fraction of the page width from the left.

- Minimum: 0
- Maximum: 1

###### `results[].span.bbox.right`

**number · required**

The right edge, as a fraction of the page width from the left.

- Minimum: 0
- Maximum: 1

###### `results[].span.bbox.top`

**number · required**

The top edge, as a fraction of the page height from the top.

- Minimum: 0
- Maximum: 1

###### `results[].span.end`

**integer · optional · nullable**

First unit after the span. Absent when unit is file.

- Minimum: 0
- Maximum: 2147483647

###### `results[].span.file_id`

**string · required**

ID of the file within the collection.

###### `results[].span.pages`

**object · optional · nullable**

The pages that a chars span of a PDF or Office file covers, from 1, so a client can show a page number. Absent on every other span. Read spans refuses pages that do not match the chars.

###### `results[].span.pages.end`

**integer · required**

First page after the range.

- Minimum: 1

###### `results[].span.pages.start`

**integer · required**

First page of the range, from 1.

- Minimum: 1

###### `results[].span.start`

**integer · optional · nullable**

First unit of the span. Pages count from 1. Chars and seconds count from 0. Absent when unit is file.

- Minimum: 0
- Maximum: 2147483647

###### `results[].span.unit`

**string · required**

pages (a section or a figure of a PDF or Office file), chars (Unicode code points of the text form), seconds (whole seconds), or file (the whole file).

- `pages`: Pages of a PDF or an office file, from 1. Page 7 is start 7, end 8.
- `chars`: Unicode code points of the text form of a file, from 0: a txt, md, or mail file, or a PDF or office file.
- `seconds`: Whole seconds of a video or an audio file, from 0.
- `file`: The whole file, with no start or end.

###### `results[].text`

**string · optional · nullable**

Text of the span, when include has text: at most 50,000 characters of a document, or the words of lines, one line each. Absent for an image. Empty for pages or a file that hold no text.

## Errors

[Error format and retry guidance](/engine/reference/errors.md).

### 400 · `invalid_request`

A field, path, parameter, or conditional request rule is invalid.

Correct the request. Unknown fields are rejected.

```json
{
  "error": {
    "code": "invalid_request",
    "message": "A field, path, parameter, or conditional request rule is invalid.",
    "request_id": "req_example",
    "retryable": false,
    "details": {}
  }
}
```

### 400 · `invalid_span`

A span in the body has a bad shape, such as a pages span that starts at 0.

Send each span as Search, Answer, or Research returned it. Pages count from 1.

```json
{
  "error": {
    "code": "invalid_span",
    "message": "A span in the body has a bad shape, such as a pages span that starts at 0.",
    "request_id": "req_example",
    "retryable": false,
    "details": {}
  }
}
```

### 401 · `unauthenticated`

The API key is missing, invalid, or revoked.

Send an active key in Authorization: Bearer or X-API-Key.

```json
{
  "error": {
    "code": "unauthenticated",
    "message": "The API key is missing, invalid, or revoked.",
    "request_id": "req_example",
    "retryable": false,
    "details": {}
  }
}
```

### 403 · `forbidden`

The key can see the resource but lacks permission for this operation.

Use a key with the required action or scope kind.

```json
{
  "error": {
    "code": "forbidden",
    "message": "The key can see the resource but lacks permission for this operation.",
    "request_id": "req_example",
    "retryable": false,
    "details": {}
  }
}
```

### 404 · `not_found`

The resource is absent, deleted, or outside this key’s scope.

Check the identifier and the key’s namespace and collection scope.

```json
{
  "error": {
    "code": "not_found",
    "message": "The resource is absent, deleted, or outside this key’s scope.",
    "request_id": "req_example",
    "retryable": false,
    "details": {}
  }
}
```

### 409 · `collection_not_ready`

The collection cannot serve this retrieval request yet.

Read retryable and details.retry_after_seconds. If processing failed, inspect file errors and correct the source.

```json
{
  "error": {
    "code": "collection_not_ready",
    "message": "The collection cannot serve this retrieval request yet.",
    "request_id": "req_example",
    "retryable": true,
    "details": {
      "retry_after_seconds": 30
    }
  }
}
```

### 413 · `request_too_large`

The JSON body exceeds the route limit: 6,291,456 bytes on :answer and research, 4,194,304 bytes on :search and :read-spans, and 1,048,576 bytes elsewhere.

Reduce the JSON body. Use upload or a URI source for larger file content.

```json
{
  "error": {
    "code": "request_too_large",
    "message": "The JSON body exceeds the route limit: 6,291,456 bytes on :answer and research, 4,194,304 bytes on :search and :read-spans, and 1,048,576 bytes elsewhere.",
    "request_id": "req_example",
    "retryable": false,
    "details": {}
  }
}
```

### 503 · `temporarily_unavailable`

A dependency, cluster readiness condition, or capacity limit prevents this operation.

Inspect retryable and details. Retry only when permitted, preserving the idempotency key for the same write.

```json
{
  "error": {
    "code": "temporarily_unavailable",
    "message": "A dependency, cluster readiness condition, or capacity limit prevents this operation.",
    "request_id": "req_example",
    "retryable": true,
    "details": {}
  }
}
```

### 500 · `internal_error`

An unexpected server failure prevented this request from completing.

Retain request_id for investigation. Follow retryable before retrying a write.

```json
{
  "error": {
    "code": "internal_error",
    "message": "An unexpected server failure prevented this request from completing.",
    "request_id": "req_example",
    "retryable": false,
    "details": {}
  }
}
```

## Other request languages

### cURL

```bash
curl -sS -X POST "$GRAPHON_ENGINE_URL/namespaces/acme/collections/incident-logs:read-spans" \
  -H "Authorization: Bearer $GRAPHON_ENGINE_API_KEY" \
  -H 'Content-Type: application/json' \
  --data-raw '{
  "spans": [
    {
      "file_id": "fil_01K6Q6N7D8E9F0G1H2J3K4M5N6",
      "unit": "pages",
      "start": 1,
      "end": 3
    }
  ],
  "include": [
    "text"
  ]
}'
```

### Python

```python
import json
import os
import urllib.request

request = urllib.request.Request(
    os.environ["GRAPHON_ENGINE_URL"] + "/namespaces/acme/collections/incident-logs:read-spans",
    data=json.dumps({"spans": [{"file_id": "fil_01K6Q6N7D8E9F0G1H2J3K4M5N6", "unit": "pages", "start": 1, "end": 3}], "include": ["text"]}).encode("utf-8"),
    headers={
        "Authorization": "Bearer " + os.environ["GRAPHON_ENGINE_API_KEY"],
        "Content-Type": "application/json",
    },
    method="POST",
)
with urllib.request.urlopen(request) as response:
    print(response.read().decode())
```

### JavaScript

```javascript
const response = await fetch(process.env.GRAPHON_ENGINE_URL + "/namespaces/acme/collections/incident-logs:read-spans", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.GRAPHON_ENGINE_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({"spans":[{"file_id":"fil_01K6Q6N7D8E9F0G1H2J3K4M5N6","unit":"pages","start":1,"end":3}],"include":["text"]}),
});
console.log(response.status, await response.text());
```

## Sitemap

See the full [sitemap](/sitemap.md) for all pages.
