Skip to content
Docs Security Sign in
PdfParse

PDF parsing for RAG with chunk metadata

Retrieval quality starts at parsing. PdfParse splits each document along its own sections into chunks sized for your embedding model, and every chunk carries the pages it came from, the heading path above it and the box where it sits on the page. Your answers can cite the exact paragraph instead of a whole file.

Parse PDF

Inspect the chunks of a real document

Drop a report or a manual and open the chunks view to see each section boundary, token count and source box on page one.

Drop a PDF or scan

PDF, PNG or JPG up to 10 MB. Page one is parsed in full, right here.

Or try a sample file
Output

Optional. Comma separated field names, for example invoice_number, total, due_date.

Page one is parsed in your browser and never uploaded. No card needed to try page one.

What one call returns

Page one rendered with every block outlined, next to the JSON your code receives.

  1. Reading text layer
  2. Detecting layout
  3. Extracting tables
  4. Building JSON
Rendered page Page 1 of your document

Headings, paragraphs, tables and totals detected

heading
Invoice
paragraph
Harbor Street Joinery
14 Dock Lane
Portsmouth, NH 03801
key_value
Invoice no.INV-2026-0418IssuedOct 14, 2026DueNov 13, 2026
table
DescriptionQtyUnitAmount
Oak countertop, 2.4 m2640.001,280.00
Cabinet doors, shaker1285.001,020.00
On site fitting1450.00450.00
total
Subtotal2,750.00Tax 8.5%233.75Total dueUSD 2,983.75
paragraph
Payment by bank transfer within 30 days.
Quote the invoice number with your transfer.
Goods remain ours until paid in full.
footer
Harbor Street JoineryPage 1 of 2
Locked page

Pages 2 and later are parsed after you create an account.

Output
{
  "document": {
    "pages": 2,
    "parsed_pages": 1,
    "language": "en"
  },
  "pages": [
    {
      "number": 1,
      "blocks": [
        { "type": "heading", "text": "Invoice", "bbox": [48, 52, 132, 84] },
        { "type": "paragraph", "text": "Harbor Street Joinery, 14 Dock Lane, Portsmouth" },
        { "type": "key_value", "pairs": {
            "Invoice no.": "INV-2026-0418",
            "Issued": "Oct 14, 2026",
            "Due": "Nov 13, 2026" } },
        { "type": "table", "columns": ["Description", "Qty", "Unit", "Amount"],
          "rows": [
            ["Oak countertop, 2.4 m", 2, 640.00, 1280.00],
            ["Cabinet doors, shaker", 12, 85.00, 1020.00],
            ["On site fitting", 1, 450.00, 450.00] ] },
        { "type": "total", "label": "Total due", "value": 2983.75, "currency": "USD", "confidence": 0.98 }
      ]
    }
  ],
  "fields": {
    "invoice_number": { "value": "INV-2026-0418", "confidence": 0.99, "page": 1 },
    "total": { "value": 2983.75, "confidence": 0.98, "page": 1 },
    "due_date": { "value": "2026-11-13", "confidence": 0.97, "page": 1 }
  }
}

Downloads from the demo include a watermark field and cover page one only.

Page one is parsed in your browser, so your file never reaches our servers.

Request
The same request from your code
Read the API reference
$ curl https://api.pdfparse.ai/v1/parse \
  -H "Authorization: Bearer $PDFPARSE_API_KEY" \
  -F file[email protected] \
  -F outputs=json,markdown,csv
200 OK, 1 page parsed

Chunks with everything a citation needs

Splitting text every N characters cuts tables in half and glues the end of one topic to the start of the next. PdfParse chunks by structure first and by size second, so each chunk covers one coherent part of the document.

  • text, written as Markdown so headings and tables stay readable to the model
  • page_start and page_end, for a citation that points to the right page
  • section_path, the chain of headings above the chunk, from chapter down to subsection
  • bbox, the region on the page, so your interface can highlight the source passage
  • A maximum size in tokens that you set, with tables kept whole whenever they fit
JSON
{
  "chunks": [
    {
      "text": "### 4.2 Data retention\n\nCustomer records are kept for seven years after the account closes...",
      "page_start": 18,
      "page_end": 19,
      "section_path": ["4 Compliance", "4.2 Data retention"],
      "bbox": [72, 96, 540, 702]
    }
  ]
}

