Claude API PDF Text Extraction Workflow Guide
Claude API PDF Text Extraction Workflow
If you're searching for a Claude API PDF text extraction workflow, you're probably trying to pull structured data, summaries, or raw text out of invoices, contracts, research papers, or scanned forms without building a custom OCR pipeline. The short answer: Claude's multimodal models can read PDFs directly (as document blocks or rendered images), interpret layout and tables, and return exactly the text or JSON structure you ask for in a single API call.
This matters because traditional PDF extraction tools (PyPDF2, pdfplumber, Tesseract OCR) choke on multi-column layouts, scanned documents, handwriting, and tables that don't follow a clean grid. Claude handles all of these because it's reasoning over the visual and textual content together, not just parsing a byte stream. Below is a practical workflow you can implement today, along with the tradeoffs to watch for.
Why PDF extraction is harder than it looks
Most "just extract the text" requests actually require multiple layered capabilities:
- Encoding detection — many PDFs embed fonts with non-standard character maps that break naive text extraction.
- Layout awareness — multi-column academic papers, tables with merged cells, and footnotes need to be read in the right order.
- OCR for scans — a PDF that's actually a flattened image has zero extractable text without OCR.
- Structure mapping — turning "text on a page" into "field: value" pairs for downstream systems.
Claude solves all four because it treats the PDF as a document it can reason about, not just a text blob. That's the core reason an LLM-based workflow beats regex-and-library pipelines for anything beyond simple, clean PDFs.
Step 1: Prepare the PDF for the API
Claude's Messages API accepts documents as base64-encoded content blocks. Keep these practical limits in mind:
- Large PDFs should be split into logical chunks (per-section or per-N-pages) if you're hitting context or size limits.
- Scanned PDFs still work — Claude can read embedded images inside the document — but very low-resolution scans reduce accuracy.
- Strip out boilerplate pages (cover sheets, blank pages) before sending if you're optimizing for cost.
Step 2: Send the extraction request
A typical request pairs a document content block with a precise instruction. Precision in the prompt is what separates "pretty good" extraction from "production-ready" extraction.
curl -X POST https://api.subtoapi.app/v1/messages \
-H "Authorization: Bearer $SUBTOAPI_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-sonnet-4",
"max_tokens": 2048,
"messages": [
{
"role": "user",
"content": [
{
"type": "document",
"source": {
"type": "base64",
"media_type": "application/pdf",
"data": "<BASE64_PDF_DATA>"
}
},
{
"type": "text",
"text": "Extract all line items from this invoice as JSON with fields: description, quantity, unit_price, total. Return only valid JSON, no commentary."
}
]
}
]
}'
The key design choice here is forcing structured output. If you ask for "the text," you'll get prose. If you ask for a specific JSON schema, you get something your code can parse deterministically.
Step 3: Enforce structure with a schema
For production workflows, don't rely on Claude's free-text JSON — describe the exact shape you want and validate it on the way out.
const response = await fetch("https://api.subtoapi.app/v1/messages", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.SUBTOAPI_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
model: "claude-sonnet-4",
max_tokens: 4096,
messages: [{
role: "user",
content: [
{ type: "document", source: { type: "base64", media_type: "application/pdf", data: pdfBase64 } },
{ type: "text", text: `Extract this document into JSON matching this schema:
{
"title": string,
"date": string,
"parties": string[],
"key_terms": string[]
}
Return only JSON.` }
]
}]
})
});
const data = await response.json();
const extracted = JSON.parse(data.content[0].text);
If you need stricter guarantees, pair this with Claude's tool use — define a tool with an input schema and have Claude call it with the extracted fields instead of writing raw text. See /docs/tools for the mechanics.
Step 4: Handle multi-page and multi-document batches
For workflows processing hundreds of PDFs (contract review, invoice processing, research corpora), build a simple pipeline:
- Queue incoming PDFs and base64-encode each.
- Send one request per document (or per logical section) with a consistent extraction prompt.
- Validate the JSON response against your schema before writing to your database.
- Retry failed validations with a stricter "return only JSON, no markdown fences" instruction appended.
- Log token usage per document so you can forecast cost at scale.
Running this through SubToAPI gives you an HTTPS endpoint with your own sub_live_ key, streaming support for long documents, and usage metadata per call — useful when you need to track cost per document type or per customer in a multi-tenant product. Check /docs/quickstart to get a key issued in minutes, or /pricing if you're evaluating Solo vs. Team seats for a shared extraction pipeline.
Step 5: Add streaming for long documents
For long PDFs where you want partial results as they're generated (useful for progress indicators in a UI), use streaming instead of waiting for the full response:
const stream = await fetch("https://api.subtoapi.app/v1/messages", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.SUBTOAPI_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
model: "claude-sonnet-4",
max_tokens: 4096,
stream: true,
messages: [{ role: "user", content: [
{ type: "document", source: { type: "base64", media_type: "application/pdf", data: pdfBase64 } },
{ type: "text", text: "Summarize each section of this document in order." }
]}]
})
});
Details on consuming the event stream are in /docs/streaming.
Common pitfalls
- Skipping validation — always JSON-parse and validate before trusting output downstream.
- One giant prompt for everything — splitting extraction (get raw text) from transformation (map to schema) as two calls often produces more reliable results than one mega-prompt.
- Ignoring page limits — if a PDF is enormous, chunk it by section rather than hoping the model handles 300 pages in one call.
- Not setting
max_tokenshigh enough — truncated JSON is the most common cause of parse failures in extraction pipelines.
questions
Can Claude extract text from scanned (image-based) PDFs? Yes. Claude reads the visual content of the PDF, including embedded images, so it can OCR scanned pages without a separate OCR tool, though very low-resolution scans reduce accuracy.
How do I get structured JSON instead of plain text? Describe the exact JSON schema you want in your prompt, set max_tokens high enough to avoid truncation, and validate the parsed output before using it — or use tool calls for stricter schema enforcement (/docs/tools).
What's the best way to process hundreds of PDFs at scale? Build a queue that sends one request per document with a consistent prompt, validates each response, retries failures, and tracks token usage per call — SubToAPI's usage metadata (/docs/messages) makes this easy to monitor across a team.