API Reference
Endpoints, request parameters, and response fields for the Net Amount tax APIs.
Endpoints
| Method | Path | Purpose |
|---|---|---|
GET | /health | Return API health and loaded data metadata. |
GET | /lookup | Look up sales tax by address or coordinates from query parameters. |
POST | /lookup | Look up sales tax by address or coordinates from a JSON body. |
POST | /v1/tax/validate-address | Validate a transaction address against the lookup source. |
POST | /v1/tax/calculate | Calculate line-level seller-collected sales tax. |
POST | /v1/tax/calculate/batch | Calculate up to 50 transaction requests. |
GET | /v1/tax/calculations/{calculation_id} | Retrieve a committed calculation record. |
POST | /v1/tax/calculations/{calculation_id}/replay | Replay a stored request against current rules. |
GET | /v1/tax/codes | List tax-code taxonomy and rule coverage counts. |
GET | /v1/tax/codes/{tax_code} | Inspect one tax code and its rules. |
GET | /v1/tax/coverage | Report rule coverage by jurisdiction and tax code. |
GET | /v1/tax/states/{state_code} | Return imported state guidance, sourcing, filing, and source metadata. |
GET | /v1/tax/states/{state_code}/sourcing | Return only the sourcing slice for an imported state. |
POST | /v1/tax/exemptions/validate | Validate a stored exemption certificate reference. |
POST | /v1/transactions | Create a transaction ledger record. |
POST | /v1/transactions/{id}/void | Append a void event. |
POST | /v1/transactions/{id}/refund | Append a refund event. |
POST | /v1/transactions/{id}/adjust | Append an adjustment event. |
Authentication
Keyless GET /lookup and POST /lookup are available for the public website/demo path when public lookup is enabled. Requests that include an API key use the paid API-key quota path.
The /v1/tax/* and /v1/transactions* endpoints always require Authorization: Bearer <key> or X-Api-Key when billing is enabled. Local offline development may set BILLING_ENABLED=false.
Lookup requests consume the lookup quota. Versioned tax and transaction requests consume the separate calculate meter and return X-Usage-Meter: calculate with quota headers when billing is enabled.
Lookup inputs
Provide either address or both lat and lon.
| Field | Type | Notes |
|---|---|---|
address | string | Street address or place text to geocode. |
lat | number | Latitude for coordinate lookup. |
lon | number | Longitude for coordinate lookup. |
asOfDate | YYYY-MM-DD | Optional rate-effective date. Defaults to the current date. |
geocodingProvider | geocodeearth or geoapify | Optional address geocoder. |
provider | geocodeearth or geoapify | Alias for geocodingProvider. |
Rate dates
Rate-date lookup lets you ask which rates were active on a specific calendar date. Pass asOfDate with a YYYY-MM-DD value.
When a rate date is provided, Net Amount matches the address or coordinate to jurisdictions, then filters each jurisdiction's rates to rows active on that date. In database mode this uses the imported rate effective period; in file mode it uses the source effDate and endDate values. The normalized date is echoed back as input.asOfDate.
Historical coverage currently starts at 2018-01-01. Requests for dates before 2018 are outside the supported historical dataset and may return incomplete or no historical rate coverage. When no rate date is provided, the API uses the current date.
Historical (past) rate dates are a paid feature on the Scale plan and above. A rate date strictly before today returns 403 historical_lookup_requires_plan for keyless/public requests and for API keys below Scale. Current-date and future-date lookups are available on every plan, including keyless demo traffic.
Rate dates apply to tax rates. The current lookup still uses the available place boundary dataset to determine which jurisdictions contain the point.
GET lookup
curl --get http://localhost:8787/lookup \
--data-urlencode "address=200 E Colfax Ave, Denver, CO 80203" \
--data-urlencode "asOfDate=2026-05-01"curl "http://localhost:8787/lookup?lat=39.7392&lon=-104.9903"curl --get http://localhost:8787/lookup \
--data-urlencode "lat=39.7392" \
--data-urlencode "lon=-104.9903" \
--data-urlencode "asOfDate=2024-07-01"POST lookup
curl http://localhost:8787/lookup \
--request POST \
--header "Content-Type: application/json" \
--data '{
"address": "200 E Colfax Ave, Denver, CO 80203",
"asOfDate": "2026-05-01"
}'Validate address
curl http://localhost:8787/v1/tax/validate-address \
--request POST \
--header "Content-Type: application/json" \
--header "Authorization: Bearer $NET_AMOUNT_API_KEY" \
--data '{
"address": {
"line1": "200 E Colfax Ave",
"city": "Denver",
"state": "CO",
"postal_code": "80203",
"country": "US"
},
"asOfDate": "2026-05-01"
}'The response includes validation_id, status, normalized address data, point, matching jurisdictions, data version, warnings, and rule trace.
Calculate tax
Money fields are integer minor units, so 10000 means $100.00 for USD. Quantities are decimal strings with up to 6 decimals.
curl http://localhost:8787/v1/tax/calculate \
--request POST \
--header "Content-Type: application/json" \
--header "Authorization: Bearer $NET_AMOUNT_API_KEY" \
--header "Idempotency-Key: order_1001_calc" \
--data '{
"transaction": {
"type": "sale",
"document_code": "order_1001",
"transaction_date": "2026-05-01",
"currency": "USD",
"commit": false
},
"seller": {
"registrations": [
{
"state": "CO",
"status": "registered",
"effective_from": "2020-01-01"
}
]
},
"addresses": {
"ship_to": {
"line1": "200 E Colfax Ave",
"city": "Denver",
"state": "CO",
"postal_code": "80203",
"country": "US"
}
},
"amounts": {
"shipping_amount": 0
},
"line_items": [
{
"line_id": "line_1",
"quantity": "1",
"unit_amount": 10000,
"tax_code": "commodity.01"
}
]
}'Calculate resolves taxability from effective-dated rule rows. Imported product-taxability commodity codes such as commodity.01 and commodity.05.110 calculate after running bun run db:import:product-taxability; those imported rules are state-scoped and return a review warning until promoted. MVP seed rows are visible for review but are not production calculation inputs. Known codes without reviewed/data-backed rules return unsupported_jurisdiction_rule by default.
Batch calculate
curl http://localhost:8787/v1/tax/calculate/batch \
--request POST \
--header "Content-Type: application/json" \
--header "Authorization: Bearer $NET_AMOUNT_API_KEY" \
--data '{ "calculations": [/* calculate request objects */], "continue_on_error": true }'Catalog and coverage
Use GET /v1/tax/codes, GET /v1/tax/codes/{tax_code}, and GET /v1/tax/coverage to inspect taxonomy, source/review status, effective dates, and unsupported boundaries before relying on a category.
State guidance
Imported summary/sourcing responses are exposed through GET /v1/tax/states/{state_code}. The response includes normalized sourcing, filing, state rate fields, summary text/html, source links, raw-source metadata, and review warnings. These rows are needs_review guidance until promoted into reviewed rule rows.
curl http://localhost:8787/v1/tax/states/AL \
--header "Authorization: Bearer $NET_AMOUNT_API_KEY"Transactions and replay
Committed calculations can be retrieved with GET /v1/tax/calculations/{calculation_id} and replayed with POST /v1/tax/calculations/{calculation_id}/replay. POST /v1/transactions creates a ledger record from a committed calculation or inline calculate request; void/refund/adjust endpoints append lifecycle events.
Response shape
type LookupResponse = {
input?: {
address?: string
asOfDate?: string
geocodingProvider?: string
}
point: {
lon: number
lat: number
}
geocode?: {
formatted?: string
confidence?: number
matchType?: string
provider?: string
}
totals: Record<string, { rate: number; percent: number }>
jurisdictions: Array<{
placeId: string
name?: string
placeTypeIds?: string
taxTypeIds?: string
rates?: Array<{
taxTypeName?: string
value?: number
}>
}>
timings: Record<string, number | null>
meta: {
source?: string
matchedJurisdictions?: number
boundaryCount?: number
ratePlaceCount?: number
loadedAt?: string
}
}Errors
Validation errors return 400 with an error object. Versioned calculate errors use stable codes such as unsupported_tax_code, unsupported_jurisdiction_rule, jurisdiction_not_found, and idempotency_conflict. Missing routes return 404 and point callers back to the supported endpoints.
{
"error": {
"code": "missing_lookup_input",
"message": "Provide either address or lon/lat."
}
}