Extract data from PDF files: A developer guide to structured data from PDFs and scans
Table of contents
Structured output with per-field confidence scores through the Nutrient Data Extraction API.
- Use a PDF parser for clean, born-digital files when text or coordinates are enough.
- Add optical character recognition (OCR) for scans. Add layout analysis when reading order, tables, or form structure matters.
- Use schema-based extraction when your application needs known fields as typed JSON.
- Add vision language model (VLM) processing for degraded scans, page imagery, or cursive and free-form handwriting.
- Treat confidence as an uncalibrated routing signal. Use match labels and bounding boxes to check each important value against its source.
- Choose open source for stable layouts and full control. Choose an API when document variety and maintenance cost exceed the value of custom rules.
Why PDFs resist structured extraction
A PDF describes how a page should look, but it doesn’t guarantee a logical record of headings, paragraphs, rows, columns, and fields.
That mismatch creates several extraction problems:
- Text may be split into positioned fragments and stored outside reading order.
- A table may be a set of words and drawn lines rather than a table object.
- A scanned page may contain pixels without a searchable text layer.
- Multicolumn pages require spatial reasoning before text can be read in sequence.
- Visible form values may live in form fields, overlays, flattened page content, or images.
- Checkboxes and handwriting require recognition beyond plain text parsing.
An agent needs more than a text dump. It often needs typed fields, table cells, page references, and evidence that connects each value to the source.
The first design decision is therefore not “Which model should I use?” It’s “What representation does the next system require?”
PDF extraction techniques: What each layer does
The terms below describe different pipeline stages. They aren’t interchangeable.
PDF parsing
PDF parsing reads encoded text and page geometry from a born-digital PDF. It can return characters, text blocks, fonts, and coordinates without reading page pixels.
Use parsing for searchable PDFs when the application needs raw text, Markdown, or position data. Parsing alone can’t recover text from an image-only scan.
Optical character recognition
Optical character recognition converts image pixels into machine-readable characters. It creates a text layer for scanned PDFs, photographs, and other raster documents.
OCR answers “What characters are visible?” It doesn’t inherently identify an invoice total, reconstruct every table, or map values into your business schema.
Layout analysis
Layout analysis groups text and visual regions into ordered document elements. Those elements can include paragraphs, tables, pictures, formulas, and key-value regions.
Use it when reading order and spatial relationships matter. It sits between character recognition and field-level extraction.
Document classification
Document classification assigns a document type, such as invoice, contract, or claim form. A pipeline can use that label to select a schema, mode, or review policy.
Classification is a technique and pipeline stage rather than a single product feature. See Nutrient’s SDK-side document classification guide for implementation details.
Schema-based extraction
Schema-based extraction maps document content into a caller-defined JSON object. The schema names fields, types, required values, nested objects, and arrays.
Use it when downstream code expects stable keys such as invoice_number, due_date, and line_items across varying layouts.
VLM-assisted extraction
VLM-assisted extraction uses a vision language model to interpret page imagery and visual context. It can help with degraded scans, image descriptions, and connected handwriting.
Use it selectively. Visual interpretation adds processing depth and cost, and high-stakes output still needs source checks or human review.
These layers often form one pipeline: parse or OCR, analyze layout, classify if needed, extract to a schema, validate, and route exceptions.
DIY and open source PDF extraction
An open source stack works well when layouts are stable, output needs are narrow, and your team can maintain the rules.
Common building blocks include:
- pypdf — Reads PDF files, iterates through pages, and extracts encoded text.
- PDFMiner — Exposes text positions, font details, and lower-level layout information.
- PyMuPDF — Extracts text and images, and renders PDF pages.
- Tabula and Camelot — Reconstruct tables from text-based PDFs. Camelot supports bordered and spacing-based table strategies.
- pdfplumber — Exposes text boxes and lines for custom table logic.
- Tesseract — Adds OCR for scans before parsing or field rules run.
This pypdf example checks common input failures and returns one string for downstream processing:
import os
import pypdf
def extract_text_pypdf(pdf_path): """Extract text from a PDF and report common input failures.""" if not os.path.exists(pdf_path): return "Error: File not found"
try: with open(pdf_path, "rb") as file: reader = pypdf.PdfReader(file)
if reader.is_encrypted: return "Error: PDF is password-protected"
if len(reader.pages) == 0: return "Error: PDF contains no pages"
text_content = [] for page_number, page in enumerate(reader.pages, start=1): try: page_text = page.extract_text() or "" if page_text.strip(): text_content.append( f"--- Page {page_number} ---\n{page_text}" ) except Exception as page_error: text_content.append( f"--- Page {page_number} ---\n" f"Error extracting text: {page_error}" )
return "\n\n".join(text_content) or "No readable text found in PDF" except PermissionError: return "Error: Permission denied accessing file" except Exception as error: return f"Error processing PDF: {error}"
pdf_path = input("Enter PDF path: ").strip()print(extract_text_pypdf(pdf_path))Use this approach when the same rules continue to work across a representative sample. Add tests for missing text, changed labels, split tables, and unexpected page order.
Move beyond a DIY stack when new layouts keep creating exceptions. The same applies when you need typed JSON, per-field source locations, multilingual routing, or a review queue.
These tutorials cover the individual building blocks:
- Extract text from a PDF using Python
- Use Tesseract OCR in Python
- Tesseract Python guide
- Extract text from a PDF with PyMuPDF
Hosted document AI APIs
Hosted document services remove infrastructure and model maintenance from the application team. AWS Textract, Google Document AI, and Azure AI Document Intelligence return JSON with structures such as tables and key-value pairs.
These services are generally priced per page. Compare them on your documents, required fields, output evidence, latency, retention rules, and total processing cost.
Nutrient Data Extraction API exposes two endpoints, and choosing between them is the first decision in any integration:
POST https://api.nutrient.io/extraction/parsereturns Markdown, spatial elements, or both. It reads a whole document.POST https://api.nutrient.io/extraction/extractreturns data shaped by an inline JSON Schema. It answers for known fields.
How to extract data from a PDF using an API
The API flow has three steps, and they’re the same whether the file is born-digital or scanned.
1. Authenticate. Send Authorization: Bearer <API key> on every request. Keep the key on a backend service.
2. Submit the document. Post the file as multipart file plus an instructions JSON string, or post JSON with a remote url. Supported uploads include PDF, DOCX, DOC, XLSX, XLS, PPTX, PPT, RTF, PNG, JPEG, TIFF, BMP, GIF, and WEBP, and the parse endpoint also accepts a raw PDF, PNG, JPEG, or TIFF body. Two decisions belong in instructions:
- Which endpoint. Use
/extraction/parsewhen you want the whole document as Markdown or spatial elements. Use/extraction/extractwhen you already know the fields you need. - Which mode. A born-digital PDF already contains encoded text, so
text(parse only) orstructureis usually enough. A scan contains pixels, so it needs an OCR-capable mode —structure,understand, oragentic. There’s no separate OCR flag to set; the mode carries it.
3. Read the JSON. Parse responses return output.elements (spatial) or Markdown. Extract responses return output.data in your schema’s shape, with per-field evidence in output.metadata. Both report credit usage under usage.data_extraction_credits.
You don’t need to detect the scan yourself before submitting. A single OCR-capable mode handles a mixed batch of native and scanned PDFs, and the recognition and match signals in the response tell you which pages came back weak. The rest of this guide covers the two decisions in step two — mode depth first, then schemas.
Choosing PDF processing depth
Nutrient’s parse endpoint has four modes. Each mode trades cost for deeper document interpretation.
| Mode | Cost per page | OCR | Layout depth | Output | Use it for |
|---|---|---|---|---|---|
text | 1 credit | No | Text extraction | Markdown only | Born-digital PDFs for search, retrieval-augmented generation (RAG), or migration |
structure | 1.5 credits | Yes | Basic segmentation | Spatial or Markdown | Scans with straightforward layouts |
understand | 9 credits | Yes | AI-augmented layout analysis | Spatial or Markdown | Tables, forms, formulas, printed-style handwriting, and complex layouts |
agentic | 18 credits | Yes | AI plus VLM analysis | Spatial or Markdown | Degraded scans, image descriptions, and cursive or free-form handwriting |
understand is the API default. Set the mode explicitly so cost and behavior remain visible in code review.
The following request parses a PDF in understand mode and returns spatial elements:
curl -X POST https://api.nutrient.io/extraction/parse \ -H "Authorization: Bearer your_api_key_goes_here" \ -F "file=@document.pdf" \ -F 'instructions={"mode":"understand","output":{"format":"spatial"}}'import json
import requests
with open("document.pdf", "rb") as document: response = requests.post( "https://api.nutrient.io/extraction/parse", headers={ "Authorization": "Bearer your_api_key_goes_here", }, files={"file": document}, data={ "instructions": json.dumps( { "mode": "understand", "output": {"format": "spatial"}, } ) }, timeout=120, )
response.raise_for_status()result = response.json()
for element in result["output"]["elements"]: print(element["type"], element.get("text", ""))Spatial and Markdown output can be requested together with formats. Spatial output can also include words, HTML tables, semantic block formatting, headers, footers, or words found inside pictures.
The relevant output defaults are:
includeWords: falseuseHtmlTables: trueenableSemanticBlockFormatting: trueincludeHeadersAndFooters: falseextractWordsFromPictures: false
Choose a mode with this sequence:
- Use
textfor born-digital documents when Markdown is enough. - Use
structurewhen a scan needs OCR and its layout is straightforward. - Use
understandfor complex layouts, tables, forms, formulas, or printed-style handwriting. - Use
agenticwhen tested samples need VLM help with imagery, degradation, or connected handwriting. - Use the extract endpoint instead when the application already knows its target fields.
Mixed pipelines can route each document family to the least expensive mode that passes a labeled evaluation set.
Extracting known fields into structured JSON
The extract endpoint accepts an object-root JSON Schema and returns output.data in that shape. Use it when field names are known before processing.
This request extracts three invoice fields:
curl -X POST https://api.nutrient.io/extraction/extract \ -H "Authorization: Bearer your_api_key_goes_here" \ -F "file=@invoice.pdf" \ -F 'instructions={"schema":{"type":"object","properties":{"invoice_number":{"type":"string","description":"Invoice identifier"},"total_amount":{"type":"number","description":"Final total after discounts and tax"},"due_date":{"type":"string","format":"date","description":"Payment due date"}},"required":["invoice_number","total_amount"]},"parseConfig":{"mode":"understand"},"options":{"includeCitations":true,"strict":false,"multimodal":false}}'import json
import requests
schema = { "type": "object", "properties": { "invoice_number": { "type": "string", "description": "Invoice identifier", }, "total_amount": { "type": "number", "description": "Final total after discounts and tax", }, "due_date": { "type": "string", "format": "date", "description": "Payment due date", }, }, "required": ["invoice_number", "total_amount"],}
instructions = { "schema": schema, "parseConfig": {"mode": "understand"}, "options": { "includeCitations": True, "strict": False, "multimodal": False, },}
with open("invoice.pdf", "rb") as document: response = requests.post( "https://api.nutrient.io/extraction/extract", headers={ "Authorization": "Bearer your_api_key_goes_here", }, files={"file": document}, data={"instructions": json.dumps(instructions)}, timeout=120, )
response.raise_for_status()print(json.dumps(response.json()["output"]["data"], indent=2))The extract endpoint accepts text, structure, understand, or agentic in parseConfig.mode, and it defaults to understand.
The supported schema vocabulary is intentionally small.
- Types:
string,number,integer,boolean,array, andobject. - Object keywords:
propertiesandrequired. - Array keyword:
items. - Guidance:
descriptionon any supported type. - String constraints:
enumandformat: "date".
Keep the schema inline. The API doesn’t support $ref, definitions, allOf, oneOf, numeric ranges, other string formats, conditionals, or additionalProperties.
The options object accepts includeCitations, strict, and multimodal. Their defaults are true, false, and false, respectively. The request object’s optional free-text instructions field can contain up to 10,000 characters.
This trimmed response came from a verified extract request. output.metadata mirrors the data shape and adds citation details at scalar leaves:
{ "output": { "data": { "invoice_number": "INV-2026-0001", "total_amount": 1547.5, "due_date": "2026-09-01" }, "metadata": { "invoice_number": { "bbox": { "x": 452, "y": 258, "width": 219, "height": 24 }, "match": "id_match", "confidence": 0.95, "confidenceComponents": { "groundingScore": 0.95, "source": "no-logprobs" }, "pageIndex": 0, "pageNumber": 1, "source_bboxes": [ { "bbox": { "x": 199, "y": 164.6, "width": 473.9, "height": 231.4 }, "block_id": "b0", "pageIndex": 0, "pageNumber": 1 } ] } }, "pages": [ { "page": 1, "width": 1200, "height": 1697 } ] }}Responses also report usage.data_extraction_credits.cost and usage.data_extraction_credits.remainingCredits. The Runs API can retrieve stored runs when run storage is enabled.
Trusting extracted data
Structured JSON proves that a response matches a shape. It doesn’t prove that each value came from the correct place.
With citations enabled, each scalar metadata leaf can include a page, bounding box, match label, confidence signal, and source block references.
source_blocks lists source block IDs. source_bboxes adds each block’s bounding box, block ID, page index, and page number.
The match field explains how a value was grounded:
| Match label | Meaning |
|---|---|
id_match | One source block matched exactly. |
id_match_multiblock | Source text matched across multiple blocks. |
id_match_partial | Only some cited source blocks were resolved. |
fuzzy_match | The value approximately matched source text. |
not_found | The value couldn’t be grounded to a source location. |
The confidence value ranges from zero to one, but it’s relative and uncalibrated; it isn’t a probability or percentage.
An absent confidence value means that no score was available. It doesn’t mean low confidence.
confidenceComponents can include probabilityScore, marginScore, groundingScore, and formatScore. Its source value is logprobs+margin, logprobs-only, or no-logprobs.
recognitionScore reports OCR legibility as the minimum recognition score across matched source blocks. It’s omitted for born-digital text, not_found, and VLM-only extractions.
Bounding boxes use a top-left origin. Use render-space pixels when the page includes width and height; otherwise, use PDF points.
This function flags top-level fields with weak grounding or a low available score:
def fields_needing_review(metadata, threshold=0.7): """Return top-level fields whose citations suggest manual review.""" flagged = []
for field, citation in metadata.items(): if not isinstance(citation, dict): continue
match = citation.get("match") confidence = citation.get("confidence")
if match in ("fuzzy_match", "not_found"): flagged.append(field) elif confidence is not None and confidence < threshold: flagged.append(field)
return flagged
output = response.json()["output"]print(fields_needing_review(output.get("metadata", {})))Treat 0.7 as an example, not a universal cutoff. Select thresholds against a labeled sample from each document family and field risk class.
Read the citations and confidence guide for nested traversal and coordinate details. The confidence score article explains why source evidence matters alongside a score.
Extracting tables, forms, checkboxes, handwriting, and multilingual content
Spatial parse output uses typed elements. The union includes paragraph, formula, picture, table, keyValueRegion, and handwriting.
Common fields include id, type, bounds, confidence, readingOrder, and page metadata. Bounds contain x, y, width, and height.
Page metadata contains pageIndex, pageNumber, width, and height. Formula elements add latex. Picture elements add classification, classificationConfidence, and altDescription.
Tables
A table element contains rowCount, columnCount, and cells. Each cell can include its row, column, row span, column span, and text.
Use spatial table output when the application needs reconstructed cells. Use schema arrays when the application needs known rows mapped into business objects.
Forms and key-value pairs
A keyValueRegion contains pairs of key and value entities plus relationshipConfidence.
This element isn’t guaranteed for every label-and-value layout. A simple form can instead return paragraphs or a table.
If fields are known, request them through the extract endpoint with a schema. Don’t make application correctness depend on every document producing keyValueRegion elements.
Checkboxes
Checkbox state is a paragraph role, not a separate spatial element type. The relevant roles are CheckboxSelected and CheckboxUnselected.
Other paragraph roles include Title, SectionHeader, Header, Footer, Caption, Footnote, ListItem, PageNumber, and Code.
Handwriting
Use understand for clearly separated, printed-style handwriting. Use agentic for cursive, connected, or free-form handwriting.
There’s no handwriting or intelligent character recognition request toggle. Handwriting appears as an output element when detected.
Even agentic output can be confidently wrong. Route high-stakes handwritten fields to review and show the cited page region.
Multilingual documents
The API recognizes more than 100 languages, but it doesn’t detect them. It defaults to English (eng), so a German or Japanese scan submitted without a language hint is read by an English model — the quiet kind of wrong text described earlier.
Set options.language to a language name or an ISO 639-2 code ("german" or "deu"), and pass an array when a document mixes them. The option applies only to the OCR modes; text mode reads encoded characters and never looks at pixels, so the setting has no effect there.
Refer to the supported languages guide for the current list. Recognizing a language isn’t translating it.
Document data extraction in production
A pipeline that passes a curated test set will still encounter documents it wasn’t designed for. Production traffic is where data extraction from PDF files stops being a parsing problem and becomes a routing problem.
These are the failure modes that show up first.
Poor scan quality
Faxed, photocopied, and phone-camera pages arrive skewed, low-contrast, or compressed to the point where characters blur together. OCR still returns text — it just returns the wrong text, without complaining.
recognitionScore is the signal here. It reports OCR legibility as the minimum recognition score across matched source blocks, so a single unreadable region pulls the field’s score down rather than being averaged away. It’s omitted for born-digital text, so its absence isn’t a failure.
agentic is the mode intended for degraded scans, because its VLM stage interprets page imagery rather than relying on the character layer alone. Whether it recovers a given document family is a question for your samples, not an assumption to build routing on.
Tables that span pages
Spatial elements carry a single pageIndex and pageNumber, so a table that continues past a page break arrives as separate elements tied to separate pages. Relating them is application work. The documents make it harder: Column headers may repeat on each page, appear only on the first, or be restated in a slightly different form.
Whether continuation rows land in a single schema array is a property of the documents rather than a guarantee, so confirm it on multipage samples before relying on it.
Whichever route you take, subtotal and carry-forward rows are the ones that quietly double a total. Use the description on each array item to exclude them, and reconcile the extracted rows against a document total.
Annotations and overlays
A value added as a PDF annotation, a stamp, or a flattened overlay can sit outside the original page content. A field then holds the printed value while the operative one is written beside it — an amended figure, a countersigned date, a struck-through line item.
Where both can occur, extract both and let a business rule decide which wins. Signatures, initials, ticked boxes, and margin notes are the least predictable content on a page and the most likely to carry the meaning that matters.
Multicolumn and mixed layouts
Two-column contracts, sidebars, footnotes, and pages that switch column count mid-document all break naive reading order. Text that’s correct character by character can still be sequenced into nonsense.
Use readingOrder on spatial elements rather than sorting by y-coordinate, and use understand or deeper for any document family where columns vary. text mode does no layout analysis at all.
Fallback logic
The routing rule that survives contact with production is: run the cheapest mode that passes, measure the result, and escalate only the documents that fail.
This function starts at the mode you expect to work, checks the result with the review rules defined earlier, and escalates only when fields come back ungrounded. The ladder begins at structure rather than text because text runs no OCR — it has nothing to read on anything scanned:
import json
import requests
MODE_LADDER = ["structure", "understand", "agentic"]
def extract_with_fallback(pdf_path, schema, start_mode="structure"): """Extract fields, escalating modes only while fields fail their checks.""" ladder = MODE_LADDER[MODE_LADDER.index(start_mode) :] last_output = None flagged = []
for mode in ladder: instructions = { "schema": schema, "parseConfig": {"mode": mode}, "options": {"includeCitations": True}, }
with open(pdf_path, "rb") as document: response = requests.post( "https://api.nutrient.io/extraction/extract", headers={ "Authorization": "Bearer your_api_key_goes_here", }, files={"file": document}, data={"instructions": json.dumps(instructions)}, timeout=120, )
response.raise_for_status() last_output = response.json()["output"] flagged = fields_needing_review(last_output.get("metadata", {}))
if not flagged: return {"mode": mode, "output": last_output, "review": []}
return {"mode": ladder[-1], "output": last_output, "review": flagged}Three things make this safe to run at volume. The ladder is bounded, so a document that fails everywhere ends in a review queue instead of a retry loop. The escalation is per document, not per batch, so one bad scan doesn’t move a whole day’s traffic to 18 credits a page. And the final return still carries its output and its flagged fields, so a reviewer sees the best available answer next to the reason it’s uncertain.
Log the mode each document settled on. When a document family starts escalating more often than it used to, that’s a change in the incoming documents, and it’s visible weeks before anyone notices wrong values downstream.
Human review as a pipeline stage
Human review is exception handling, not a fallback added after launch. Define its routing rules before extracted values can trigger payments, decisions, or record updates.
A practical review item contains:
- The field name, extracted value, and expected type.
- The page image with the cited bounding box highlighted.
- The match label, available confidence signals, and recognition score.
- The document family and business rule that triggered review.
- A correction action that records the reviewed value.
Route fuzzy_match, not_found, missing required evidence, and sample-calibrated low scores to review. Always review fields whose failure cost exceeds the automation benefit.
The extraction-to-action article covers review in a wider automation pipeline. The React PDF viewer tutorial shows how to build a document viewing surface for custom workflows.
Cloud API vs. self-hosted processing
Use a cloud extraction API when managed processing fits your data policy and operating model. It keeps the integration focused on requests, outputs, and review logic.
Use Nutrient Document Engine as the starting point for self-hosted document processing. Use the Nutrient OCR SDKs for in-app viewing and OCR on supported web and mobile surfaces.
These are different product paths. Verify the required extraction features and deployment constraints before assuming API parity.
Security and data lifecycle
Keep the bearer API key on a trusted backend. Don’t embed it in browser code, mobile bundles, logs, or source control.
Send only documents that the caller is authorized to process. Apply your own access control before the extraction request and before returning stored results.
Retention behavior depends on the plan and whether data retention is enabled, so confirm the policy for your account before production use.
The service rejects unencrypted HTTP requests and uses HTTPS with TLS for API communication. Review the Data Extraction API security guide and Nutrient privacy policy for current handling terms.
Avoid logging document bodies or extracted sensitive values by default. Log request identifiers, processing decisions, review outcomes, and credit usage according to your own retention policy.
Decision framework for PDF data extraction
Start with a representative labeled set. Include clean PDFs, scans, layout variants, long tables, checked forms, handwriting, and every language you expect.
Published numbers narrow the shortlist but don’t replace that set. The Data Extraction API benchmarks show how the modes score on public document collections; your own documents decide which mode you ship.
Then answer these questions:
- What output does the next system need? Choose Markdown for retrieval, spatial elements for layout-aware consumers, or schema-shaped JSON for known fields.
- Does the file contain encoded text? Use parsing for born-digital files and OCR for scans.
- How complex is the layout? Use structure for basic segmentation, understand for complex structure, and agentic for tested VLM-dependent cases.
- Are the fields known? Use the extract endpoint with a schema instead of reconstructing them from parse elements.
- How will you verify important values? Require citations, route weak matches, and tune thresholds on labeled examples.
- Can the document leave your environment? Choose a cloud or self-hosted product path based on policy and required features.
- Who owns exceptions? Define the review queue, correction action, and downstream retry behavior.
Use open source when layouts are stable, rules remain small, and your team benefits from owning the entire stack. Include maintenance, OCR tuning, evaluation, and review tooling in the cost.
Use a hosted API when varied documents make custom rules expensive or when structured output and source grounding would otherwise require several services.
Don’t begin with the deepest mode for every page. Measure field quality and review volume. Then route each document family to the least expensive passing path.
Conclusion
To extract data from a PDF reliably, treat it as a layered engineering problem rather than a single call. Parsing reads encoded content, OCR reads pixels, layout analysis rebuilds structure, and schema extraction produces agent-usable JSON.
In production, the boundary is trust. Ground important values in source blocks, treat confidence as uncalibrated, and send ambiguous or high-impact fields to review.
Start with the Nutrient Data Extraction API and the Data Extraction API guides. Test the four processing modes on your own documents before fixing routing and review thresholds.
FAQ
Send the file to an extraction endpoint with a bearer API key and a JSON Schema describing the fields you need. Then read the typed object from the response. With Nutrient, that’s a POST to /extraction/extract with the schema inline in instructions; the response returns output.data in your schema’s shape and per-field source evidence in output.metadata. The same request works for born-digital PDFs and scans, provided the mode is OCR-capable.
There is no universal best method. Use schema-based extraction when you know the target fields, and select the shallowest processing mode that passes a representative evaluation set. Add source grounding and review rules before automating high-impact actions.
Compare support for born-digital files and scans, schema-shaped JSON, tables, handwriting, languages, source locations, and review signals. Also compare latency, page cost, retention policy, deployment options, and behavior on your own labeled documents.
Enable citations on the extract request. Nutrient returns metadata leaves with fields such as match, confidence, bbox, page references, and source blocks. Treat confidence as relative and uncalibrated, and use match labels as direct grounding outcomes.
Yes. Scans require an OCR-capable mode. Use understand mode for printed-style handwriting and agentic mode for cursive, connected, or free-form handwriting. Then review high-stakes fields.
Use open source when layouts are stable and your team can maintain OCR, parsing, tests, and exception rules. Use an API when document variety, schema mapping, source grounding, or review tooling would create a larger custom system.
PDF parsing reads text and geometry already encoded in a born-digital file. OCR recognizes characters from page pixels. Scanned PDFs need OCR before text or fields can be extracted.
Spatial output can contain tables and key-value regions. Key-value regions depend on the document, while checkbox state appears through selected or unselected paragraph roles. Use schema extraction when known fields must have stable JSON keys.
No. Nutrient’s field confidence is a relative, uncalibrated signal rather than a probability. Check the match label and cited page region, and retain review for high-stakes fields.