nace-sdk (Python)
The official Python client for the Drex API: calibrated answers to typed questions (POST /v1/systemone) and document jobs — parse, split, classify, extract and ground.
The client is built on the TypeSafe AI Python SDK (MIT; see THIRD_PARTY_NOTICES.md): system_one, Noul, Choice, Score, SystemOneResponse with .nouls, .choices and .scores, RetryPolicy and the error classes match typesafe-sdk, so TypeSafeClient code runs on NaceClient after renaming the imports. It depends only on httpx, pydantic and typing-extensions.
pip install nace-sdk
export NACE_API_KEY=nace_sk_...
Python 3.11 or later. Create a key on the API keys page.
Decisions
from nace_sdk import Choice, NaceClient, Noul, Score
with NaceClient() as client: # reads NACE_API_KEY, NACE_BASE_URL, NACE_DEFAULT_DECISION_MODEL
result = client.system_one(
state="I was charged twice for my March invoice.",
questions={
"wants_refund": Noul(instructions="Is the customer asking for a refund?"),
"topic": Choice(instructions="Which topic is it?", criteria={"billing": None, "shipping": None, "other": None}),
"urgency": Score(instructions="How urgent is it?", criteria=["low", "medium", "high"]),
},
)
print(result.nouls["wants_refund"].noul)
print(result.choices["topic"].choice, result.request_id)
AsyncNaceClient has the same methods, awaited.
Documents
from nace_sdk import NaceClient, UrlSource
with NaceClient() as client:
# An https:// URL whose last path segment names the file: its extension picks the parser.
created = client.documents.parse("https://example.com/invoice.pdf", output={"formats": ["markdown"]})
job = client.jobs.wait(created.job_id)
print(job.result["document"]["markdown"])
# A URL whose last segment isn't the file's name needs a file_name.
client.documents.parse(UrlSource(url="https://arxiv.org/pdf/1706.03762", file_name="attention.pdf"))
# on_conflict="new_version" replaces the file at that path instead of failing with path_conflict.
file = client.documents.upload("invoice.pdf", path="invoices/invoice.pdf", on_conflict="new_version")
extracted = client.documents.extract(
file.as_source(),
schema={"type": "object", "properties": {"total": {"type": "number"}}},
wait_seconds=60,
)
# Or save the schema once and name it.
saved = client.extraction_schemas.create("Invoice", {"type": "object", "properties": {"total": {"type": "number"}}})
client.documents.extract(file.as_source(), schema_id=saved.schema_id)
Uploads go straight to the document service with a one-use token, never with your API key; files of 32 MiB and more go up in parts. To run a resumable upload yourself, use create_upload_session, upload_part, complete_upload_session, get_upload_session (which parts landed) and abort_upload_session; create_upload_grant mints a token for another client to upload with. Every document job and upload session create carries an Idempotency-Key, so retries never start a second job or session. The /v1 routes send no Access-Control-Allow-Origin header, so keep Drex calls on your server: a browser uploads with a grant your server mints, and opens job files through jobs.file_link links.
client.jobs has get, list, iter, wait, delete, events, get_request (the request a job ran under), rows (a parsed sheet's rows, a page at a time), file_link (a signed link to a stored file) and download (any job file, including full Markdown that is still being prepared). client.extraction_schemas has list, iter, get, create, list_versions and iter_versions (oldest first), get_version and create_version; creates are not retried unless you pass retry, since a retry could save a second schema.
Errors and retries
Every failure raises a NaceError. HTTP failures are NaceAPIError subclasses (NaceBadRequestError, NaceAuthenticationError, NaceInsufficientCreditError, NacePermissionDeniedError, NaceNotFoundError, NaceConflictError, NaceUnprocessableEntityError, NaceRateLimitError, NaceInternalServerError, NaceOverloadedError) with status, body, request_id, type, code, issues, detail and server_retryable; a success whose body doesn't match is a NaceAPIResponseValidationError, and a failed connection a NaceAPIConnectionError (NaceAPITimeoutError on a timeout). jobs.wait raises NaceJobFailedError or NaceJobTimeoutError. The client retries 408, 429, 5xx, dropped connections and timeouts (2 retries within a 30 s budget, 0.5 s doubling to 5 s, honoring retry-after-ms), except an error the server marks "retryable": false. The budget counts from the first attempt, so with the defaults an attempt that fails after 30 s, such as one that hit the 60 s timeout, is not retried: raise RetryPolicy(timeout=...) above your attempts and waits, or pass None. jobs.download retries opening each file the same way, but not a transfer cut off midway; jobs.events is not retried, and saved-schema creates aren't unless you pass retry.
from nace_sdk import NaceClient, RetryPolicy
# Up to 5 attempts of up to 90 s each, and the waits between them.
client = NaceClient(timeout=90, retry=RetryPolicy(max_retries=4, timeout=500))
The SDK logs to the nace_sdk logger: NACE_LOG_LEVEL=debug sets its level, and a handler (for example logging.basicConfig()) shows the records. At debug it logs headers and bodies, with the API key, upload tokens and PDF passwords redacted.
The nace command line tool is pip install nace-cli; it reads the key nace login saves through nace_sdk.credentials. Full guide: Python SDK.
License
Apache-2.0
Metadata
Release files for nace-sdk 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| nace_sdk-0.1.0.tar.gz | 54.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| nace_sdk-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 120.9 kB
Release files / nace_sdk-0.1.0.tar.gz
| Download URL | nace_sdk-0.1.0.tar.gz |
|---|---|
| Size | 54.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
7165b20acf8b89ca2c1d45dec9daebfb3429a58be1c71739941ff6bf22f099f8
|
|
BLAKE2b-256 checksum How to use checksums |
821e46fe70f0ba96676c7fa4e9c0557638ada825b4ba06fa14b6495807b88db2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 8, 2026.
Transparency logRelease files / nace_sdk-0.1.0-py3-none-any.whl
| Download URL | nace_sdk-0.1.0-py3-none-any.whl |
|---|---|
| Size | 66.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
ae8ea7cbd3de6825c02c39492926532bc3a6719fa7c413c9c7f04b3d027c3d12
|
|
BLAKE2b-256 checksum How to use checksums |
ae4d3461b06f3392c96e3e0df44c9d3da38fee67b407b1757ed38affbb90afff
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 8, 2026.
Transparency log