Python SDK for OpenSalesTax — calculate US sales tax via the v1 HTTP API
Project description
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.
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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
57536b52d68100e0d4db2bec8fce3bacbd32d48e090c19b0fcbc88b88ef1241a
|
|
| MD5 |
6e50b58c8cbe20f9bee4b2071acdcf82
|
|
| BLAKE2b-256 |
92f4089ea1bd4a52cc0e9fdaa198bff74bb6c8232d63755f3f0dd73713e1f13d
|
Provenance
The following attestation bundles were made for opensalestax-0.1.2.tar.gz:
Publisher:
publish.yml on ejosterberg/opensalestax-python
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
opensalestax-0.1.2.tar.gz -
Subject digest:
57536b52d68100e0d4db2bec8fce3bacbd32d48e090c19b0fcbc88b88ef1241a - Sigstore transparency entry: 1454111404
- Sigstore integration time:
-
Permalink:
ejosterberg/opensalestax-python@1a62f875c0b85ac36b95e7f69fa78869d81102c4 -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/ejosterberg
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@1a62f875c0b85ac36b95e7f69fa78869d81102c4 -
Trigger Event:
push
-
Statement type:
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9c93d11e7454b38a04cdef3f1caf5c57bca8b326fe6de413a9b97903051a5635
|
|
| MD5 |
84f32902dbc12dfc6945c4eb73767c2f
|
|
| BLAKE2b-256 |
9f6ad19cb8ec2db76fef146063d28332a493a5f54b0eb314df8c5f9647950fb6
|
Provenance
The following attestation bundles were made for opensalestax-0.1.2-py3-none-any.whl:
Publisher:
publish.yml on ejosterberg/opensalestax-python
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
opensalestax-0.1.2-py3-none-any.whl -
Subject digest:
9c93d11e7454b38a04cdef3f1caf5c57bca8b326fe6de413a9b97903051a5635 - Sigstore transparency entry: 1454111515
- Sigstore integration time:
-
Permalink:
ejosterberg/opensalestax-python@1a62f875c0b85ac36b95e7f69fa78869d81102c4 -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/ejosterberg
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@1a62f875c0b85ac36b95e7f69fa78869d81102c4 -
Trigger Event:
push
-
Statement type: