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 — total applicable rate, e.g. 0.0825
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.0625
r.rate.combined_district_rate  # sum of special-district rates

Rate fields arrive from the API as fixed 5-decimal strings and are parsed to floats for you. 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.pyas_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.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

taxql-0.1.1.tar.gz (16.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

taxql-0.1.1-py3-none-any.whl (13.4 kB view details)

Uploaded Python 3

File details

Details for the file taxql-0.1.1.tar.gz.

File metadata

  • Download URL: taxql-0.1.1.tar.gz
  • Upload date:
  • Size: 16.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.14.6

File hashes

Hashes for taxql-0.1.1.tar.gz
Algorithm Hash digest
SHA256 f9f43650ec7bc9845756066143be5b1a52acd4cc2ff733089308b02e34b5c3b0
MD5 217e446f77bb7df23b570343cf4430e5
BLAKE2b-256 e1c042cb97242a2ae322569657aa4bbae51df02442d95c0100e3fc39ec72c310

See more details on using hashes here.

File details

Details for the file taxql-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: taxql-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 13.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.14.6

File hashes

Hashes for taxql-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 3b4003167d9218a9fe9a5f29db7773d21e0d7f1ebea6f03d3a839d89e73b340d
MD5 0735654e1737803ba3ad0ed37bb50210
BLAKE2b-256 924eb5143e029d8a4d93a0cf4b3e5206a458f856345b905b0384a3ae59d8138e

See more details on using hashes here.

Release history Release notifications | RSS feed

0.2.1

2 files

0.2.0

2 files

This release

0.1.1 This release

2 files

0.1.0

2 files

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