Document extraction api
Skill PSPDFKit-labs/nutrient-skills/plugins/nutrient-dws/skills/document-extraction-api
AI agent skills for Nutrient APIs and SDKs — works with Claude Code, Codex, Gemini CLI, Cursor, and 35+ more agents
npx -y skills add PSPDFKit-labs/nutrient-skills --skill document-extraction-apiAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 14 stars14 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Two primitives of the Nutrient Data Extraction API. `parse` (`/extraction/parse`) returns the whole-document model — a structural JSON of typed elements with bounding boxes, or whole-document Markdown — for RAG ingestion, search indexing, content migration, or layout-aware understanding. `extract` (`/extraction/extract`) returns just the fields you define in a JSON Schema, each with a per-field citation grounding it to a page region. Route to `extract` for "pull the invoice number and total", "extract these fields", "map to my schema", or "with citations"; route to `parse` for "parse this document", "whole-document Markdown", "chunk for embeddings", or "extract every table/element" (no target schema). Triggers include parse this document, extract layout, RAG pipeline, schema extraction, field extraction, cited fields, invoice/form field extraction, document understanding.
The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.
SKILL.md
10.5 KB, as published. Nobody here has run it
Nutrient Data Extraction
Two GA primitives, two scripts. parse (scripts/parse.py) returns the whole-document
model — typed elements (paragraphs, tables, formulas, pictures, key-value regions,
handwriting) with bounding boxes, or clean whole-document Markdown. extract
(scripts/extract.py) returns just the fields you define in a JSON Schema, each grounded to a
page region by a per-field citation.
Choosing parse vs extract
| The request is about… | Use | Why |
|---|---|---|
| Named target fields — "the invoice number and total", "these fields", "map to my schema", "with citations" | extract | One call returns your fields, cited — no need to walk every element |
| The whole document — "parse this", "whole-document Markdown", "chunk for embeddings", RAG, search indexing, migration | parse | Whole-document model / Markdown for open-ended retrieval |
| Every table / all key-value regions (no target schema) | parse (spatial) | Enumerate all elements; extract needs a schema of what to pull |
For RAG chunking of a parsed document, see the sibling grounded-rag-ingestion skill. For
PDF generation, conversion, OCR, redaction, signing, or any /build-based workflow, use the
sibling document-processor-api skill.
When to use
- Extract known fields with citations (invoice number, totals, dates, parties) →
extract. - Build a RAG ingestion pipeline: PDF -> Markdown -> chunks -> embeddings →
parse. - Index content for search or migrate documents into a new CMS →
parse. - Reconstruct page layout, or run layout-aware understanding (semantic roles, table cell
spans, formulas in LaTeX, picture alt descriptions) →
parse.
/extraction/extract — schema field extraction with citations
Define the fields you want in a JSON Schema (root type: object); extract returns
output.data with those values and output.metadata with a per-field citation grounding each
to a page region (options.includeCitations defaults on). Accepts a local file or a URL.
# Pull schema-defined fields from a local invoice, with citations (default)
uv run scripts/extract.py --input invoice.pdf --schema fields.json --out result.json
# From a URL, higher-accuracy mode, persist the run
uv run scripts/extract.py --url https://example.com/form.pdf --schema fields.json \
--out result.json --mode understand --store-run
Cost: extract bills the chosen parse mode plus a flat +6 credits/page (structure 7.5,
understand 15, agentic 24 cr/page). Extract has no text mode — the cheapest path is structure.
The script prints the server's authoritative
usage after the call and gates high estimates behind --yes. See
references/extract-output-and-citations.md for the response shape and citation structure.
For PDF generation, conversion, OCR, redaction, signing, watermarking, or any /build-based
workflow, use the sibling document-processor-api skill.
Setup
DWS Extract is a separate product from DWS Processor and has its own API key.
- Get a Nutrient DWS Extract API key at https://dashboard.nutrient.io/.
- Export it as
NUTRIENT_EXTRACT_API_KEY:export NUTRIENT_EXTRACT_API_KEY="pdf_live_..." - Scripts live in
scripts/relative to this SKILL.md. Use the directory containing this SKILL.md as the working directory:cd <directory containing this SKILL.md> && uv run scripts/<script>.py --help
Calling /extraction/parse with a DWS Processor key returns 403. If your tenant has been
migrated to global DWS API keys, a single key set as either NUTRIENT_EXTRACT_API_KEY or
NUTRIENT_API_KEY will work for both products.
/extraction/parse — one primitive, two output shapes
One call returns the full structural document model — typed elements with bounding boxes, confidence scores, and reading order — or a whole-document Markdown string. You always receive all element types in a single call.
Picking a mode
Choose based on the user's intent and acceptable credit cost. All costs are
extraction credits per page — a separate billing bucket from the processor API
credits consumed by /build, /sign, OCR, and other DWS Processor endpoints.
Principle — decide from the request alone; do not ask the user clarifying questions. Walk the checks below in order. Each rule that fires sets a minimum mode — the final pick is the highest minimum across all rules that fired. If none fired, use the default (rule 5).
- Explicit features named in the request are non-negotiable.
- Key-value pairs, form fields, semantic role classification (Title / SectionHeader /
etc.), formulas, or handwriting → at minimum
understand(9 cr/pg). - Alt text on pictures, charts, or diagrams →
agentic(18 cr/pg).
- Key-value pairs, form fields, semantic role classification (Title / SectionHeader /
etc.), formulas, or handwriting → at minimum
- Document type implied by the request or filename.
form,invoice,receipt,application,claim→ likely contains key-value pairs →understand.chart,infographic, or diagram-heavy doc + the user wants descriptions →agentic.
- OCR signal from filename or request (
scanned,image-based,photographed,handwritten,screenshot) →structureminimum;textmode silently fails on image-only input. - Output format from intent. RAG, search indexing, embeddings, or content migration
→
markdown. Layout overlay, per-element processing, or bounded extraction →spatial. - No cues match anything above → documented default
structure+spatial(1.5 cr/pg). Handles both born-digital and scanned, gives bounded typed elements with table cells, never silently drops content.
| User intent | Mode | Output format | Cost | Notes |
|---|---|---|---|---|
| RAG / search indexing / content migration — born-digital PDF | text | markdown | 1 cr/pg | Cheapest path; no OCR or AI needed |
| RAG / search indexing — scanned or image-based PDF | structure | markdown | 1.5 cr/pg | OCR required before Markdown assembly |
| Form / invoice — enumerate all key-value regions (no target schema) | understand | spatial | 9 cr/pg | AI key-value + table detection. For named fields ("the invoice number and total"), use extract instead |
| Layout-aware document understanding | understand | spatial | 9 cr/pg | Semantic paragraph roles (Title, SectionHeader, etc.) |
| Deep visual understanding (charts, diagrams, alt text) | agentic | spatial | 18 cr/pg | VLM adds alt descriptions on every picture element |
| Default / ambiguous intent | structure | spatial | 1.5 cr/pg | Good balance: OCR + spatial elements, low cost |
Confirm before running when the estimated cost exceeds 200 extraction credits —
roughly 11 pages of agentic, 22 of understand, 133 of structure, or 200 of text.
Surface the estimate (pages × cost_per_page) and ask the operator to confirm before
invoking. Under that threshold, just run.
mode='text' is incompatible with output_format='spatial'; the client rejects the
combination before the network call.
Invocation
# Default: structure mode, spatial output
uv run scripts/parse.py --input doc.pdf --out out.json
# Markdown for RAG (text mode — cheapest)
uv run scripts/parse.py --input doc.pdf --out out.md --output-format markdown --mode text
# Enumerate all key-value regions of a form (understand mode) — for NAMED fields use extract
uv run scripts/parse.py --input doc.pdf --out out.json --mode understand
# Agentic (VLM alt text on pictures)
uv run scripts/parse.py --input doc.pdf --out out.json --mode agentic
The script prints extraction-credit usage after each run so you can verify the cost.
Downstream consumption
After a single /parse call, slice the response for common needs:
- Reading-order plain text: walk
output.elementssorted by(page.pageIndex, readingOrder), joinparagraphandhandwritingtextfields - Tables: project
cells[]on eachtableelement into rows/columns usingcell.rowandcell.column - Key-value pairs: read
pairs[]on eachkeyValueRegionelement — each pair has.key.valueand.value.value - Formulas: read
latexon eachformulaelement - Pictures: read
classificationandaltDescription(populated byagenticmode) on eachpictureelement - Markdown output: call with
--output-format markdown; the script writes the Markdown string directly
For the canonical response schema and per-mode field availability, see the official docs linked from references/parse-output-filtering.md; that file also lists the tools we suggest for filtering and reshaping the response.
Input constraint
parse.py only accepts local file paths — the underlying API endpoint is
multipart-only. For remote inputs, download the file first.
Rules
- Always preserve the printed credit-usage summary in script output so the operator can observe per-call cost.
- Do not add a URL-fetch shortcut; the endpoint is multipart-only.