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.2.tar.gz (23.9 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.2-py3-none-any.whl (13.7 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: opensalestax-0.1.2.tar.gz
  • Upload date:
  • Size: 23.9 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.2.tar.gz
Algorithm Hash digest
SHA256 57536b52d68100e0d4db2bec8fce3bacbd32d48e090c19b0fcbc88b88ef1241a
MD5 6e50b58c8cbe20f9bee4b2071acdcf82
BLAKE2b-256 92f4089ea1bd4a52cc0e9fdaa198bff74bb6c8232d63755f3f0dd73713e1f13d

See more details on using hashes here.

Provenance

The following attestation bundles were made for opensalestax-0.1.2.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.2-py3-none-any.whl.

File metadata

  • Download URL: opensalestax-0.1.2-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.2-py3-none-any.whl
Algorithm Hash digest
SHA256 9c93d11e7454b38a04cdef3f1caf5c57bca8b326fe6de413a9b97903051a5635
MD5 84f32902dbc12dfc6945c4eb73767c2f
BLAKE2b-256 9f6ad19cb8ec2db76fef146063d28332a493a5f54b0eb314df8c5f9647950fb6

See more details on using hashes here.

Provenance

The following attestation bundles were made for opensalestax-0.1.2-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