> For clean Markdown of any page, append .md to the page URL. > For a complete documentation index, see https://docs.zip.tax/v-6-0/guides/reference/authentication/llms.txt. > For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.zip.tax/_mcp/server. # Authentication Every Ziptax request is authenticated with an API key. The same key works across every API version (`/request/v10` through `/request/v60`) and against the account metrics, TIC search, and cart calculation endpoints. ## Get an API key 1. Sign in to [platform.zip.tax](https://platform.zip.tax). 2. Navigate to `Develop > API Keys` using the side navigation menu. 3. Generate a new key and copy the value. Keys are opaque strings; keep them out of client-side code and source control. For a step-by-step walkthrough with screenshots, see [How to create an API key](../tutorials/how-to-create-an-api-key). ## Sending the key Two options. The header form is recommended for production; the query form is fine for quick tests. ### Option 1: `X-API-KEY` header (recommended) ```bash curl -H "X-API-KEY: YOUR_API_KEY" \ "https://api.zip-tax.com/request/v60?address=200+Spectrum+Center+Dr+Irvine+CA" ``` ### Option 2: `key` query parameter ```bash curl "https://api.zip-tax.com/request/v60?key=YOUR_API_KEY&address=200+Spectrum+Center+Dr+Irvine+CA" ``` If both are set, the header wins and the query parameter is ignored. ## Which endpoints need a key | Endpoint | Auth required | | ----------------------------------------- | ----------------- | | `GET /request/v10` ... `GET /request/v60` | Yes | | `GET /account/v{N}/metrics` | Yes | | `POST /search/tic` | Yes | | `POST /calculate/cart` | Yes (header only) | | `GET /data/tic` | No, public | | `GET /system/health` | No, public | | `GET /system/metadata` | No, public | ## Entitlements Each key carries entitlements that gate specific features and quotas. When a key is missing one, Ziptax returns a specific response code. | Entitlement | Controls | Error code if missing | | -------------------- | ------------------------------------------------------------------------------------------ | --------------------------------------------------- | | `request_rate` | Requests per minute, set by your plan — see [Rate Limiting & Errors](rate-limiting-errors) | [108](response-codes#108-rate-limit) | | `geo_enabled` | Address and lat/lng lookups (postal code lookups don't require it) | Returned per endpoint | | `rate_loc_can` | `countryCode=CAN` for Canadian rates | [112](response-codes#112-international-not-enabled) | | `product_rates` | `taxabilityCode` for product-specific rules | [113](response-codes#113-product-rules-not-enabled) | | `core_request_limit` | Per-period quota on base rate lookups | [107](response-codes#107-feature-not-enabled) | | `geo_request_limit` | Per-period quota on geocoded lookups | [107](response-codes#107-feature-not-enabled) | Plan upgrades update entitlements on the existing key. No rotation required. > **Info** > > Read current usage against `core_request_limit` and `geo_request_limit` from the [Account Metrics endpoint](account-metrics). ## Invalid or missing keys A malformed, unknown, or deactivated key returns response code **101** with HTTP 401. ```json { "metadata": { "version": "v60", "response": { "code": 101, "name": "RESPONSE_CODE_INVALID_KEY", "message": "Key format is not valid or key not found." } } } ``` Common causes: * Trailing whitespace or wrapping quotes after copy-paste. * A deactivated key (regenerate from the platform). * Wrong environment. * Using `apiKey` or `api_key` as the query parameter name. Only lowercase `key` is accepted. ## Rate limiting Per-key, 60-second sliding window. Your plan sets the ceiling: 10 requests per minute on Starter, 100 on Growth, 500 on Pro, and an account-defined limit on Enterprise. Every response includes: ``` X-RateLimit-Limit: 500 X-RateLimit-Remaining: 487 ``` Exceeding the limit returns HTTP 429 with response code 108. See [Rate Limiting & Errors](rate-limiting-errors) for backoff guidance. ## Key hygiene * Store keys as environment variables. Never check them into source control or front-end bundles. * Use a separate key per environment so you can rotate without affecting others. * Rotate after a suspected leak: generate a new key, deploy it, then deactivate the old one. * Never expose keys to the client. Ziptax is server-to-server; route browser and mobile calls through your backend. * Don't log raw keys. Ziptax internal logs mask all but the first 8 and last 4 characters; mirror that pattern. ## Using keys with the SDKs Each SDK accepts the key in the constructor and sets the header on every request: #### Python ```python import os from ziptax import Ziptax client = Ziptax(api_key=os.environ["ZIPTAX_API_KEY"]) rate = client.by_address(address="200 Spectrum Center Dr, Irvine CA") ``` #### Node.js ```javascript import { Ziptax } from "ziptax"; const client = new Ziptax({ apiKey: process.env.ZIPTAX_API_KEY }); const rate = await client.byAddress({ address: "200 Spectrum Center Dr, Irvine CA", }); ``` #### Go ```go client := ziptax.New(os.Getenv("ZIPTAX_API_KEY")) rate, err := client.ByAddress(ctx, ziptax.ByAddressRequest{ Address: "200 Spectrum Center Dr, Irvine CA", }) ``` ## Related #### [Response Codes](response-codes) Every application-level code Ziptax can return. #### [Rate Limiting & Errors](rate-limiting-errors) How `request_rate` is enforced and how to back off. #### [Account Metrics](account-metrics) Check usage against your plan's quotas. > How to authenticate Ziptax API requests using an API key, including where to send the key and which entitlements gate specific features.