Guide
Extract PDF data into Zod and Pydantic models
A PDF-to-JSON integration has two separate jobs: produce an object your application can use, and preserve enough evidence to decide whether to use it. This guide extracts an invoice into a Zod or Pydantic model while retaining field states and source references. A value passing type validation can still be the wrong value from the document.
Use a local invoice PDF you are allowed to process and a Velrim API key. The examples make a real extraction call when you run them; page billing applies. They print only request metadata. In your application, consume the returned object directly rather than logging document contents.
TypeScript: a PDF to a validated Zod object
In a Node project, run npm install @velrim/sdk zod and npm install --save-dev tsx. Set VELRIM_API_KEY in your environment. Save the download as invoice.ts, then run npx tsx invoice.ts invoice.pdf with Node 22 or later.
The required invoice fields are strings, a number and a currency enum. A purchase-order number is allowed to be absent or explicitly null. The SDK returns result.data after parsing it with this Zod model; the fields map remains a separate object. Download invoice.ts.
Python: a PDF to a Pydantic instance
In a Python virtual environment, run python -m pip install velrim and set VELRIM_API_KEY. Save this file as invoice.py, then run python invoice.py invoice.pdf. The typed object is result.parsed; result.data and result.fields remain available.
Optional[str] = None accepts both omission and null. That is convenient for application code, but the parsed attribute alone cannot tell which extraction state occurred. Inspect result.fields before making a decision that depends on absence. Download invoice.py.
Keep present, null and missing separate
Use the RFC 6901 pointer /po_number to look up the field entry. Optional and nullable describe what the model accepts. The extraction state describes what the API returned. Do not turn every absent value into an empty string or zero: that erases information and can make a failed extraction look like a valid amount.
| Field state | Meaning for the caller | Handling in this invoice example |
|---|---|---|
present | A value was returned; inspect confidence and grounding separately. | Pass the typed value to business checks. |
null | The field is explicitly null. | Allow it for po_number; decide whether the workflow needs review. |
missing | The field could not be extracted; inspect reason. | Keep the omission visible and route required information to review. |
| No field entry | There is no metadata at the pointer you requested. | Check the pointer and schema; do not treat absent metadata as a pass. |
Handle validation failure without inventing defaults
A required field may be missing even in an HTTP 200 extraction response. If the returned data cannot satisfy your model, the TypeScript SDK raises SchemaMismatchError; the Python model path can raise Pydantic ValidationError. Catch these at your task boundary and send the document reference to review. An extraction-level HTTP error is a different failure: follow the error reference.
If you need to inspect a partial response before enforcing a strict application model, use the raw JSON Schema path described in the extraction docs, retain the envelope, then validate explicitly. A TypeScript generic on the raw path does not perform runtime validation. Keep defaults and coercions out of the extraction model unless you deliberately want that transformation; Pydantic can coerce compatible inputs by default.
What the types do not establish
An invoice total is still a number when it was copied from the wrong subtotal. A source anchor points to a region; grounding: "verified" means the value was located there, not that it belongs to the requested field. Check currencies, totals, duplicate invoice IDs and the receiving account in your own application.
Next, build an exception-review workflow or use the migration checklist. For library semantics, see Zod optionals and nullables and Pydantic models. Velrim confidence measurements and their document-class limits are at /reliability.