API v3 is available. This SDK targets the v2 API, which keeps working through its deprecation window. A new SDK major targeting v3 is in progress — see the v3 SDKs page and the v3 API reference.
httpx. It mirrors the TypeScript SDK and exposes a fluent builder over the same typed-graph workflow definition.
Installation
Requires Python 3.13 (the SDK pins to>=3.13,<3.14 today; this is expected to loosen before the official launch).
anyformat; the import path uses the dotted namespace anyformat.sdk (transport) and anyformat.workflow (schema factories).
Authentication
Pass the API key toClient, or set ANYFORMAT_API_KEY in the environment and read it from there.
Basic usage
Full flow: build a workflow with a fluent builder, run a document, await the typed result.Result exposes typed scalar accessors via result.fields[name] for linear workflows (one untagged extraction) and the full envelope at result.raw for everything else (parse markdown, multiple extractions per split, classifications, splits).
Async usage
AsyncClient is the async sibling — every builder, handle, and result method has an async counterpart.
Builder methods
All node types in the typed graph are exposed as fluent methods.parse is required; the others are optional and can be chained in any topology the API allows.
| Method | What it adds | Notes |
|---|---|---|
.parse(*, mode='standard', prompt_hint=None, figure_enhancement=False, cache=True) | The required parse node | All args keyword-only. Knobs are mode-discriminated (per-mode @overloads): mode='agentic' adds effort='low'|'mid'|'accurate'; mode='lite' adds ocr_effort='medium'|'high', region='eu'|'global', skip_routing_review, figures, text_formatting, routing_effort. |
.classify(*categories) | A classify node | Categories from ClassifyCategory(id=..., name=..., description=...) |
.split(*rules, route_from=None) | A splitter node | Use route_from= after .classify() to wire which branch fans out |
.extract(fields, *, branch=None, mode='standard', lookup_suggestion=None, lookup_reasoning_effort=None, lookup_file_uploads=None, use_images=False) | An extract node | branch= required after .classify() / .split(); Schema.* build fields. mode selects the tier ('max' staff-gated). lookup_reasoning_effort ('minimal'|'low'|'medium'|'high') + inline lookup_file_uploads + a field flagged lookup=True drive smart lookup. use_images feeds page images. |
.validate(*rules, branch=None) | A validate node | Attaches to the most recent extract (or the one named by branch) |
.create() | Persists the workflow | Returns a Workflow handle you can .run(...) on |
.build() | Same shape without the network call | Returns a WorkflowDefinition — useful for tests and inspection |
Workflow.run(file=None, *, text=None) accepts either file: bytes | pathlib.Path | str (a file path) or text= (raw text — useful for emails / plain-text bodies). Exactly one must be set. Returns a Run handle; Run.wait(timeout=300, poll_interval=3) returns the Result.
Smart lookup
A smart-lookup field is resolved by matching the document against a reference file (a CSV/catalog) instead of being read off the page. Flag the field withlookup=True, and pass the reference file to .extract(..., lookup_files=[...]) — the SDK reads each path and uploads it with the workflow. Here the model reads vendor_name, then resolves the canonical vendor_id from a vendor catalog:
lookup=True is accepted on every Schema.* factory. The looked-up field comes back in result.fields alongside the others — there’s no separate section for it. An extract with a lookup field but no lookup_files is rejected with 400. By default the lookup overwrites the field; add lookup_only_if_missing=True to extract it from the document too and let the lookup fill it only where extraction found no value.
Managing workflows
The client lists and deletes the workflows on your account:| Method | What it does | Returns |
|---|---|---|
client.list_workflows(page=1, page_size=20, status=None) | One page of your workflows | list[Workflow] — each is .run(...)-able (page_size max 100) |
client.delete_workflow(workflow_id) | Soft-deletes a workflow | The deleted workflow_id (a 404 raises NotFound) |
AsyncClient: await client.list_workflows(...) returns list[AsyncWorkflow] and await client.delete_workflow(id) returns the id.
Reading results
Error handling
The SDK raises typed exceptions you can catch:APIError exposes .status_code, .error_code, and .detail. The wire-format error codes (e.g. EXTRACTION_FAILED, RATE_LIMITED) are documented at Errors.
Webhooks (not on the SDK surface yet)
Client doesn’t expose webhook endpoints today. Until it does, register and delete webhooks with httpx directly:
Links
- PyPI —
pip install anyformat - TypeScript SDK — the same fluent shape in TS
- Coding assistant — let Claude drive anyformat from your editor
