# Sales Tax Rate by ZIP Code: Streamlined Sales Tax States (`plainrecordlabs/sst-sales-tax-rates`) Actor

Sales tax rates by ZIP code or ZIP+4 for the 24 Streamlined Sales Tax member states, from the free SST rate and boundary files: state, county, city and district rates, the combined rate, the food rate and the dates they apply. No CA, TX, NY or FL. Unofficial tool.

- **URL**: https://apify.com/plainrecordlabs/sst-sales-tax-rates.md
- **Developed by:** [Plain Record Labs](https://apify.com/plainrecordlabs) (community)
- **Categories:** E-commerce, Developer tools
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.00 / 1,000 sales tax rates

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Sales Tax Rate by ZIP Code for the Streamlined Sales Tax States

Look up the sales tax rate by ZIP code or ZIP+4 and get the state, county, city and special district rates, the
combined rate, the food rate and the dates they apply. The rates come from the free rate and boundary files the
Streamlined Sales Tax Governing Board posts for its member states, and every row names the file it came from.

**Covered:** the 24 Streamlined Sales Tax member states. Arkansas, Georgia, Indiana, Iowa, Kansas, Kentucky,
Michigan, Minnesota, Nebraska, Nevada, New Jersey, North Carolina, North Dakota, Ohio, Oklahoma, Rhode Island, South
Dakota, Tennessee, Utah, Vermont, Washington, West Virginia, Wisconsin and Wyoming.

**Not covered:** California, Texas, New York, Florida, Illinois, Pennsylvania, Arizona, Colorado, Louisiana and every
other state that is not a member. A ZIP there returns no row and costs nothing.

> This is an unofficial tool, not affiliated with the Streamlined Sales Tax Governing Board or any state. It is a
> lookup, not tax advice. The state files are the source of truth, and the state's own rate lookup is the place to
> confirm a rate before you file. It is not a certified service provider, so using it does not give you the
> liability relief the Streamlined agreement gives sellers who use the state files or certified software.

### What people use it for

- **Invoicing, quoting and checkout tools:** a sales tax rates API you can call with the ship to ZIP instead of
  keeping your own rate table for these states.
- **Spreadsheets and bookkeeping:** paste a column of customer ZIPs and get the combined rate for each one.
- **Rate table updates:** run your ZIP list each quarter, or with next quarter's date once the new files are posted.

### Input

| Field | What it does |
|---|---|
| **ZIP codes** | 5-digit ZIPs like `84003` or ZIP+4s like `84003-1234`. Up to 10,000 per run. |
| **Date** | The day the rate should apply, like `2026-10-01`. Empty means today. |
| **Only these states** | Two letter codes like `UT` or `WA`. Empty means all 24. Handy for a ZIP on a state line. |
| **Max results** | A cap on how many rows one run returns. |

Give the ZIP+4 when you have it. A 5-digit ZIP can cover more than one city or district, and when it does, the
state's file gives the ZIP the lowest combined rate in it. The ZIP+4 record gives the rate for that part of the ZIP.
A ZIP+4 with no record of its own falls back to the 5-digit record, and the row says so.

### Output

One row per ZIP and matching record. Most ZIPs return one row. A ZIP that sits in two states' files returns one row
per state. This one is from a test run on 2026-10-27:

```json
{
  "zip": "98101-1234",
  "zip5": "98101",
  "plus4": "1234",
  "state": "WA",
  "stateName": "Washington",
  "matchLevel": "zip+4",
  "date": "2026-10-27",
  "combinedRate": 0.1055,
  "combinedRatePercent": 10.55,
  "stateRate": 0.065,
  "countyFips": null,
  "countyRate": 0,
  "placeFips": "63000",
  "placeClass": null,
  "cityRate": 0.0405,
  "specialDistrictRate": 0,
  "specialDistricts": [],
  "foodCombinedRate": 0.1055,
  "foodStateRate": 0.065,
  "combinedInterstateRate": 0.1055,
  "compositeSerCode": "01726",
  "effectiveFrom": "2026-01-01",
  "effectiveTo": "2027-03-31",
  "rateFile": "WAR2026Q4AUG27.zip",
  "boundaryFile": "WAB2026Q4AUG27.zip",
  "rateFileUrl": "https://www.streamlinedsalestax.org/ratesandboundry/Rates/WAR2026Q4AUG27.zip",
  "boundaryFileUrl": "https://www.streamlinedsalestax.org/ratesandboundry/Boundary/WAB2026Q4AUG27.zip",
  "notes": []
}
```

Washington files its local rate under the city code, so Seattle's 4.05% sits in `cityRate` here.

Things to know before you rely on it:

- Rates are decimals, so `0.0725` is 7.25%. `combinedRatePercent` gives the same number as a percent.
- `foodCombinedRate` is the food and drug rate from the rate file. A state with no separate food rate repeats its
  general rate there, and that includes states where groceries are not taxed at all, like Washington. Whether food
  is taxable is set by state law, not by this field. Kansas puts its food rates in a separate file this tool does
  not read, so Kansas rows leave the food fields empty.
- Fifteen of these states also publish address level records. The Streamlined rules say to use the address record
  when a state has one. This tool reads only the ZIP+4 and 5-digit ZIP records, so for an exact address use the
  state's own address lookup.
- The files cover the general sales tax rate and the food rate. The Technology Guide says they cannot be used for
  items a state may tax at another rate, like motor fuel, electricity and gas, or vehicles, boats and aircraft.
- `effectiveFrom` and `effectiveTo` are the days every record in the row is in effect. An empty `effectiveTo` means
  the file gives no end date yet.
- A ZIP that is not in any member state's file, or that has no record in effect on your date, returns no row. The
  run's SUMMARY record lists those ZIPs and the reason.

| Field | What it holds |
|---|---|
| `zip`, `zip5`, `plus4` | The ZIP as you gave it, its 5-digit part and the 4-digit extension. |
| `state`, `stateName` | The member state whose file the row came from. |
| `matchLevel` | `zip+4`, `zip5`, or `zip5-fallback` when a ZIP+4 had no record of its own. |
| `date` | The day the rates were looked up for. |
| `combinedRate`, `combinedRatePercent` | The total general rate as a decimal and as a percent. |
| `stateRate`, `countyRate`, `cityRate`, `specialDistrictRate` | The parts of the combined rate, as decimals. |
| `countyFips`, `placeFips`, `placeClass` | The county and place codes from the boundary file. |
| `specialDistricts` | Each district with its code, type, rate and food rate. |
| `foodCombinedRate`, `foodStateRate` | The food and drug rate and its state part. |
| `combinedInterstateRate`, `compositeSerCode` | The rate for sales from out of state and the state's reporting code. |
| `effectiveFrom`, `effectiveTo` | The days the row's records are in effect. |
| `rateFile`, `boundaryFile`, `rateFileUrl`, `boundaryFileUrl` | The SST files behind the row, with links. |
| `notes` | Anything to know about the row. |

### Where the data comes from

Each member state has to publish a rate file and a boundary file in one format, and the Governing Board posts them at
streamlinedsalestax.org under Rate and Boundary Files. States post new files before each quarter when anything
changes. This tool reads an index we build from those files, so a run does not download them. The `rateFile` and
`boundaryFile` fields name the exact files behind each row, with links to them.

**How fresh it is:** early each month we check the posted files and rebuild every state that posted a new one. States
post next quarter's files by the first day of the month before the quarter starts, so they are usually in the index
weeks before they take effect. A file a state posts or corrects later in a month can take up to a month to show up
here. The run's SUMMARY record says when the index was last built and which file each state's rows came from.

### FAQ

**What is the sales tax rate for my ZIP code?**
Put the ZIP in **ZIP codes** and press Start. If the ZIP is in one of the 24 member states you get the combined
rate and its parts.

**Can I call it as a sales tax rates API?**
Yes. Call it from the Apify API with a list of ZIPs and read the rows as JSON, CSV or Excel from the run's dataset.

**Why is California or Texas missing?**
They are not Streamlined Sales Tax members, so they do not publish these files. This tool covers only the states
that do.

**Does it work out the tax on an order?**
No. It gives you the rates. Which items are taxable, exemptions and shipping rules are up to you and the state.

**How current are the rates?**
As current as the files posted when we last checked, and we check once a month. Each row names its file, and the
file name carries the quarter it starts. If a rate looks off, compare it with the linked file or the state's own
lookup.

### Use it from an AI agent or MCP

AI agents can find and run this Actor through the [Apify MCP server](https://mcp.apify.com). Its `search-actors`
tool finds it, `fetch-actor-details` reads the input and output fields described above, and `call-actor` runs it on
your Apify account at the prices on the Pricing tab. To give an agent only this Actor as a tool, add it to the server
URL: `https://mcp.apify.com?tools=plainrecordlabs/sst-sales-tax-rates`.

`zips` is the only field a run needs. For example, this input returns today's rates for a ZIP in Utah and a ZIP+4 in
Seattle:

```json
{ "zips": ["84003", "98101-1234"] }
```

### Pricing

You pay per rate row returned: 0.005 USD a row on the Free and Bronze plans (5 USD per 1,000 rows), 0.0045 on Silver
and 0.004 on Gold and up. There is no start fee, and a ZIP that returns no row costs nothing. The Pricing tab has the
current prices. **Max results** and your run's cost limit cap what one run can cost.

### Feedback

Need a state's address records or another field? Open an issue on the Issues tab.

Plain Record Labs

# Actor input Schema

## `zips` (type: `array`):

5-digit ZIPs like 84003, or ZIP+4s like 84003-1234 for the exact rate. Up to 10,000 per run. A ZIP outside the member states returns no row and is listed in the run's SUMMARY record. This is the one field you need, for example \["84003", "98101-1234"].

## `date` (type: `string`):

The day the rate should apply, as YYYY-MM-DD, for example 2026-10-01. Empty = today. The files carry past rates and rates already posted for the next quarter.

## `states` (type: `array`):

Two-letter codes, for example UT or WA. Empty = every member state. Useful for ZIPs on a state line, which can sit in two states' files.

## `maxResults` (type: `integer`):

Return at most this many rate rows, for example 100. Most ZIPs return one row. 0 = no limit. Default 1000.

## Actor input object example

```json
{
  "zips": [
    "84003",
    "98101-1234",
    "57701"
  ],
  "date": "2026-10-01",
  "states": [
    "UT",
    "WA"
  ],
  "maxResults": 100
}
```

# Actor output Schema

## `rates` (type: `string`):

One item per ZIP and matching boundary record.

## `summary` (type: `string`):

How many ZIPs matched, the ZIPs that did not and why, and the source files used.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "zips": [
        "84003",
        "98101-1234",
        "57701"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("plainrecordlabs/sst-sales-tax-rates").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "zips": [
        "84003",
        "98101-1234",
        "57701",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("plainrecordlabs/sst-sales-tax-rates").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "zips": [
    "84003",
    "98101-1234",
    "57701"
  ]
}' |
apify call plainrecordlabs/sst-sales-tax-rates --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,plainrecordlabs/sst-sales-tax-rates"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/9uizSdaxtrwbuSmE2/builds/6QtRTfiO0nTzAn4YW/openapi.json
