Structured extraction is where Claude earns its keep in back-office automation: read an unstructured document, emit the fields your systems need. The architecture has three layers with a hard boundary between them: the model produces schema-guaranteed JSON, deterministic code validates and maps it, and the database write is an idempotent upsert. The model never touches the database. That boundary is what makes the pipeline safe to run unattended and safe to re-run.
Layer 1: Schema-guaranteed extraction
Use structured outputs: pass a JSON schema in output_config and the response is guaranteed to match it. Set additionalProperties: false on objects (the docs call this out explicitly). The Python SDK's client.messages.parse() helper takes a Pydantic model, auto-transforms schema constraints the API doesn't accept, and validates the response client-side — the most robust route for this pattern.
Know the documented schema limits before you design: recursive schemas, external $ref, numeric bounds (minimum/maximum), and string length constraints are rejected with a 400. That means range checks ("amount must be positive") belong in your validation layer, not the schema. Documents go in as PDF or plain-text document blocks — PDF input works on all five platforms — with the document placed before your instructions, per the long-context guidance.
from anthropic import AnthropicVertex
from pydantic import BaseModel
class Invoice(BaseModel):
invoice_number: str
vendor: str
total: float
currency: str
client = AnthropicVertex(project_id="my-project", region="global")
result = client.messages.parse(
model="claude-opus-4-8", max_tokens=1024,
output_format=Invoice,
messages=[{"role": "user", "content": f"<document>{text}</document>\nExtract the invoice fields."}])
invoice = result.parsed_output
Layer 2: Validate, then map
"Parses" is not "correct." Two documented cases still need handling: a refusal returns HTTP 200 with stop_reason: "refusal" and may not match the schema, and stop_reason: "max_tokens" means the JSON may be cut off — retry with a higher limit. Beyond that, your validation layer applies the business rules the schema can't express: totals are non-negative, dates parse, currency codes are on your list, and cross-field checks hold. Recommended practice is to route validation failures to a review queue with the source document attached rather than dropping or "fixing" them silently — extraction errors are your best prompt-improvement dataset. Schema mapping (model fields to table columns, unit normalization, foreign-key lookups) is deterministic code; per the general principle, if code can answer, code answers.
Layer 3: Idempotent upserts
Pipelines re-run: batches get resubmitted, retries fire, backfills repeat. Make the database write an upsert keyed on a natural key (invoice number + vendor, message ID, document hash), so processing the same document twice updates one row instead of inserting two. Store provenance columns alongside the data — source document ID, model ID, prompt version, timestamp, and the request ID from the response headers — so any row can be traced back and re-extracted after a prompt change. This is architecture guidance, not an API feature, but it is what separates a demo from a system.
Running at volume
Extraction jobs are independent, so they batch well. Anthropic's Message Batches API (up to 100,000 requests per batch at 50% of standard prices, results downloadable for 29 days) is available on the Claude API and Claude Platform on AWS — not on Bedrock, Vertex AI, or Foundry. Each batch request carries a custom_id (1–64 characters); make it your natural key and idempotency falls out for free, since results rejoin the pipeline under the same identity. On Bedrock and Vertex AI, use those platforms' own cloud-native batch inference for Claude instead — same pattern, different submission API. Structured outputs themselves are GA on the Claude API, Claude Platform on AWS, Bedrock, and Vertex AI, and beta on Foundry, so the extraction layer is portable even where the batch layer isn't.
Where to go next
For the summarize-rather-than-extract variant, see the document summarization pipeline; for label-only outputs, the classification service pattern. The feature matrix shows per-platform support.