Claude API PDF Document Analysis Tutorial
Claude can read PDFs natively — you don't need a separate text-extraction step for most documents. This tutorial shows exactly how to send a PDF to the Claude API, structure your prompt so you get consistent output, and handle common problems like large files, scanned pages, and multi-document batches.
If you're trying to build something like a contract reviewer, invoice parser, or research-paper summarizer, the fastest path is: base64-encode the PDF, attach it as a document content block alongside your text instructions, and ask Claude to return structured JSON. The rest of this guide walks through each piece, plus the edge cases that trip people up in production.
How PDF input works with the Claude API
Claude's Messages API accepts PDFs as a document content block inside a user message, the same way it accepts images. The model processes both the text and the visual layout of each page — so tables, headers, and multi-column layouts are understood, not just raw extracted text. This matters for documents like invoices, scanned contracts, or academic papers where position on the page carries meaning.
A basic request looks like this:
curl https://api.anthropic.com/v1/messages \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "content-type: application/json" \
-d '{
"model": "claude-sonnet-4-5",
"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 financial figures in this report in 5 bullet points."
}
]
}
]
}'
The document block must come before the text block in the content array for best results — Claude processes the document first, then applies your instruction to it.
Encoding and sending the PDF
In Node.js, reading and base64-encoding a local PDF before sending it:
import fs from "fs";
const pdfData = fs.readFileSync("./invoice.pdf").toString("base64");
const response = await fetch("https://api.anthropic.com/v1/messages", {
method: "POST",
headers: {
"x-api-key": process.env.ANTHROPIC_API_KEY,
"anthropic-version": "2023-06-01",
"content-type": "application/json",
},
body: JSON.stringify({
model: "claude-sonnet-4-5",
max_tokens: 1024,
messages: [
{
role: "user",
content: [
{
type: "document",
source: {
type: "base64",
media_type: "application/pdf",
data: pdfData,
},
},
{
type: "text",
text: "Extract the invoice number, total amount, and due date as JSON.",
},
],
},
],
}),
});
You can also reference a PDF by URL instead of inlining base64 data, which avoids bloating your request payload for larger files.
Getting structured output
For document analysis, you almost always want structured data back, not free-form prose. Two reliable approaches:
- Explicit JSON instruction — tell Claude the exact schema you expect and ask it to return only JSON.
- Tool use — define a tool with a JSON schema and let Claude call it with the extracted fields. This is more reliable for production pipelines because the output is enforced by the schema rather than hoped for in a prompt.
Example prompt for approach 1:
Extract the following fields from this PDF and return only valid JSON,
no other text:
{
"invoice_number": string,
"vendor_name": string,
"line_items": [{ "description": string, "amount": number }],
"total": number,
"due_date": "YYYY-MM-DD"
}
This works well for one-off scripts. For anything calling the API repeatedly in an app, define a tool instead — it's more robust against malformed output and plugs directly into existing function-calling code.
Handling multi-page and scanned documents
A few practical notes that affect accuracy:
- Page limits: very long PDFs consume a large number of tokens for the visual representation of each page. For documents over ~100 pages, split them into logical chunks (by chapter, section, or date range) and analyze each chunk separately, then merge results.
- Scanned PDFs: Claude can read scanned/image-based PDFs reasonably well because it processes page images directly, but quality depends on scan resolution. Low-DPI scans of handwritten or faded text will produce worse results than clean digital PDFs.
- Multiple documents in one request: you can include several
documentblocks in a single message to ask Claude to compare or cross-reference files — useful for things like reconciling a purchase order against an invoice. - File size: there's a hard size limit on request payloads. For large PDFs, check whether you actually need every page — pre-filtering to relevant sections before sending often improves both cost and accuracy.
Building this into a real product
A prompt that works in a notebook isn't the same as a reliable pipeline. In practice you'll want to add:
- Retry logic for transient errors
- Validation of the JSON Claude returns before it hits your database
- Per-document cost tracking, since PDF analysis uses more tokens than plain text chat
- Rate limiting across users if multiple people in your team are uploading documents concurrently
If you're exposing this as a feature inside your own product — letting customers upload PDFs and get analysis back through your app — you need an API layer with proper key management, usage visibility, and team controls rather than sharing a single raw API key across your codebase. SubToAPI turns your existing Claude access into a standard HTTPS API with scoped sub_live_... keys per application, streaming support, and usage metadata per key, so you can track exactly how much document analysis traffic each client or feature is generating. Check the quickstart to see the request format, or the tool use docs if you're building structured extraction with function calling. Plans start at the pricing page with a free trial at signup.
Quick checklist before you ship
- Send the
documentblock before thetextblock in your message content - Use tool calling instead of free-text JSON for anything you parse programmatically
- Chunk documents over ~100 pages
- Validate extracted output before storing it
- Track token usage per document type — PDFs cost more than plain text prompts
questions
Can Claude read PDFs without converting them to text first? Yes. Send the PDF as a base64-encoded document content block in the Messages API request. Claude processes both the extracted text and the page layout directly, so you don't need OCR or a separate text-extraction library for most documents.
What's the best way to get consistent, structured data out of a PDF? Use tool/function calling with a defined JSON schema rather than asking for JSON in plain text. The schema constrains the response format, which is far more reliable for automated pipelines than parsing free-form model output.
Is there a file size or page limit for PDF analysis? Yes, there's a request payload size limit, and very long documents consume significant tokens for page rendering. For documents beyond roughly 100 pages, split them into smaller chunks, analyze each separately, and merge the results in your application code.