How documents become retrieval ready

  1. 1

    Send the PDF or scan with a chunking option, for example strategy section and max_tokens 800.

  2. 2

    The parser restores reading order, detects heading levels and finds tables, running OCR where needed.

  3. 3

    The document tree is cut at section boundaries, and long sections are split further on paragraph edges.

  4. 4

    Each chunk gets its page range, section path and bounding box, then goes into the chunks array.

  5. 5

    You embed the chunk text, store the metadata next to the vector and show it as a citation at answer time.

Why the chunks stay clean on real PDFs

A retrieval system answers with whatever the parser gave it. If a chunk mixes two columns or carries a footer into every passage, the model quotes noise. These are the document traits we handle before chunking.

  • Two column papers and newsletters are read column by column, so no chunk interleaves two threads.
  • Tables with merged cells are written as Markdown tables inside one chunk instead of being split row by row.
  • Scanned manuals and old reports are recognized with OCR and chunked by the same section logic.
  • Rotated appendix pages are straightened so their content lands under the correct heading.
  • Repeated headers, footers and page numbers are removed, so they never become the most similar text in your index.

Request chunks from the API

Pass a chunking object with your strategy and token budget. Larger documents run as jobs; use a webhook on parse.completed to index them as they finish.

curl
curl https://api.pdfparse.ai/v1/parse \
  -H "Authorization: Bearer pp_live_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://files.example.com/employee-handbook.pdf",
    "outputs": ["json", "markdown"],
    "chunking": {"strategy": "section", "max_tokens": 800}
  }'
Python
import os
from pdfparse_ai import PdfParse

client = PdfParse(api_key=os.environ["PDFPARSE_API_KEY"])
result = client.parse(
    "employee-handbook.pdf",
    outputs=["json"],
    chunking={"strategy": "section", "max_tokens": 800},
)

records = [
    {
        "text": c["text"],
        "metadata": {
            "source": "employee-handbook.pdf",
            "pages": f"{c['page_start']}-{c['page_end']}",
            "section": " > ".join(c["section_path"]),
            "bbox": c["bbox"],
        },
    }
    for c in result.chunks
]

Teams building retrieval on PdfParse

  • AI engineers building internal assistants over handbooks, policies and product manuals.
  • Legal and compliance teams that need every answer traced to a clause and page.
  • Support teams indexing technical documentation so agents and customers get cited answers.
  • Research groups loading papers and reports into a search index with section level filters.

RAG chunking starts on Growth

Section based chunking with page range, section path and bounding boxes is included from Growth at $149 a month, or $74 a month billed yearly, with 7,000 pages, webhooks and batch jobs of up to 500 files for the first load of a library. Scale at $499 a month adds 24,000 pages, a priority queue and zero retention mode for sensitive corpora. Starter at $49 a month gives Markdown and JSON without chunk metadata. See pdf extraction api pricing.

Pricing
  • Starter 2,000 pages, 1 API key, 1 seat $49 per month $24 per month, $288 billed yearly
  • Growth 7,000 pages, 5 API keys, 3 seats $149 per month $74 per month, $888 billed yearly
  • Scale 24,000 pages, 20 API keys, 10 seats $499 per month $249 per month, $2,988 billed yearly
  • Enterprise 72,000 pages, unlimited API keys with per key limits, 25 seats $1,499 per month $749 per month, $8,988 billed yearly

FAQ

How do I choose max_tokens?

Match it to your embedding model and to how much context you pass to the LLM. 500 to 1,000 tokens works well for most question answering. Sections shorter than the limit stay whole.

Are tables split across chunks?

A table is kept in one chunk whenever it fits the token limit. Very long tables are split between rows, and the header row is repeated in each part.

How do I show a citation to the user?

Use page_start and page_end for the page reference and section_path for the heading trail. The bbox lets you highlight the passage on a rendered page.

Can I re-chunk without paying for the pages again?

Each parse call counts pages in your billing period. Keep the JSON output and you can re-split it in your own code without another call.

Do my documents train any model?

No. Files are never used to train models. On Scale and above you can turn on zero retention mode so files are not kept after the call.

Give your retrieval better source text

Sign up, send a document with chunking turned on and load the result into your index.