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.

pip install taxql

Quickstart

from taxql import TaxQL

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

response = client.lookup(
    state="tx",
    address="6515 Cottonwood Creek Dr",
    city="Frisco",
    zip="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)
client.lookup(state="tx", address="6515 Cottonwood Creek Dr",
              city="Frisco", zip="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.0.tar.gz (15.8 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.0-py3-none-any.whl (13.1 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: taxql-0.1.0.tar.gz
  • Upload date:
  • Size: 15.8 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.0.tar.gz
Algorithm Hash digest
SHA256 7597d2d8c50cb469c9aa250178bb14fe6d73d0cfaab7c1e7c7185912c14da12a
MD5 9dc66f70914ef0dcd7e30aabee6689f1
BLAKE2b-256 c3c2279036633a45d2d578622b6a2dae85b156de742ce0d5b24c5796668c4ce2

See more details on using hashes here.

File details

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

File metadata

  • Download URL: taxql-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 13.1 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.0-py3-none-any.whl
Algorithm Hash digest
SHA256 833fea4c71adf43d2a8db0cb95c444b5d9a2214ae5259a45e2ec7f303f197413
MD5 bb24f9dfab4a482476aca2dc75e58fd3
BLAKE2b-256 566b272ce857e43ec34c0f49375430c5fb0c2528f74d9b94c2cc9891e97e85f0

See more details on using hashes here.

Release history Release notifications | RSS feed

0.2.1

2 files

0.2.0

2 files

0.1.1

2 files

This release

0.1.0 This release

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