← Blog

How to Build a Claude-Powered Data Extraction Tool

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

Building a Claude-powered data extraction tool means sending unstructured text — invoices, emails, contracts, resumes, support tickets — to Claude and getting back clean, structured data you can store in a database or feed into another system. The core pieces are: a well-defined output schema, a prompt (or tool definition) that forces Claude to follow it, validation on the response, and a pipeline that handles retries and edge cases.

This is one of the most common production use cases for LLMs because it replaces brittle regex and OCR post-processing with something that actually understands context — Claude can tell a shipping address from a billing address, or figure out that "net 30" means a 30-day payment term, without you writing a parser for every document variant.

Step 1: Define your extraction schema first

Before writing any prompt, decide exactly what fields you need and their types. This is the single biggest factor in extraction quality — vague schemas produce vague output.

{
  "invoice_number": "string",
  "invoice_date": "YYYY-MM-DD",
  "vendor_name": "string",
  "line_items": [
    { "description": "string", "quantity": "number", "unit_price": "number" }
  ],
  "total_amount": "number",
  "currency": "string (ISO 4217)"
}

Be explicit about formats (date format, decimal vs. string for money), nullable fields, and what to do when a field is missing. "Missing" is not an edge case — it's the normal case in real documents, so decide upfront whether Claude should return null, omit the key, or return an empty string.

Step 2: Use tool definitions for structured output

The most reliable way to get structured data out of Claude is tool use (sometimes called function calling). Instead of asking Claude to "return JSON" in plain text and hoping it doesn't add commentary, you define a tool whose input schema matches your extraction schema, and Claude returns a tool call with arguments that already conform to it.

{
  "name": "extract_invoice",
  "description": "Extract structured invoice data from raw document text",
  "input_schema": {
    "type": "object",
    "properties": {
      "invoice_number": { "type": "string" },
      "invoice_date": { "type": "string" },
      "vendor_name": { "type": "string" },
      "line_items": {
        "type": "array",
        "items": {
          "type": "object",
          "properties": {
            "description": { "type": "string" },
            "quantity": { "type": "number" },
            "unit_price": { "type": "number" }
          }
        }
      },
      "total_amount": { "type": "number" }
    },
    "required": ["invoice_number", "total_amount"]
  }
}

This approach is far more consistent than prompting for raw JSON because the schema constrains the model's output shape directly. If you're new to this pattern, the tool use docs walk through the request/response format in detail.

Step 3: Write the extraction prompt

Even with tool use, the surrounding prompt matters. Keep it short and specific:

Avoid asking Claude to "double-check" or "be extra careful" — it doesn't improve accuracy and just adds latency. Precision comes from schema and examples, not instructions about effort.

Step 4: Call the API and validate the response

Here's a minimal extraction call using SubToAPI, which exposes Claude through a standard HTTPS endpoint with an API key you generate in your dashboard:

const res = 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,
    tools: [extractInvoiceTool],
    tool_choice: { type: "tool", name: "extract_invoice" },
    messages: [
      { role: "user", content: `Extract fields from this invoice:\n\n${documentText}` }
    ]
  })
});

const data = await res.json();
const extracted = data.content.find(b => b.type === "tool_use")?.input;

Never trust the output blindly, even with a forced schema. Run it through a validator (Zod, JSON Schema, Pydantic equivalents) before writing to your database. Validation catches cases where a number came back as a string, a date is malformed, or a required field is missing because the source document genuinely didn't have it.

Step 5: Handle real-world document mess

Production extraction tools fail not because of the model but because of the inputs:

Step 6: Scale and monitor usage

Once the extraction pipeline works for one document, the next problem is volume and cost. Running thousands of extraction calls a day means you need:

SubToAPI gives each application its own sub_live_ key with usage metadata per request, so you can isolate the extraction service from other Claude-powered features in your product and see exactly what it costs. Start with the quickstart to get a key, or check pricing if you're estimating cost across a Solo, Team, or Scale plan — all available with a free trial from signup.

Keep schemas versioned

As your extraction tool matures, your schema will change — new fields, renamed fields, new document types. Version your schemas (invoice_v1, invoice_v2) rather than mutating them in place, and keep old prompt/schema pairs around so you can re-run historical documents consistently if you ever need to backfill.

Questions

Can Claude extract data from scanned PDFs directly? Claude processes text and images depending on the model, but for scanned PDFs you typically run OCR first (or convert the PDF to images and use image input) and pass the resulting text to Claude for structuring. Pure text extraction from a raw PDF binary isn't something Claude does without a preprocessing step.

Should I use tool use or plain JSON prompting for extraction? Use tool use. It constrains the output to your schema at the API level, which is more reliable than asking Claude to emit raw JSON in a text response, especially as your schema grows more complex with nested objects and arrays.

How do I handle documents where required fields are missing? Define explicit behavior in your schema and prompt — return null rather than omitting the key or guessing a value. Then validate on your side and route anything with missing required fields to manual review instead of silently accepting incomplete 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 →