opensalestax-python
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-onlyLineItem(amount, category="general")— pre-tax amount asDecimalJurisdictionBreakdown(name, type, rate_pct, tax)— one taxing authorityCalculatedLine(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
ejosterberg/opensalestax-odoo— Odoo Community 16.0/17.0/18.0 connector- (ERPNext connector planned)
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.
Release files for opensalestax 0.3.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| opensalestax-0.3.1.tar.gz | 29.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| opensalestax-0.3.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 46.7 kB
Release files / opensalestax-0.3.1.tar.gz
| Download URL | opensalestax-0.3.1.tar.gz |
|---|---|
| Size | 29.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
a20291e52052ed8b3da436f0535ecf9054c43c2f70f5ca7ce4647774bd0c29b7
|
|
BLAKE2b-256 checksum How to use checksums |
9070c56147e3791ba1ce4665cfd1d0f59da5c1f0b3873abb1784a3dcad90590f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on May 19, 2026.
Transparency logRelease files / opensalestax-0.3.1-py3-none-any.whl
| Download URL | opensalestax-0.3.1-py3-none-any.whl |
|---|---|
| Size | 16.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
0868c2af2fa78af982ebae54b78d7f942fe52442f7d5bffca63dfada78af0c3d
|
|
BLAKE2b-256 checksum How to use checksums |
4ac081146c69bc3276edac59796d446f3dccf44abc5bcf03e49e8e50a57bd295
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on May 19, 2026.
Transparency log