← Blog

Claude API PDF Document Analysis Example

2026-09-27 · 5 min read · SubToAPI Team

What you're trying to do

If you searched for this, you probably want to send a PDF — an invoice, a contract, a research paper, a scanned form — to Claude and get back a summary, extracted fields, or answers to questions about its content. The short answer: yes, the Claude API supports native PDF input through the Messages API, and it doesn't require you to pre-process the file with a separate OCR tool. You send the PDF as a base64-encoded document block alongside your text prompt, and Claude reads both the text and the visual layout (tables, charts, scanned pages) in one pass.

This article shows a working request/response example, how to structure prompts for reliable extraction, and the practical limits you'll hit (file size, page count, token cost) before you build this into production.

How PDF input works in the Claude API

PDF support is exposed as a document content block inside a message, similar to how image blocks work. You pass the file as base64 data with a media_type of application/pdf, and Claude processes each page — both the extracted text and the rendered image of the page — so it can read tables, handwriting, stamps, and layout that plain text extraction would miss.

A minimal request body looks like this:

{
  "model": "claude-sonnet-4",
  "max_tokens": 1024,
  "messages": [
    {
      "role": "user",
      "content": [
        {
          "type": "document",
          "source": {
            "type": "base64",
            "media_type": "application/pdf",
            "data": "<BASE64_PDF_DATA>"
          }
        },
        {
          "type": "text",
          "text": "Summarize this contract in 5 bullet points and list any dates or deadlines mentioned."
        }
      ]
    }
  ]
}

The order matters: put the document block before the text instruction so Claude has the content in context before it reads what you're asking it to do.

Full curl example

BASE64_PDF=$(base64 -w 0 invoice.pdf)

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",
    "max_tokens": 1024,
    "messages": [
      {
        "role": "user",
        "content": [
          {
            "type": "document",
            "source": {
              "type": "base64",
              "media_type": "application/pdf",
              "data": "'"$BASE64_PDF"'"
            }
          },
          {
            "type": "text",
            "text": "Extract the invoice number, total amount, and due date as JSON."
          }
        ]
      }
    ]
  }'

On macOS, drop the -w 0 flag from base64 since it isn't supported — pipe through tr -d '\n' instead.

JavaScript example

import fs from "fs";

const pdfBuffer = fs.readFileSync("./contract.pdf");
const base64Pdf = pdfBuffer.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",
    max_tokens: 1024,
    messages: [
      {
        role: "user",
        content: [
          {
            type: "document",
            source: {
              type: "base64",
              media_type: "application/pdf",
              data: base64Pdf
            }
          },
          {
            type: "text",
            text: "What are the three main risks mentioned in this document?"
          }
        ]
      }
    ]
  })
});

const data = await response.json();
console.log(data.content[0].text);

Getting structured output from documents

For document analysis pipelines — invoices, resumes, forms — you usually want structured data back, not prose. Two approaches work well:

For multi-page contracts or long reports, chunking rarely helps — pass the whole PDF in one request when possible so Claude retains context across sections (a clause on page 12 referencing a definition on page 2, for example).

Limits and practical considerations

Simplifying this with SubToAPI

If you're already paying for Claude access and want to expose this document analysis capability as a stable HTTPS API for your own app or team, SubToAPI turns your Claude subscription into application API keys (sub_live_...) that speak the same Messages format shown above — including document content blocks, streaming, and tool use. You get per-key usage metadata so you can see exactly which integration is burning tokens on large PDFs, without managing raw provider credentials across your team. Start with /docs/quickstart, or check the full request format at /docs/messages. Plans start at €9/month with a free trial at /signup.

questions

Does the Claude API support PDF input natively, or do I need OCR first? Native support — you send the PDF as a base64-encoded document block in the Messages API and Claude reads both text and page layout. No separate OCR step is required for typical use cases.

Can I extract structured JSON from a PDF instead of a text summary? Yes. Either ask for JSON explicitly in your prompt with a defined schema, or use tool calling (see /docs/tools) for more reliable structured output that's easier to parse downstream.

Why does analyzing a PDF cost more tokens than a text file with the same word count? Claude processes each PDF page as an image in addition to extracted text, so cost scales with page count as well as content length — a sparse 40-page PDF can cost more than a dense text document of similar length.

Turn your Claude access into an HTTPS API

SubToAPI gives you application API keys, streaming, tool use and usage insights on top of your existing Claude access — set up in minutes.

Start free  Read the quickstart →