Skip to main content

Python SDK for OpenSalesTax — calculate US sales tax via the v1 HTTP API

Project description

opensalestax-python

PyPI License Python CI

Python SDK for OpenSalesTax — calculate US sales tax via the engine's v1 HTTP API.

A thin, well-typed wrapper around the engine's four endpoints. Used by the OpenSalesTax connectors for Odoo and ERPNext, and suitable for any Python application that needs destination-based US sales-tax calculation.

Install

pip install opensalestax

Requires Python 3.10+.

Quickstart

from decimal import Decimal
from opensalestax import OpenSalesTaxClient, Address, LineItem

with OpenSalesTaxClient(base_url="http://localhost:8080") as client:
    result = client.calculate(
        address=Address(zip5="55401"),  # Minneapolis, MN
        line_items=[
            LineItem(amount=Decimal("100.00"), category="general"),
        ],
    )

    print(f"Subtotal:  ${result.subtotal}")
    print(f"Tax:       ${result.tax_total}")
    for line in result.lines:
        for j in line.jurisdictions:
            print(f"  {j.name} ({j.type}) {j.rate_pct}%: ${j.tax}")

Output (against engine v0.54+):

Subtotal:  $100.00
Tax:       $9.0250
  Minneapolis (city) 0.50000%: $0.5000
  Hennepin County (county) 0.15000%: $0.1500
  Minnesota (state) 6.87500%: $6.8750
  Hennepin County Transit Sales Tax (district) 0.50000%: $0.5000
  Metro Area Transportation Sales Tax (district) 0.75000%: $0.7500
  Metro Area Sales and Use Tax for Housing (district) 0.25000%: $0.2500

API

OpenSalesTaxClient

OpenSalesTaxClient(
    base_url: str,                    # engine URL, e.g. "http://localhost:8080"
    api_key: str | None = None,       # optional Bearer token
    timeout: float = 10.0,            # seconds
    user_agent: str | None = None,    # appended to default UA
    verify: bool = True,              # TLS verification
)

Use as a context manager (recommended) or call client.close() manually.

Methods

Method Endpoint Returns
health() GET /v1/health HealthResponse
states() GET /v1/states list[StateCoverage]
rates(zip5, zip4=None) GET /v1/rates RateStack
calculate(address, line_items) POST /v1/calculate CalculationResult

Models

All models are immutable (frozen=True) Pydantic v2. Money and rates use decimal.Decimal to preserve precision; rates are expressed as percents (e.g. Decimal("6.875") means 6.875%).

  • Address(zip5, zip4=None) — engine v1 is ZIP-only
  • LineItem(amount, category="general") — pre-tax amount as Decimal
  • JurisdictionBreakdown(name, type, rate_pct, tax) — one taxing authority
  • CalculatedLine(amount, category, tax, rate_pct, jurisdictions, note)
  • CalculationResult(subtotal, tax_total, lines, disclaimer)
  • RateStack(input, jurisdictions, combined_rate_pct, disclaimer)
  • HealthResponse(status, version, database_connected)
  • StateCoverage(abbrev, name, has_sales_tax, sst_member, tier, notes)

Errors

Flat hierarchy — connectors typically catch one or two:

from opensalestax import (
    OpenSalesTaxError,            # base class
    OpenSalesTaxNetworkError,     # timeout, connection refused, DNS, TLS
    OpenSalesTaxAPIError,         # non-2xx HTTP response (.status_code, .response_body)
    OpenSalesTaxValidationError,  # response shape didn't match SDK models
    NonUSDError,                  # non-USD or non-US address
)

Recommended pattern in a connector:

try:
    result = client.calculate(address=addr, line_items=items)
except OpenSalesTaxNetworkError:
    fall_back_to_catalog_rate()      # engine unreachable
except OpenSalesTaxAPIError as e:
    if e.status_code >= 500:
        fall_back_to_catalog_rate()  # engine glitch
    else:
        surface_to_merchant(e)       # 4xx — config / data issue

Compatibility

SDK version Engine version Python
0.1.x 0.22+ (recommended 0.54+) 3.10, 3.11, 3.12, 3.13

The engine's v1 HTTP API is the contract. Internal Python modules are not — connectors must use this SDK or call the HTTP API directly.

Calculation only

Tax calculations are provided as-is for convenience. The merchant is solely responsible for tax-collection accuracy and remittance to the appropriate jurisdictions. Verify against your state Department of Revenue before remitting.

This SDK does not:

  • File tax returns or remit collected tax (engine constitution §13)
  • Validate addresses
  • Cache calculations (caller's responsibility — caching policy is platform-specific)
  • Retry transient errors (caller's responsibility)

Connectors that use this SDK

Development

git clone https://github.com/ejosterberg/opensalestax-python.git
cd opensalestax-python
uv venv
uv pip install -e ".[dev]"

# Quality gate
ruff check src tests
ruff format --check src tests
mypy --strict src/opensalestax
pytest --cov=opensalestax --cov-fail-under=90

# Live tests against an actual engine (skipped by default)
RUN_LIVE_TESTS=1 OST_BASE_URL=http://localhost:8080 pytest -m live -v

Contributing

See CONTRIBUTING.md. All commits must carry a DCO sign-off (git commit -s). No AI co-author trailers.

License

Apache 2.0. Apache 2.0 + DCO sign-off + SPDX header on every source file.

Project details


Download files

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

Source Distribution

opensalestax-0.1.1.tar.gz (23.6 kB view details)

Uploaded Source

Built Distribution

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

opensalestax-0.1.1-py3-none-any.whl (13.7 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: opensalestax-0.1.1.tar.gz
  • Upload date:
  • Size: 23.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for opensalestax-0.1.1.tar.gz
Algorithm Hash digest
SHA256 0936f2d2a8e2b9f4d844fab966ad5660fc212c52ea162abae2501ad359af8791
MD5 a8edc8f531542be1705df6439f269990
BLAKE2b-256 0fb60d5579c08c8725afa4ef71c9e7228eeefe2e62ddd53c9bf90472607c9a3b

See more details on using hashes here.

Provenance

The following attestation bundles were made for opensalestax-0.1.1.tar.gz:

Publisher: publish.yml on ejosterberg/opensalestax-python

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

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

File metadata

  • Download URL: opensalestax-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 13.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for opensalestax-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 7e8bc98c319d167ac0546ec610d00c6131e9e666a03368c3202f12553d9fb71d
MD5 a3301bcaf77838d6fdd5210742ee9b8c
BLAKE2b-256 694cdd6965c161b6ddfacddf971a52d9e6d81a71ed3d29c62ca7b0b74de9c6c3

See more details on using hashes here.

Provenance

The following attestation bundles were made for opensalestax-0.1.1-py3-none-any.whl:

Publisher: publish.yml on ejosterberg/opensalestax-python

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page