Claude API PDF Document Processing: A Practical Guide
Claude can read PDFs directly through the Messages API without any separate OCR or text-extraction step. You send the file as a document content block alongside your prompt, and the model processes both the text and the visual layout of each page — tables, charts, scanned text, and all. This guide covers the request format, practical limits, and a working implementation.
If you've been manually extracting text with a PDF library before sending it to an LLM, you can skip that step entirely for most use cases. Claude's PDF support treats each page like an image the model can see, so it understands layout, headers, and embedded figures the way a human reader would — not just a flat string of extracted text.
How Claude reads PDFs
When you attach a PDF to a request, Claude processes it as a sequence of page images combined with any extractable text layer. This matters for document types that trip up pure text extraction:
- Scanned contracts or invoices with no selectable text
- Multi-column research papers where column order breaks naive extraction
- Tables and charts where spatial position carries meaning
- Forms with checkboxes, signatures, or handwritten annotations
Because the model sees the page rather than a pre-parsed string, it can answer questions like "what's the total in the bottom-right table" or "is this checkbox marked" — things a plain text pipeline would lose.
Request format
A PDF is sent as a document block inside the content array of a user message, next to a source object that holds the base64-encoded file and its media type:
{
"model": "claude-sonnet-4",
"max_tokens": 1024,
"messages": [
{
"role": "user",
"content": [
{
"type": "document",
"source": {
"type": "base64",
"media_type": "application/pdf",
"data": "<base64-encoded-pdf>"
}
},
{
"type": "text",
"text": "Summarize the key terms and renewal clause in this contract."
}
]
}
]
}
The same structure works through SubToAPI's Messages endpoint, which proxies requests to Claude while giving you a single application key, usage metadata, and team seats. See /docs/messages for the full request reference.
curl https://api.subtoapi.app/v1/messages \
-H "Authorization: Bearer $SUBTOAPI_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-sonnet-4",
"max_tokens": 1024,
"messages": [
{
"role": "user",
"content": [
{
"type": "document",
"source": {
"type": "base64",
"media_type": "application/pdf",
"data": "'"$(base64 -i invoice.pdf)"'"
}
},
{ "type": "text", "text": "Extract vendor name, invoice number, and total due as JSON." }
]
}
]
}'
In JavaScript, you read the file, encode it, and send it the same way:
import fs from "fs";
const pdfData = fs.readFileSync("contract.pdf").toString("base64");
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: 1024,
messages: [
{
role: "user",
content: [
{ type: "document", source: { type: "base64", media_type: "application/pdf", data: pdfData } },
{ type: "text", text: "List all parties and the effective date." }
]
}
]
})
});
const data = await response.json();
console.log(data.content[0].text);
Practical limits to plan around
PDF processing has real constraints, and they affect how you design a pipeline:
- File size and page count caps. Anthropic enforces a maximum file size and page count per request — large multi-hundred-page documents need to be split before sending. Always check current limits in the API documentation rather than assuming they match older versions.
- Token cost scales with pages. Each page consumes vision tokens in addition to any text tokens, so a 50-page PDF costs noticeably more than a 50-page plain-text prompt. Budget accordingly, especially on high-volume pipelines.
- No persistent file storage. Each request sends the full base64 payload; there's no "upload once, reference later" file ID in the base Messages API, so repeated queries against the same document mean repeated uploads unless you cache the encoded data client-side.
- Mixed content works. You can combine a
documentblock withimageblocks and plaintextblocks in the same message — useful when a PDF needs to be compared against a screenshot or a separate dataset.
Common document-processing patterns
Structured extraction. Ask Claude to return JSON matching a schema — invoice fields, resume sections, contract clauses — and parse the response directly into your application. Pair this with a strict system prompt describing the exact field names you expect.
Multi-document comparison. Send two or three PDFs in one request (e.g., two contract drafts) and ask Claude to diff the terms. This avoids building a separate diffing tool for every document type you handle.
Long-document summarization. For reports beyond the page limit, split the PDF into logical sections (by chapter or page range) and summarize each chunk, then run a final pass that combines the summaries. Streaming the final response helps when the combined output is long — see /docs/streaming for implementation details.
Tool-augmented workflows. Combine PDF input with tool use so Claude can, for example, read an invoice and then call a function to look up the vendor in your database or flag a discrepancy. Check /docs/tools for the tool-calling format.
Why route PDF requests through an API layer
Sending PDFs directly to Claude works fine for prototypes, but production document pipelines usually need more: per-application API keys instead of one shared credential, visibility into which document types are consuming the most tokens, and the ability to add team members without sharing a single secret. SubToAPI sits in front of your Claude access and gives you sub_live_... keys, streaming, and usage metadata per key, so a document-processing service and a chatbot on the same team don't share billing blind spots. Get started at /signup, see plan details at /pricing, or jump into /docs/quickstart to send your first PDF request in a few minutes.
Questions
Does Claude extract text from scanned PDFs without a text layer? Yes. Because Claude processes PDF pages as images, it can read scanned documents and handwritten or low-quality text the same way it reads any image, without needing a separate OCR step first.
Can I send multiple PDFs in a single request? Yes, you can include several document blocks in one message's content array, which is useful for comparing contracts, cross-referencing invoices, or summarizing a set of related reports together.
What happens if my PDF exceeds the size or page limit? The request will fail with an error. Split large PDFs into smaller files or page ranges, process each chunk separately, and combine the results in your application logic — a common pattern for reports or books beyond the single-request limit.