Skip to main content

LookupTax SDK for Python

Validate tax IDs — VAT, EIN, GSTIN, ABN and more — against official government registries.

lookuptax.com

pip install lookuptax

Python 3.9+.

Quick start

from lookuptax import LookupTax

with LookupTax(api_key="...") as lookuptax:
    res = lookuptax.validate("IE", "53057102A")

    if res.result.status == "VALID":
        print("Confirmed:", res.entity.name)

Read result.status, not isValid

Every response carries a result whose status is the single source of truth. Five mutually exclusive values:

result.status Meaning
VALID Format passed and a government registry confirmed the entity.
INVALID Definitive negative — bad structure/check digit, or no registry record.
UNVERIFIED No queryable registry for that country, so only the format was checked. A pass is not proof the business exists.
INDETERMINATE A registry was temporarily unreachable. Retry — do not treat as invalid.
UNSUPPORTED We don't validate that country or number type yet.

Do not branch on validation["overall"]["isValid"]: it is true for VALID, UNVERIFIED and INDETERMINATE alike, so a format-only pass looks identical to a registry-confirmed one.

Two helpers encode that correctly:

res.result.is_valid      # True only for VALID — UNVERIFIED is not "valid"
res.result.should_retry  # True only for INDETERMINATE
match res.result.status:
    case "VALID":         accept(res.entity)
    case "INVALID":       reject(res.result.reason)
    case "UNVERIFIED":    accept_with_caveat()   # format checked only
    case "INDETERMINATE": retry_later()          # registry was down
    case "UNSUPPORTED":   skip()

Errors vs outcomes

The SDK mirrors the API's split:

  • Validation outcomes return. An INVALID tax ID is a successful call — you get a response, not an exception. UNSUPPORTED returns too, even though it arrives as HTTP 422, because the body still carries a result.
  • Request errors raise. Authentication, quota, rate limit, malformed request — the call could not be processed at all.
from lookuptax import LookupTaxError, QuotaExceededError, RateLimitError

try:
    res = lookuptax.validate("IE", "53057102A")
except RateLimitError as e:
    time.sleep(e.retry_after or 1)
except QuotaExceededError:
    notify_billing()
except LookupTaxError as e:
    log.error("%s (%s)", e.code, e.status)

Branch on e.code (a stable machine-readable string), never on the message.

Transient failures — 429, 500, 503 — retry automatically (2 attempts by default, honouring Retry-After). Pass max_retries=0 to opt out. Batch submission is never retried automatically, because a retried submission that actually succeeded would create a second batch and reserve quota twice.

EU routing

lookuptax.validate("DE", "DE123456789", validation_source="vies")   # VIES only
lookuptax.validate("DE", "DE123456789", validation_source="local")  # national registry only

With vies or local there is no fallback to the other — if the requested source cannot answer you get INDETERMINATE rather than a silent answer from the other one, which is what makes it usable for reconciliation. Default is auto. Ignored outside VIES-covered countries.

Countries needing a name

lookuptax.validate("MX", "XAXX010101000", additional_params={"name": "Empresa Ejemplo SA de CV"})

Batch

Up to 100 IDs, validated asynchronously. Enterprise plan only.

from lookuptax import TaxIdInput

batch = lookuptax.create_batch([
    TaxIdInput("IE", "53057102A"),
    TaxIdInput("MX", "XAXX010101000", name="Empresa Ejemplo SA de CV"),
])

done = lookuptax.wait_for_batch(batch["batch_id"])

for item in lookuptax.iterate_batch_results(batch["batch_id"]):
    print(item.tin, item.validation_result.result.status if item.validation_result else item.status)

wait_for_batch polls on a relaxed cadence by design. When a registry is unreachable an item keeps retrying for roughly three days, carrying a next_retry_at — a long-running item is not a stuck one.

Configuration

LookupTax(
    api_key="...",                              # required
    base_url="https://api.lookuptax.com/v1",    # default
    timeout=30.0,                               # seconds, per request
    max_retries=2,                              # 429/500/503 only
    client=httpx.Client(...),                   # inject a proxy/transport, or for tests
)

base_url is configurable because the published path prefix is deployment configuration rather than a property of the SDK.

API

Method Endpoint
validate(country_iso, tin, ...) GET /validate
create_batch(tax_ids, ...) POST /batch
get_batch(batch_id, ...) GET /batch/{id}
list_batches(...) GET /batch
cancel_batch(batch_id) POST /batch/{id}/cancel
delete_batch(batch_id) DELETE /batch/{id}
get_tax_id(request_id) GET /batch/tax-id/{id}
wait_for_batch(batch_id, ...) polls to a terminal state
iterate_batch_results(batch_id, ...) generator over every page

License

MIT

Metadata

Release files for lookuptax 1.0.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 lookuptax 1.0.1
File Size Uploaded Provenance
lookuptax-1.0.1.tar.gz 11.2 kB Without provenance Details

Built distribution (wheel)

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

Total release size: 22.8 kB

Release files / lookuptax-1.0.1.tar.gz

Download URL lookuptax-1.0.1.tar.gz
Size 11.2 kB
Tags Source
SHA-256 checksum
How to use checksums
cfabe92bd6e75ec65a7d5c04f70d3b831714fa3294b622aff20832e32a348496
BLAKE2b-256 checksum
How to use checksums
37f96e88a4511b535258f62565a38575999b4e0d22bd7b22aa319038b7ed6922
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.7

Release files / lookuptax-1.0.1-py3-none-any.whl

Download URL lookuptax-1.0.1-py3-none-any.whl
Size 11.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
b2e5f3c3ff2b3e632f944e305aac933b7d51033de4fc5ea6ee4d275d4950a7b5
BLAKE2b-256 checksum
How to use checksums
5e88e0b9c327440f9bb92dd0c6ee0e7c2fe922e7e5018c4e5f973965b63843a5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.7

Security for lookuptax 1.0.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.

Release history Release notifications | RSS feed

This release

1.0.1 This release

2 release files No provenance

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