/v1 Platform API. It resolves
credentials, mints idempotency keys, retries transient failures, and parses
every response into a Pydantic model.
Quick start
Key features
Typed end to end
Pydantic models for every request and response. Unknown fields and unknown enum members load instead of raising, so an additive server change never breaks a client.
Sync and async
NdiClient and AsyncNdiClient expose identical namespaces. The sync client runs on httpx.Client directly rather than wrapping an event loop.Jobs, not polling loops
Slow operations return a
Job. Wait with jobs.wait, follow progress with jobs.events, or finish inline with wait_seconds.Automatic retries
Backoff with full jitter for
429, 5xx, and dropped connections. Job-creating calls carry an idempotency key, so a retry collapses onto the original job instead of billing twice.Typed error hierarchy
Every failure is an
NdiError subclass carrying the status code, error code, request ID, and retryability.Two thin dependencies
httpx and pydantic only. The wire models are hand-written, so the package pulls in nothing from the NDI backend.Installation
ndi-sdk on PyPI.
To upgrade, see Update the SDKs, CLI, and MCP.
Authentication
Set your API key as an environment variable:ValueError rather than letting the first
call fail as a 401. base_url defaults to $NDI_BASE_URL, then
https://ndi-api.nace.ai; set it only to reach another host.
Client options
Core methods
Upload
Stage bytes for a granular operation. The handle is single-use and expires.Upload documentation
Content types, TTL, and when to use a workspace instead.
Parse
Turn a file into markdown, blocks, and optional chunks.Parse documentation
Parse modes, OCR, figures, chunking, and spreadsheet options.
Extract
Fill a JSON Schema from a document. Passjson_schema or a stored schema_id.
Extract documentation
Schema design, field status, and citations.
Split
Cut a multi-document packet into classified segments. Give every class you expect to find; pages that match none are governed byunknown_policy.
Split documentation
Classes, unknown and overlap policy, and split rules.
Classify
Label a whole document, or each page, against classes you define.Classify documentation
Granularity, unknown policy, and per-page labels.
Ground
Pin quoted text to a location in the source document.Ground documentation
Targets, match methods, and location shapes.
Workspaces
Build a durable corpus, then search it. Uploading registers a ledger row; ingestion is a separate, billable call.Workspaces documentation
Workspaces, files, ingestion, tools, and search.
Automatic search and QA
Ask one question withsearch.automatic, or hold a conversation with
search.qa. Both return a Job.
search.fact, search.deep, and search.filtered are deprecated: they warn
and keep working until they are retired. See
Search.
Automatic search
Routes, modes, structured answers, and catalog tables.
QA
Conversations, follow-ups, and clarifying questions.
Journal-entry testing
An audit procedure over an ingested ledger package, not a search preset. Returns aJob whose result is a JetResult. effort is the one knob;
omit it to take the deployment’s own floor.
Journal-entry testing
Effort, file scope, SQL receipts, and figures.
Ledger understanding
Measures the ingested journal package — table roles, integrity, and optional GL parsing — and stores a report on the workspace. Returns aJob whose result is a LedgerUnderstandingResult.
Ledger understanding
Policy, reuse, the report, and in-progress conflicts.
Async client
AsyncNdiClient has the same surface with await. Share one client across
tasks.
Async and concurrency guide
Bounded concurrency,
return_exceptions, and client lifetime.Response types
documents.parse returns a Job, not a ParseResult — the work outlives the
HTTP request, so the result arrives on the job. Once the job succeeds,
job.result is a ParseResult. Each method has one result model, tagged by
result_type:
Job is one model for every kind of work, its result field is typed
as the union of all result models. That is invisible at runtime, but a type
checker will ask you to narrow before reading variant fields:
UnknownResult holding the raw payload, so a newly added job kind is never an
outage.
Pagination
iter_all walks cursor pages for workspaces, files, jobs, and domains; access
labels use iter_access_labels. See Pagination.
Error handling
CatchNdiError for anything the SDK raises, or a specific subclass when you
plan to act on it.
Error handling guide
The full exception hierarchy, retry policy, and request IDs.
Advanced usage
Timeouts and retries
timeout bounds one HTTP request, not a job. Waiting for work is
jobs.wait(...).
Custom HTTP client
Bring your ownhttpx.Client for proxies, mTLS, connection tuning, or a mock
transport in tests. When you pass one, you close it.
Default headers
Streaming job progress
jobs.events yields Server-Sent Events until the job is terminal. Treat it as
a latency convenience and confirm the final state with jobs.get or
jobs.wait.
Legacy API surface
client.legacy exposes the older /api/v1 product API. It is imported on
first use, so a process that never touches it does not pay for its models.
Next steps
1
Get an API key
Create a key and export it as
NDI_API_KEY. See Authentication.2
Run the quickstart
Parse your first document end to end in the Quickstart.
3
Explore the methods
Start with Parse, then Job management.
The same operations also run from the terminal — see NDI CLI.