API Reference

Endpoints, request parameters, and response fields for the Net Amount tax APIs.

Endpoints

MethodPathPurpose
GET/healthReturn API health and loaded data metadata.
GET/lookupLook up sales tax by address or coordinates from query parameters.
POST/lookupLook up sales tax by address or coordinates from a JSON body.
POST/v1/tax/validate-addressValidate a transaction address against the lookup source.
POST/v1/tax/calculateCalculate line-level seller-collected sales tax.
POST/v1/tax/calculate/batchCalculate up to 50 transaction requests.
GET/v1/tax/calculations/{calculation_id}Retrieve a committed calculation record.
POST/v1/tax/calculations/{calculation_id}/replayReplay a stored request against current rules.
GET/v1/tax/codesList tax-code taxonomy and rule coverage counts.
GET/v1/tax/codes/{tax_code}Inspect one tax code and its rules.
GET/v1/tax/coverageReport 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}/sourcingReturn only the sourcing slice for an imported state.
POST/v1/tax/exemptions/validateValidate a stored exemption certificate reference.
POST/v1/transactionsCreate a transaction ledger record.
POST/v1/transactions/{id}/voidAppend a void event.
POST/v1/transactions/{id}/refundAppend a refund event.
POST/v1/transactions/{id}/adjustAppend 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.

FieldTypeNotes
addressstringStreet address or place text to geocode.
latnumberLatitude for coordinate lookup.
lonnumberLongitude for coordinate lookup.
asOfDateYYYY-MM-DDOptional rate-effective date. Defaults to the current date.
geocodingProvidergeocodeearth or geoapifyOptional address geocoder.
providergeocodeearth or geoapifyAlias 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."
  }
}