Claude API Document Extraction: Structured Data Guide
Extracting structured data from documents with the Claude API means sending unstructured text — invoices, contracts, resumes, forms, scanned reports — and getting back clean, predictable JSON instead of free-form prose. Claude is well suited to this because it can read messy layouts, tables, and inconsistent formatting, then map them onto a schema you define, rather than relying on brittle regex or template-based parsers.
The practical answer is: use Claude's tool use (function calling) feature with a JSON schema that describes exactly the fields you want, feed it the document text (or image, via vision), and parse the tool call arguments as your structured output. This approach is far more reliable than asking Claude to "return JSON" in plain prose, because tool schemas constrain the model's output shape instead of hoping it follows instructions.
Why Document Extraction Needs a Different Approach
Plain prompting ("extract the invoice number and total as JSON") works until it doesn't — the model adds explanatory text, uses inconsistent key names, or returns a code block instead of raw JSON. For production pipelines this breaks parsers silently. Three techniques fix this:
- Tool use with strict schemas — define the expected output as a tool with a JSON Schema, and force Claude to call it.
- Low temperature — extraction tasks benefit from determinism, not creativity.
- Explicit field-level instructions — tell Claude what to do when a field is missing (
null, empty string, or omit).
Defining a Schema for Extraction
Here's a tool definition for extracting invoice data:
{
"name": "extract_invoice",
"description": "Extract structured invoice data from document text",
"input_schema": {
"type": "object",
"properties": {
"invoice_number": { "type": "string" },
"invoice_date": { "type": "string", "format": "date" },
"vendor_name": { "type": "string" },
"total_amount": { "type": "number" },
"currency": { "type": "string" },
"line_items": {
"type": "array",
"items": {
"type": "object",
"properties": {
"description": { "type": "string" },
"quantity": { "type": "number" },
"unit_price": { "type": "number" }
},
"required": ["description", "quantity", "unit_price"]
}
}
},
"required": ["invoice_number", "total_amount", "line_items"]
}
}
Claude will populate this schema from the document text and return it as structured tool input, not as a loose JSON string you have to hope is valid.
Extraction Example Against the Claude API
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,
"tools": [{ "name": "extract_invoice", "input_schema": {"...": "..."} }],
"tool_choice": {"type": "tool", "name": "extract_invoice"},
"messages": [
{"role": "user", "content": "Extract data from this invoice text:\n\n[document text]"}
]
}'
The response contains a tool_use content block with input matching your schema exactly. No parsing JSON out of markdown fences, no retry logic for malformed output.
Handling Scanned Documents and PDFs
If your documents are images or scanned PDFs rather than plain text, use Claude's vision capability by sending the document as a base64-encoded image alongside your extraction prompt. Claude reads layout, tables, and handwriting reasonably well, and you can still request the same tool-based structured output on top of the visual input — the extraction schema doesn't change based on whether the source was text or an image.
For multi-page PDFs, extract each page separately and merge results, or convert pages to images and send them as multiple content blocks in a single message if the document is short enough to fit your context window.
Validating and Handling Partial Extractions
Real documents are inconsistent. Build these safeguards into your extraction pipeline:
- Mark optional fields explicitly in the schema and handle
nullvalues downstream rather than assuming every field is present. - Add a
confidenceorextraction_notesfield to the schema so Claude can flag ambiguous cases (e.g., "total amount unclear, two possible values found"). - Validate against the schema on your side too — tool use reduces malformed output but doesn't guarantee business-logic correctness (e.g., a total that doesn't match line item sums).
- Log raw input alongside extracted output so you can audit extraction quality over time.
Running This at Scale Through an API Layer
If you're building a document extraction pipeline that multiple services or team members need to call — an internal tool, a backend job queue, a customer-facing upload feature — routing every call through a shared API key management layer makes operational sense. SubToAPI turns your existing Claude access into an HTTPS API with application-specific keys (sub_live_...), so each extraction service or environment gets its own key, and you can see token usage per key without sharing a single Anthropic credential across your team.
A basic extraction call through SubToAPI looks like this:
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-5",
max_tokens: 1024,
tools: [extractionTool],
tool_choice: { type: "tool", name: "extract_invoice" },
messages: [{ role: "user", content: documentText }]
})
});
This is useful once extraction moves from a prototype script to a production service: you get usage metadata per key, streaming support if you're processing long documents incrementally, and team seats so engineers don't need direct access to your Anthropic account. See the tool use docs and the quickstart for setup details, and check pricing if you're evaluating plans for a team rollout.
A Practical Checklist
- Define a strict JSON Schema tool for every document type you extract (invoices, resumes, contracts need separate schemas).
- Force tool use with
tool_choiceso Claude always returns structured data, never prose. - Use low temperature (0–0.2) for deterministic extraction.
- Add confidence/notes fields to catch ambiguous extractions before they hit downstream systems.
- Validate extracted data against business rules, not just schema shape.
- Route extraction calls through scoped API keys if multiple services or team members need access — see /docs/messages for request/response details.
Questions
Does Claude API support extracting data directly from PDF files? Not as raw PDF upload — convert PDF pages to images or extract text first, then send either as input. Claude's vision capability handles scanned/image-based pages well.
What's the most reliable way to force structured JSON output from Claude? Use tool use (function calling) with a JSON Schema and tool_choice set to force that specific tool, rather than asking for JSON in a plain text prompt.
Can I extract tables and line items, not just single fields? Yes — define nested array fields in your schema (like line_items above) and Claude will populate repeated structures such as invoice rows or table data.