Skip to main content

TaxQL Python SDK

Official Python client for the TaxQL API. Address-accurate U.S. sales tax lookups across all 50 states + DC, plus Canada (GST/HST/QST for all 13 provinces and territories, in beta).

pip install taxql

Quickstart

from taxql import TaxQL

client = TaxQL(api_key="your-key")

response = client.lookup(
    state="tx",
    address="6515 Cottonwood Creek Dr, Frisco, TX 75034",
)

print(f"Rate: {response.combined_rate * 100:.4f}%")
print(f"State: {response.rate.state}  County: {response.rate.county}")
print(f"Confidence: {response.confidence}")

Output:

Rate: 8.2500%
State: TX  County: COLLIN
Confidence: exact

Why TaxQL

  • Address-accurate, not ZIP-approximated. We resolve to the jurisdiction at the address (including SPDs / ESDs / MUDs), not the city in the address line.
  • Best rate by default. Each lookup returns a single applicable rate plus a confidence signal and any advisory warnings.
  • Historical queries. Pass as_of=date(2026, 6, 1) to retrieve the rate in effect on that date.
  • Half the price of incumbent providers.

Lookup modes

Provide exactly one input mode per call:

# Address (highest precision) — pass a full single-line address
client.lookup(state="tx", address="6515 Cottonwood Creek Dr, Frisco, TX 75034")

# ZIP only (single best rate; lower confidence on straddling ZIPs)
client.lookup(state="wa", zip="98039")

# Location name (DoR-published place name)
client.lookup(state="wa", location="Seattle")

# Lat/lng
client.lookup(state="ca", lat=34.05, lng=-118.25)

Response shape

lookup() returns a TaxResponse with a rate block and a meta block. Convenience properties cover the common path:

r = client.lookup(state="tx", address="...", city="...", zip="...")

r.combined_rate      # float accessor — total applicable rate, e.g. 0.0825
r.combined_rate_string  # "0.08250" — the lossless wire string
r.confidence         # "exact" | "high" | "medium" | "low" | "none"
r.warnings           # list of advisories (see below)

r.rate.state         # "TX"
r.rate.county        # "COLLIN"
r.rate.state_rate    # "0.06250" — wire STRING (since 0.2.0)
r.rate.state_rate_as_float()   # 0.0625 — explicit accessor
r.rate.combined_district_rate  # "0.02000" — sum of special-district rates

Since 0.2.0, rate fields are preserved as the API's fixed 5-decimal strings — never coerced to float, so binary floating point never touches tax arithmetic. Convert explicitly with rate_to_float(...), the Rate.*_as_float() accessors, or r.combined_rate (a float accessor); for exact money math read the string and use decimal.Decimal. Matches the PHP and Node SDKs. For the verbose per-jurisdiction body (every candidate row, component breakdown, resolved-place detail), request mode=full on the raw HTTP API.

Warnings

response.warnings is a list whose items are either plain strings or structured objects carrying a code:

for w in response.warnings:
    if isinstance(w, dict) and w.get("code") == "place_input_mismatch":
        print(f"input '{w['customer_input_place']}' resolved to "
              f"'{w['resolved_place_name']}'")
    elif isinstance(w, str):
        print(w)

Async client

import asyncio
from taxql import AsyncTaxQL

async def main():
    async with AsyncTaxQL(api_key="your-key") as client:
        # Three concurrent lookups
        responses = await asyncio.gather(
            client.lookup(state="tx", zip="75034"),
            client.lookup(state="wa", zip="98039"),
            client.lookup(state="ca", zip="94022"),
        )
        for r in responses:
            print(r.rate.state, f"{r.combined_rate * 100:.4f}%")

asyncio.run(main())

Historical queries

Pass as_of to retrieve the rate in effect on a specific date:

from datetime import date

# Q2 2026 (current as of 2026-06-01)
q2 = client.lookup(state="wa", zip="98039", as_of=date(2026, 6, 1))

