API reference
One call gives the sales tax rate at a US street address, split into the state, county, city and district taxes that make it up. Every answer comes from the state's own published data; when that can't place an address in one rate, the API refuses with the reason instead of guessing.
| Base URL | https://api.opentaxrate.com |
| Auth | Authorization: Bearer <key> or x-api-key: <key>. Create keys in the dashboard; lookups are paid from your credit (pricing), 120 a minute per key. |
| Without a key | Free, 10 lookups a minute and 300 a day per IP address. Addresses are placed only by the geocoders we host (Pelias and Photon, built from open address data). When neither can place one, the API answers only if its whole ZIP has one rate, and declines otherwise. |
| With a key | The same data and rules, plus commercial geocoders as further witnesses, so fewer addresses are declined or answered from the ZIP alone. |
| Rates | Decimals: 0.1055 is 10.55%. |
| OpenAPI | /v1/openapi.json, for generating a client or importing into Postman. |
GET /v1/rate
The rate at an address on a given day.
Request
curl "https://api.opentaxrate.com/v1/rate?address=1410+E+Pine+St%2C+Seattle%2C+WA+98122" \
-H "Authorization: Bearer otr_live_..."Parameters
Give the address one of two ways. Both answer the same; with both, the fields win over the line.
GET /v1/rate?address=1410+E+Pine+St,+Seattle,+WA+98122 GET /v1/rate?state=WA&street=1410+E+Pine+St&city=Seattle&zip=98122 | Name | Description |
|---|---|
| As one line | |
address | The whole address: 1410 E Pine St, Seattle, WA 98122. Commas are optional; the state can be a code or its name;
the ZIP is optional. A line that can't be read is a 400 saying what's missing. |
| Or as fields | |
state | Two-letter state code. Required without address. |
street | House number and street, e.g. 1410 E Pine St. |
city | City name. |
zip | Five digits or ZIP+4. Recommended in either form: without it the address is geocoded, and a few more are refused. |
| Either way | |
date | YYYY-MM-DD; today when absent. |
code | What was sold, one or more taxability codes separated by commas: food.catering,food.grocery. Adds taxability, one entry
per code. An unknown code is a 400 listing the valid ones. |
include | trace adds placement evidence; regions adds regional data. Combine as trace,regions. |
Response
{
"state": "WA",
"rate": 0.1055,
"stateRate": 0.065,
"localRate": 0.0405,
"byType": {
"general": 0.1055,
"generalInterstate": 0.1055,
"foodDrug": 0.1055,
"foodDrugInterstate": 0.1055
},
"parts": [
{ "kind": "state", "name": "Washington", "code": "45:53", "rate": 0.065, "ids": {} },
{ "kind": "city", "name": "Seattle", "code": "01:63000", "rate": 0.0405, "ids": {} }
],
"match": "street",
"locationCode": "53-000-63000",
"jurisdictions": "settled",
"alternatives": [],
"locationIds": { "Composite SER Code": "01726", "Location Code": "1726" },
"zip4": "98109-4607",
"source": "Streamlined Sales Tax files WAB2026Q4AUG27 + WAR2026Q4AUG27, street match on 2026-10-22",
"reporting": {
"agency": { "name": "Washington Department of Revenue", "url": "https://dor.wa.gov/..." },
"form": "Combined Excise Tax Return",
"notes": []
}
} | Field | Description |
|---|---|
rate | The combined rate on a general sale. With code, each sale's own rate is in taxability. |
stateRate, localRate | The state's part, and everything else. |
byType | Rates on interstate and food-and-drug sales; null where the state publishes none. |
parts | Each tax adding up to rate: kind (state, county, city, district), name, code (the publisher's type and code, e.g. 01:63000), rate, byType, and ids. |
match | How the address was placed: statewide, street, zip4, zip5, map (geocoded into outlines), zip_area (the whole ZIP has one rate) or authority (the state's own lookup). |
locationCode | Our identifier for the combination of taxes. Not a filing code. |
jurisdictions, alternatives | Whether parts name the jurisdictions the tax is paid to. The rate is settled either way; uncertain where
the address could be in another combination of jurisdictions with the same total, each listed in alternatives as { locationCode, parts }, or the state's own lookup gave one local part for every local tax. |
locationIds, parts[].ids | The state's own codes, as published. See State codes and filing. |
zip4 | The address's ZIP+4 where a Streamlined state's files give it; else null. |
source | The files or service the rate came from, and the date. |
reporting | The agency, return, and any local filing. See Reporting. |
taxability | With code: how each code asked is taxed here, keyed by the code. See Taxability. |
regions | With include=regions. See Regional data. |
Taxability
Name what was sold with code and the answer says whether that sale is taxed at the address on the date, at what
rate, and the government page that says so. The general rate and parts don't change. Rules are
researched for Washington, New York, California and every Canadian province and territory; everywhere else, and any code without
a rule there, answers not_researched. See Taxability codes for the
codes and what each covers.
GET /v1/rate?address=488+14th+St,+Brooklyn,+NY+11215&date=2026-10-08&code=food.catering,food.grocery {
"state": "NY",
"rate": 0.08875,
"parts": [
{ "kind": "state", "name": "New York", "code": "45:36", "rate": 0.04, "ids": {} },
{ "kind": "county", "name": "Kings", "code": "00:047", "rate": 0.04875, "ids": {} }
],
...
"taxability": {
"food.catering": {
"treatment": "taxable",
"rate": 0.08875,
"taxes": [
{
"tax": "NY",
"parts": ["45:36", "00:047"],
"treatment": "taxable",
"rate": 0.08875,
"rule": {
"summary": "A caterer's entire charge for a customer's event is taxable, including food, beverages, staff, rentals, room charges and service charges, even when separately stated.",
"from": "2019-04-08",
"fromIs": "earliest_verified",
"treatment": "taxable",
"conditions": [],
"citations": [
{
"title": "TB-ST-110 (issued April 8, 2019)",
"url": "https://www.tax.ny.gov/pubs_and_bulls/tg_bulletins/st/caterers_and_catering_services.htm"
}
]
}
}
]
},
"food.grocery": {
"treatment": "not_taxed",
"rate": 0,
"taxes": [
{
"tax": "NY",
"parts": ["45:36", "00:047"],
"treatment": "exempt",
"rate": 0,
"rule": { "summary": "Unprepared food for home consumption sold by food stores is exempt from state and local sales tax under Tax Law §1115(a)(1).", ... }
}
]
}
}
} Read each entry in three lines. When rate is null, never charge the general rate instead.
const t = answer.taxability[code];
if (t.rate !== null) charge(amount * t.rate); // taxable, partial, not_taxed
else if (t.treatment === 'conditional') ask(t.taxes); // show each rule's conditions; a person picks
else sendElsewhere(); // not_researched: never use the general rate | Field | Description |
|---|---|
treatment | The sale as a whole: taxable (every part at its rate), partial (some parts, or a reduced rate), not_taxed (no part), conditional (it turns on facts the code doesn't fix) or not_researched (a part has no researched rule for this code on this date). |
rate | The combined rate to charge on this sale; null exactly when conditional or not_researched. |
taxes[].tax | What the rule covers: a state (every part there), CA:GST/HST, a province's HST change (ON/CA:HST), a provincial tax (BC:PST), or * for a nontaxable line. |
taxes[].parts | The codes of the answer's parts this rule decides; together the entries cover every part once. |
taxes[].treatment | The legal treatment: taxable, exempt, zero_rated, conditional, not_a_sale or not_researched. Exempt and zero-rated both mean no tax, but a Canadian return
reports them on different lines. |
taxes[].rate | What these parts add to the sale's rate; null when conditional or not researched. |
taxes[].rule | The rule that decided them; null only when not researched. |
rule.from, rule.to, rule.fromIs | The days the rule answers for; no to means still in force. effective: from is the day
it took effect. earliest_verified: it's likely older, but earlier dates aren't researched and answer not_researched. |
rule.summary, rule.treatment | The rule in one sentence, and what it decides. |
rule.conditions | For a conditional rule, each fact the outcome turns on: { when, treatment, rate? }. For a settled one, exceptions worth knowing. |
rule.citations | The official pages the rule was read from: title, url, and sometimes a quote. |
Errors
{ "error": "This address is within 12 m of a city line ...", "code": "ADDRESS_NEEDS_CONFIRMATION" } | Status | Code | Meaning |
|---|---|---|
| 400 | BAD_REQUEST | A parameter failed validation, or address couldn't be read; the message says what's missing. |
| 401 | INVALID_API_KEY | The key isn't one we issued. |
| 402 | NO_CREDITS | The key's credit doesn't cover the lookup. |
| 422 | ADDRESS_NEEDS_CONFIRMATION | The data can't place the address in one rate; the message says why. |
| 422 | UNSUPPORTED_STATE | No rates for this state yet. |
| 429 | RATE_LIMITED, KEY_LIMITED | Too many lookups; wait the Retry-After seconds. |
| 502 | UPSTREAM_UNAVAILABLE | A state's data isn't loaded, or its service is down. |
| 504 | UPSTREAM_TIMEOUT | A state's service timed out. |
GET /v1/coverage
Each state, how its addresses are placed, the files loaded for it, and its registered regionSources. See Sources for where each state's data comes from, or run the API yourself.
Request
curl "https://api.opentaxrate.com/v1/coverage" \
-H "Authorization: Bearer otr_live_..."