← Blog

Claude API Document Extraction: Structured Data Guide

2026-10-23 · 5 min read · SubToAPI Team

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:

  1. Tool use with strict schemas — define the expected output as a tool with a JSON Schema, and force Claude to call it.
  2. Low temperature — extraction tasks benefit from determinism, not creativity.
  3. 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:

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

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.

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 →