Parsing for RAG

Extend Parse is a document-to-markdown API built for retrieval-augmented generation (RAG). It turns PDFs, scans, images, spreadsheets, presentations, and email into clean, layout-aware markdown that is already split into semantic chunks, each carrying the page number and bounding box you need to cite the source. You embed the chunks with the model and vector store of your choice.

This page is the end-to-end recipe. For field-level options, see Configuration; for the response shape, see Response Format.

What Parse gives a retrieval pipeline

NeedWhat Parse returns
Markdown for the LLMoutput.chunks[].content — layout-aware markdown in reading order, with headings, lists, tables, and figure summaries inline.
Chunks ready to embedchunkingStrategy.type: "section" splits at semantic boundaries (headings, tables, figures) and never breaks a markdown element across chunks. page and document strategies are also available.
CitationsEvery block has metadata.page.number, a boundingBox, and a polygon, so a retrieved chunk can be highlighted on the original page.
Tables that survive retrievalTables are emitted as HTML or markdown; nested and merged cells are preserved in HTML mode, and agentic table correction can repair misaligned structure.
Figures, charts, and diagrams as textFigures are summarized by a vision model and classified (chart, diagram, image, logo). Advanced chart extraction converts charts into data tables so the values are searchable.
Equations as LaTeXformula blocks return a latex representation for mathematical content.
ScaleAsync /parse_runs and batch processing for large document sets; Light, Auto, and Performance engines to trade accuracy against cost per page.
{
"config": {
"engine": "parse_auto",
"target": "markdown",
"chunkingStrategy": {
"type": "section",
"options": { "minCharacters": 500, "maxCharacters": 2000 }
},
"blockOptions": {
"tables": { "targetFormat": "markdown" },
"figures": { "enabled": true, "advancedChartExtractionEnabled": true },
"formulas": { "enabled": true }
}
}
}
  • parse_auto routes simple pages through the Light pipeline and complex pages through Performance, which keeps cost low on mixed document sets. Use parse_light for born-digital text at the lowest cost, or parse_performance when scans, handwriting, or dense tables dominate.
  • section chunking requires target: "markdown". Tune minCharacters and maxCharacters to your embedding model’s ideal input size.
  • Markdown tables embed well for most retrieval use cases. Switch to html when tables have merged cells or nested headers that markdown cannot express.
  • Advanced chart extraction requires parse_performance v2.0.0 (or Auto routing a page to Performance) and adds latency proportional to the number of charts.

Ingest: parse, embed, store

Each snippet parses one file with the recommended configuration and builds one record per chunk: the markdown to embed plus the file ID, page numbers, and block IDs you will need to cite the source.

from extend_ai import Extend
client = Extend()
response = client.parse(
file={"url": "https://example.com/quarterly-report.pdf"},
config={
"engine": "parse_auto",
"target": "markdown",
"chunkingStrategy": {
"type": "section",
"options": {"minCharacters": 500, "maxCharacters": 2000},
},
"blockOptions": {
"tables": {"targetFormat": "markdown"},
"figures": {"enabled": True, "advancedChartExtractionEnabled": True},
"formulas": {"enabled": True},
},
},
)
records = []
for index, chunk in enumerate(response.output.chunks):
# PDFs and images carry page metadata; spreadsheet blocks carry sheet metadata instead
pages = sorted({block.metadata.page.number for block in chunk.blocks if block.metadata.page})
sheets = sorted({block.metadata.sheet.name for block in chunk.blocks if block.metadata.sheet})
records.append(
{
"id": f"{response.file.id}:{index}",
"text": chunk.content,
"metadata": {
"file_id": response.file.id,
"pages": pages,
"sheets": sheets,
"block_ids": [block.id for block in chunk.blocks],
},
}
)
# Embed `text` with your model and upsert `records` into your vector store.

Store the block IDs, page numbers, and bounding boxes alongside each embedding. At answer time, they let you cite the exact region of the source document rather than only the file. The engine value parse_auto is passed as a string in the Java and Go snippets because those SDKs’ typed enum constants predate the Auto engine; both accept unknown enum values.

Retrieve and cite

When a chunk is retrieved, its blocks carry everything needed for a source-grounded answer:

{
"id": "chunk_qncr8Txe-wYvmFjipXgMD",
"content": "## Revenue\n\n| Quarter | Revenue |\n| --- | --- |\n| Q1 | $4.2M |\n| Q2 | $4.9M |",
"blocks": [
{
"id": "block_WNoJ0WbMj4pRW9MpMpUox",
"type": "table",
"metadata": { "page": { "number": 7, "width": 612, "height": 792 } },
"boundingBox": { "left": 61.2, "top": 158.4, "right": 550.8, "bottom": 356.4 }
}
]
}

Pass content to the LLM as context, and render boundingBox on page.number as the citation highlight. See Bounding box coordinates for reconciling coordinates with your renderer.

Scale to large document sets

  • Use the asynchronous /parse_runs endpoint and webhooks for anything larger than a handful of pages, and batch processing for backfills.
  • Bill only for what you need: Light Parse is 0.5 credits per page and Performance is 2 credits per page; see How Credits Work for the per-engine rates and worked monthly totals.
  • Re-parse only when the source changes. Block IDs are derived from content, so unchanged blocks keep stable IDs across runs.

When to add extraction

Parse alone answers “what does this document say.” When you also need “what are the fields,” run Extract against a schema on the same file. Extraction reuses the parse, returns citations with bounding boxes and confidence scores per field, and can feed a human review step for accuracy-critical records.

Next steps