Python SDK — Changelog
Backwards-compatibility policy: pre-1.0 (0.x) releases may include
breaking changes as the client tracks the API contract. From v0.1.0
onward, changes within a minor version are additive — new fields are
added with extra="allow", new methods are appended, and deprecations
are flagged at least one minor version before removal.
v0.2.1 — 2026-08-14
Section titled “v0.2.1 — 2026-08-14”Additive. Surfaces the two new meta fields the API added on 2026-08-14
(GET /v1/tax, simple mode). Backward-compatible — new optional fields only.
Meta.effective_date— the served rate’s legal-effect date (ISOYYYY-MM-DD), orNone. Populated only where the source Department of Revenue publishes a genuine per-row effective date;Nonefor the ~26 of 64 jurisdictions whose source publishes an edition or no per-rate date.Meta.effective_date_basis— always present:publisher_effective_date|edition_only|no_published_date. Describes the provenance ofeffective_date, not a legal characterization. Modelled as an openstr(not a closed enum), so an unrecognised future value passes through unchanged rather than raising.meta.periodis deprecated in the docstring (it conflated a legal-effect date and a publication-quarter edition and isNonefor most states); its wire value is unchanged. Prefereffective_date+effective_date_basis, orGET /v1/freshnesssource_periodfor the edition.
v0.2.0 — 2026-08-14
Section titled “v0.2.0 — 2026-08-14”Breaking, and deliberate. Aligns the Python client field-for-field and error-type-for-error-type with the PHP and Node SDKs, verified against live responses. Shipped as one break (not a 0.1.2 then a 0.2.0) because adoption is still effectively zero — cheaper to break now than after real users arrive.
- Rate fields are now the API’s wire STRINGS (
rate.combined_rate == "0.08250"), not floats. The API types every rate as a fixed 5-decimal string specifically so binary floating point never touches tax arithmetic; 0.1.x undid that at the boundary. Convert explicitly:rate_to_float(...)(new export), theRate.combined_rate_as_float()/state_rate_as_float()/rate_as_float(key)accessors, orresponse.combined_rate(a float accessor).response.combined_ratestill returns a float; newresponse.combined_rate_stringreturns the string. Absent buckets are"0.00000"(neverNone). - Exception hierarchy aligned. New
BillingError(402),TierError(403),NotAvailableError(501),TransportError(network/DNS/TLS/timeout). Mapping is now driven by the machineerror.codefirst, then HTTP status — so a proxy that rewrites the status can’t defeat it.PaymentRequiredError/ForbiddenErrorremain as aliases ofBillingError/TierError, soexcept PaymentRequiredErrorstill works. - Transport failures now raise
TransportError(was a generic timeoutTaxQLError).
v0.1.1 — 2026-08-13
Section titled “v0.1.1 — 2026-08-13”- Structured-address fix: when a
cityis supplied, the street line is sent onstreet(not the single-lineaddressparser, which rejects a bare street). resolved_viadocstring neutralized (removed a pre-declassification internal name); captured fixtures refreshed.
v0.1.0 — 2026-08-12
Section titled “v0.1.0 — 2026-08-12”First published release (pip install taxql). Aligns the client with
the API’s default best-rate response (mode=simple): a single
applicable rate rather than a candidate-row array. Breaking relative
to the never-published v0.0.1.
TaxResponseis now{ rate: Rate, meta: Meta }.response.combined_ratereturns the single best rate (no exception);response.confidenceandresponse.warningsread frommeta; the per-level breakdown and resolved place/county live onresponse.rate.- Removed:
TaxRow,Components,ResolvedPlace,PlaceComponent,BillingInfo, andAmbiguousLookupError(the multi-row model). For the verbose per-jurisdiction body, requestmode=fullon the raw HTTP API and readresponse.model_dump(). warningsitems may be plain strings or structured objects with acode(e.g.place_input_mismatch).Rate.country_rateadded — the national-level rate field the wire always carries (0for the US).- Errors: typed
PaymentRequiredError(402) andForbiddenError(403); the machineerror_codeandsupport_referenceare surfaced on every exception from the{ "error": {…} }envelope.
v0.0.1 — 2026-06-02
Section titled “v0.0.1 — 2026-06-02”First release. Sync (TaxQL) + async (AsyncTaxQL) clients, typed
response models, error hierarchy, retries with exponential backoff
(honoring Retry-After on 429), forward-compatible models
(extra="allow").
Public API:
TaxQL(api_key, *, base_url, timeout, max_retries, http_client)client.lookup(state, *, address, city, zip, location, lat, lng, as_of, period) -> TaxResponseclient.health() -> HealthResponseAsyncTaxQLmirror of the above- Response models:
TaxResponse,TaxRow,Components,ResolvedPlace,PlaceComponent,BillingInfo,HealthResponse - Error classes:
TaxQLError,AuthError,RateLimitError,NotFoundError,ValidationError,ServiceError,AmbiguousLookupError
Convenience properties on TaxResponse:
.combined_rate— single-row rate; raisesAmbiguousLookupErrorfor multi-row responses.primary_row— single-row access with the same semantics.resolved_place— single-row resolved-place metadata orNone
Known gaps (planned for v0.1.0):
- CLI tool (
taxql lookup tx --address ...) - Custom retry policies (jitter, per-status-code backoff)
- Streaming / batch lookup methods (not exposed by the API yet)
- Telemetry hooks (deferred until customer signal)