# Q3 2026 (loaded ahead of effective date)
q3 = client.lookup(state="wa", zip="98039", as_of=date(2026, 7, 15))

Supported on states with effective-period history (TX, CA, FL, WA, NY, and all SST states). Single-snapshot states (NM, MO, IL, CO, LA, AL, AZ, AK) return the current rate regardless of as_of.

Error handling

from taxql import (
    TaxQL, AuthError, PaymentRequiredError, ForbiddenError,
    RateLimitError, NotFoundError, ValidationError, ServiceError, TaxQLError,
)

try:
    client.lookup(state="tx", zip="75034")
except AuthError:
    print("Invalid API key — sign up at https://taxql.com/signup")
except PaymentRequiredError:
    print("Billing/subscription issue — check the dashboard")   # 402
except ForbiddenError:
    print("Your plan doesn't include this")                     # 403
except RateLimitError as e:
    print(f"Rate limited; retry after {e.retry_after}s")
except NotFoundError:
    print("Address could not be resolved")
except ValidationError as e:
    print(f"Bad input: {e}")
except ServiceError:
    print("Upstream temporarily down")
except TaxQLError as e:
    print(f"Other API error [{e.error_code}] (ref {e.support_reference})")

The SDK automatically retries 429 (rate limit) and 5xx (service error) responses up to max_retries=3 times with exponential backoff (and honors the Retry-After header on 429). 4xx errors other than 429 raise immediately — no point retrying a malformed request. Every exception carries status_code and the parsed response_body (e.g. exc.response_body["error"]["code"]).

Configuration

TaxQL(
    api_key="your-key",
    base_url="https://api.taxql.com",   # default
    timeout=10.0,                        # seconds
    max_retries=3,                       # 429 + 5xx + timeout
    http_client=None,                    # inject your own httpx.Client
)

If you inject your own httpx.Client, the SDK won't close it on __exit__ — manage its lifecycle yourself. Useful for sharing a connection pool across multiple SDK instances or for custom transport (e.g., adding proxies or TLS cert pinning).

Forward compatibility

The response models use extra="allow" so newer API versions adding fields don't break older clients. You can always access the raw response dict via response.model_dump().

Examples

Runnable examples live in examples/:

  • examples/basic.py — sync address lookup, single state
  • examples/async_lookup.py — async, multiple states in parallel
  • examples/historical.py — as_of comparison across quarters

Documentation

Full API docs at https://docs.taxql.com. OpenAPI spec at https://api.taxql.com/openapi.json.

License

Commercial — see https://taxql.com/terms.

Support

support@taxql.com — or open an issue at https://github.com/taxql/taxql-python/issues.

Metadata

Release files for taxql 0.2.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for taxql 0.2.1
File Size Uploaded Provenance
taxql-0.2.1.tar.gz 19.0 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for taxql 0.2.1
File Interpreter ABI Platform
taxql-0.2.1-py3-none-any.whl Python 3 none any Details

Total release size: 34.9 kB

Release files / taxql-0.2.1.tar.gz

Download URL taxql-0.2.1.tar.gz
Size 19.0 kB
Tags Source
SHA-256 checksum
How to use checksums
cb8fa41d29ec4d4a73c5c31020f772b355c042c01740dd0de6d3fec4f050e299
BLAKE2b-256 checksum
How to use checksums
b44a5c14a2669df22c9eedde8181acaa361853774d0c139379aa3d1175d5550c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.6

Release files / taxql-0.2.1-py3-none-any.whl

Download URL taxql-0.2.1-py3-none-any.whl
Size 15.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
000547fe501f66945b836b9b33c19c69e6cf6aa0b4ef704879d596a282d8c1d9
BLAKE2b-256 checksum
How to use checksums
0a6afcd5b5749861d57eb25573bd33743082b7374ee934cc1d73ca523456fb25
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.6

Security for taxql 0.2.1

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.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page