US Sales Tax Rates API
Download OpenAPI (sales-tax.yaml) · Base URL https://api.olympus.new-odyssey.com
Published sales and use tax rates by jurisdiction, looked up by street address or ZIP code, with the breakdown into state, county, city and special district rates, their effective dates and where each value came from. Served by the Olympus Go gateway.
Not a tax calculation. A rate here is the rate a jurisdiction publishes. It is not the tax due on a transaction and not a taxability determination: whether an item is taxable, which location's rate applies (sourcing), whether the seller must collect at all (nexus) and exemptions all change what is owed. Every response repeats this in meta.notices (not_a_taxability_determination). Transaction tax calculation is not offered.
Coverage. Only the Streamlined Sales Tax (SST) member states listed as published by GET /v1/sales-tax/coverage, for the dates listed there. Every other state (for example California, Texas, New York, Florida) is reported as unsupported, with no rate.
Source. The SST rate and boundary files each member state posts at streamlinedsalestax.org (layout: SST Technology Guide, chapter 5). Rates are as the state publishes them; nothing is estimated. Files are checked twice a month; states post changes quarterly, by the first day of the month before the quarter, so rates for the next quarter are often available ahead of time.
Dates. Rates and boundaries carry effective dates. Pass date (YYYY-MM-DD) to look up another day; the default is today in America/New_York. Each state has its own dates_from..dates_to (coverage); dates outside it are unsupported, so a quarter a state has not posted yet is never answered from its older files.
Results. Lookups always answer 200 with a status:
matched: exactly one set of jurisdictions applies and every jurisdiction has one published rate.combined_ratesis set.ambiguous: the address or ZIP maps to more than one set of jurisdictions (common for 5-digit ZIPs). Every candidate is returned with its own rates;combined_ratesis null. No single rate is chosen.unmatched: nothing in the state's files matches, or a jurisdiction's rate is missing for the date (reason: rate_missing).unsupported: the state is not published, or the date is outside the published range.
Matching. match.method says how the result was found and match.confidence how specific it is:
address_range(confidencehigh, ormediumwhen a directional or street suffix had to be ignored): the street, house number and parity fall inside one of the state's address ranges.zip4(medium): the ZIP+4 range record. Used when the address is not in the state's address ranges, or for ZIP+4 lookups.zip5(mediumwhen every record of the ZIP agrees; otherwise the result isambiguous, confidencelow).
Lookups fall back from address to ZIP+4 to 5-digit ZIP, as the SST guide prescribes; match.attempted lists the methods tried and a fallback_used notice says when an address was answered from ZIP records.
Address normalisation. Deterministic, no geocoding: the street line is upper-cased and punctuation removed; the first token is the house number (digits, optionally one letter); a unit designator (APT, UNIT, STE, LOT, #, ...) ends the street; a leading / trailing directional (N, NORTH, SW, ...) and a trailing USPS suffix (STREET -> ST, AVENUE -> AVE, ...) are split off and abbreviated; suffix words inside the name are abbreviated too (HIGHWAY 33 -> HWY 33). PO BOX n is supported. The state files are normalised the same way. data.normalized shows how the input was read.
Rates. Four rates per jurisdiction, as SST publishes them: general and food/drug, each intrastate (sale originating in the state) and interstate (originating outside it). Rates are exact decimal fractions (0.0725 = 7.25%). Food/drug rates are null where a state leaves them blank.
Provenance. Every jurisdiction rate and boundary record carries a provenance block: the SST file it came from (source_url, source_version = file name), that file's date, when this version was published and the validation status of the publication. Responses list the active publication in meta.datasets.
Authenticate with an API key in X-API-Key or Authorization: Bearer. The organization's plan must include sales-tax; requests count against its monthly quota. Every response carries X-Request-Id. Errors share the Error body.
Rates
Rates for a street address
/v1/sales-tax/rates/addressLooks the address up in the state's address ranges, then falls back to the ZIP+4 and 5-digit ZIP records (see Matching). state and street are required, plus zip or city (with zip the city is not used for matching).
Authentication: API key (X-API-Key or Bearer)
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| streetrequired | query | string | Street line, e.g. Example: 100 Main St |
| city | query | string | City, used for matching only when |
| staterequired | query | string | 2-letter state code. Unpublished states answer |
| zip | query | ZipInput | 5-digit ZIP or ZIP+4 ( |
| date | query | string (date) | Day to look up ( Example: 2026-10-20 |
Responses
| 200 | The lookup result (see Results for each status). | LookupResponse |
| 400 | Invalid parameter ( | Error |
| 401 | Missing or invalid API key ( | Error |
| 403 | Organization suspended ( | Error |
| 429 | Per-minute rate limit exceeded ( | Error |
| 503 | The sales tax dataset has no active publication yet ( | Error |
curl -s "https://api.olympus.new-odyssey.com/v1/sales-tax/rates/address?street=100%20Main%20St" \
-H "X-API-Key: $OLYMPUS_API_KEY"Rates for a ZIP+4 or 5-digit ZIP
/v1/sales-tax/rates/zip/{zip}With a ZIP+4, the ZIP+4 range record is used when the state publishes one, otherwise the 5-digit ZIP. A 5-digit ZIP that spans several jurisdiction sets returns every candidate as ambiguous, never just one. Without state, every published state is searched (some ZIPs cross state lines); a ZIP found nowhere whose prefix belongs to a state we do not publish is unsupported.
Authentication: API key (X-API-Key or Bearer)
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| ziprequired | path | ZipInput | 5-digit ZIP or ZIP+4 ( |
| state | query | string | Only search this state (2-letter code). |
| date | query | string (date) | Day to look up ( Example: 2026-10-20 |
Responses
| 200 | The lookup result. | LookupResponse |
| 400 | Invalid parameter ( | Error |
| 401 | Missing or invalid API key ( | Error |
| 403 | Organization suspended ( | Error |
| 429 | Per-minute rate limit exceeded ( | Error |
| 503 | The sales tax dataset has no active publication yet ( | Error |
curl -s "https://api.olympus.new-odyssey.com/v1/sales-tax/rates/zip/{zip}" \
-H "X-API-Key: $OLYMPUS_API_KEY"Jurisdictions
Every jurisdiction rate of a state on a date
/v1/sales-tax/states/{state}/jurisdictionsThe state's rate file on the date: the state rate, county, city (municipality, town, village) and special district rates, each with its code, FIPS code where it has one, rates, effective dates and provenance. Rate rows are listed even when no boundary record assigns them. An unpublished state answers 200 with state.status: unsupported and an empty list.
Authentication: API key (X-API-Key or Bearer)
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| staterequired | path | string | 2-letter state code. |
| level | query | Level | Only jurisdictions of this level. |
| date | query | string (date) | Day to look up ( Example: 2026-10-20 |
Responses
| 200 | The state's jurisdictions. | JurisdictionsResponse |
| 400 | Invalid parameter ( | Error |
| 401 | Missing or invalid API key ( | Error |
| 403 | Organization suspended ( | Error |
| 429 | Per-minute rate limit exceeded ( | Error |
| 503 | The sales tax dataset has no active publication yet ( | Error |
curl -s "https://api.olympus.new-odyssey.com/v1/sales-tax/states/{state}/jurisdictions" \
-H "X-API-Key: $OLYMPUS_API_KEY"Coverage
States served, their files and the data vintage
/v1/sales-tax/coverageEvery state and DC with its status: published (with the SST files, their effective quarter and checksum, the date range served and the matching methods its files support) or unsupported (reason sst_member_not_loaded or not_sst_member). Answers 200 even before anything is published; every state is then unsupported.
Authentication: API key (X-API-Key or Bearer)
Responses
| 200 | Coverage. | CoverageResponse |
| 401 | Missing or invalid API key ( | Error |
| 403 | Organization suspended ( | Error |
| 429 | Per-minute rate limit exceeded ( | Error |
curl -s "https://api.olympus.new-odyssey.com/v1/sales-tax/coverage" \
-H "X-API-Key: $OLYMPUS_API_KEY"Schemas
Error object
The error body shared by every Olympus service.
| Field | Type | Description |
|---|---|---|
| error * | string | Human-readable message. |
| code * | string | Stable machine-readable code. |
| product | string | Product that produced the error, when applicable. |
| details | object |
ZipInput string
StateCode string
Rate number
Exact decimal fraction (0.0725 = 7.25%).
Date string (date)
Level enum: state | county | city | special
city covers municipalities, towns and villages (the place code of the SST boundary file).
statecountycityspecial
Status enum: matched | ambiguous | unmatched | unsupported
matchedambiguousunmatchedunsupported
Reason enum: state_not_supported | date_out_of_range | multiple_jurisdiction_sets | address_not_found | zip_not_found | rate_missing | address_not_found_zip_used | street_not_parsed_zip_used
Why the status is what it is: state_not_supported, date_out_of_range (unsupported); multiple_jurisdiction_sets (ambiguous); address_not_found, zip_not_found, rate_missing (unmatched); address_not_found_zip_used / street_not_parsed_zip_used (matched from ZIP records instead of an address range). Null for a direct match.
state_not_supporteddate_out_of_rangemultiple_jurisdiction_setsaddress_not_foundzip_not_foundrate_missingaddress_not_found_zip_usedstreet_not_parsed_zip_used
Method enum: address_range | zip4 | zip5
address_rangezip4zip5
Provenance object
Where this value came from and which publication introduced this version of it.
| Field | Type | Description |
|---|---|---|
| dataset_id * | enum: plutus.sst_rates_boundaries | |
| source_id * | enum: sstgb.rate_boundary_files | |
| source_url * | string (uri) | The SST file the value was read from (https://www.streamlinedsalestax.org/ratesandboundry/...). |
| source_version * | string | SST file name without extension, e.g. |
| source_date * | string (date) | null | Date of the file as served by the SST site (Last-Modified). |
| published_at * | string (date-time) | When this version of the record was first published. Unchanged records keep their original time. |
| schema_version * | string | |
| validation_status * | enum: passed | passed_with_warnings | |
| record_id * | string | Stable record key within the dataset, e.g. |
DatasetPublication object
| Field | Type | Description |
|---|---|---|
| dataset_id * | string | |
| publication_id * | integer (int64) | |
| published_at * | string (date-time) | |
| freshness * | enum: fresh | stale |
|
Notice object
| Field | Type | Description |
|---|---|---|
| code * | enum: not_a_taxability_determination | multiple_jurisdictions | fallback_used | coverage_limited | not_published | state_specific | |
| message * | string |
Meta object
| Field | Type | Description |
|---|---|---|
| request_id | string | |
| datasets * | DatasetPublication [] | Active publication of the dataset this response read (empty before the first publication). |
| notices * | Notice [] | Caveats about this response. Always includes |
Rates object
| Field | Type | Description |
|---|---|---|
| general_intrastate * | Rate | General rate on sales originating in the state. |
| general_interstate * | Rate | General rate on sales originating outside the state. |
| food_drug_intrastate * | number | null | Food and drug rate (intrastate); null when the state leaves it blank. |
| food_drug_interstate * | number | null |
Jurisdiction object
| Field | Type | Description |
|---|---|---|
| level * | Level | |
| code * | string | Code in the state's files (state FIPS, county FIPS, place code or state special district code). |
| fips * | string | null | State FIPS followed by the county or place code, as the state publishes it (Census place or county subdivision code). Null for special districts. |
| jurisdiction_type * | string | null | X12 data element 1721 type of taxing authority (45 state, 00 county, 01 city, ...). Null when no rate row resolves it. |
| jurisdiction_type_name * | string | null | |
| rates * | Rates | null | Null when the rate file has no single rate for this jurisdiction on the date. |
| effective_from * | string (date) | null | |
| effective_to * | string (date) | null | Last day the rate applies; null when open-ended. |
| provenance * | Provenance | null |
Boundary object
| Field | Type | Description |
|---|---|---|
| record_type * | enum: A | 4 | Z | SST record type of the representative record (A address range, 4 ZIP+4 range, Z 5-digit ZIP). |
| records * | object | How many records of each type assign this jurisdiction set in the matched area. |
| effective_from * | string (date) | |
| effective_to * | string (date) | null | |
| city_name * | string | null | |
| composite_ser_code * | string | null | Composite reporting code for states that require one (reporting only; not a rate). |
| provenance * | Provenance |
Candidate object
One set of jurisdictions that may apply, with its rates.
| Field | Type | Description |
|---|---|---|
| state_code * | StateCode | |
| complete * | boolean | Every jurisdiction has exactly one rate on the date. |
| combined_rates * | Rates | null | Sum of the jurisdictions' rates; null unless complete. Not the tax due on a transaction. |
| sst_default * | boolean | The state's ZIP+4 (zip4 lookups) or 5-digit ZIP (zip5 lookups) record assigns this set. States assign the lowest combined rate in the area to that record; it is not necessarily the rate at a given address. |
| jurisdictions * | Jurisdiction [] | |
| boundary * | Boundary |
StateRef object
| Field | Type | Description |
|---|---|---|
| code * | StateCode | |
| name * | string | |
| status * | enum: published | unsupported |
LookupResult object
| Field | Type | Description |
|---|---|---|
| status * | Status | |
| reason * | Reason | |
| date * | string (date) | The day looked up. |
| normalized * | object | How the input was read (null fields were absent or did not parse). |
| state * | StateRef | null | The state looked up (for ZIP lookups, the state found or the ZIP prefix's state). |
| match * | object | |
| combined_rates * | Rates | null | Set only when status is matched. Not the tax due on a transaction. |
| candidates * | Candidate [] | One per jurisdiction set found (exactly one when matched, several when ambiguous, none when nothing was found). |
LookupResponse object
| Field | Type | Description |
|---|---|---|
| data * | LookupResult | |
| meta * | Meta |
JurisdictionsResponse object
| Field | Type | Description |
|---|---|---|
| data * | object | |
| meta * | Meta |
FileRef object
| Field | Type | Description |
|---|---|---|
| name * | string | SST file name, e.g. |
| effective_quarter * | string | |
| url * | string (uri) | |
| sha256 * | string |
CoverageState object
| Field | Type | Description |
|---|---|---|
| code * | StateCode | |
| name * | string | |
| fips * | string | |
| status * | enum: published | unsupported | |
| reason * | enum: sst_member_not_loaded | not_sst_member | null | |
| sst_participant * | boolean | The state posts SST rate and boundary files. |
| dates_from * | string (date) | null | |
| dates_to * | string (date) | null | Last date served for this state: the end of its newest file quarter, or of the latest quarter whose SST posting deadline (the first day of the month before the quarter) had passed when the files were loaded, whichever is later. |
| match_methods * | Method [] | Matching methods the state's files support. |
| files * | object | null | |
| rows * | object | null | Rate rows, boundary records effective on the load date by type, and rejected lines. |
| provenance * | Provenance | null |
CoverageResponse object
| Field | Type | Description |
|---|---|---|
| data * | object | |
| meta * | Meta |
* required field