> This page is for version v5.0.
> For other versions, use one of these documentation indexes:
> - v6.0 (default): https://docs.zip.tax/v-6-0/llms.txt
> - v5.0: https://docs.zip.tax/v-5-0/llms.txt

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.zip.tax/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.zip.tax/_mcp/server.

# Account Metrics & Usage

`GET /account/v{N}/metrics` returns the current usage and quota for
the API key you pass. Replace `{N}` with the API version your key is
on (`v10`–`v60`). Use it to show an in-app dashboard, set up alerting
before you run out of quota, or debug an unexpected 108 by confirming
your plan limits.

> **Info**
>
> This endpoint reports **plan quota**: the total requests your plan
> includes and how many you've used. It's a different thing from the
> per-minute rate limit (which is `request_rate` and is documented on
> [Rate Limiting & Errors](./rate-limiting-errors)). You can burn
> through your monthly quota without ever tripping the per-minute limit,
> and vice versa.

## Making the call

Pass your API key as a query parameter (or as `X-API-KEY` header) to
`GET /account/v{N}/metrics` (examples below use `v60`):

#### cURL

```bash
curl "https://api.zip-tax.com/account/v60/metrics?key=YOUR_API_KEY"
```

#### Python

```python
import requests

res = requests.get(
    "https://api.zip-tax.com/account/v60/metrics",
    headers={"X-API-KEY": "YOUR_API_KEY"},
)
metrics = res.json()
print(f"{metrics['request_count']:,} / {metrics['request_limit']:,} used")
```

#### Node.js

```javascript
const res = await fetch(
  "https://api.zip-tax.com/account/v60/metrics",
  { headers: { "X-API-KEY": "YOUR_API_KEY" } }
);
const metrics = await res.json();
console.log(`${metrics.request_count} / ${metrics.request_limit} used`);
```

## Response shape (v60)

```json
{
  "request_count": 4215,
  "request_limit": 100000,
  "usage_percent": 4.215,
  "is_active": true,
  "message": "Contact support@zip.tax to modify your account"
}
```

| Field           | Type    | Meaning                                                                   |
| --------------- | ------- | ------------------------------------------------------------------------- |
| `request_count` | integer | Total requests made on this key this billing period.                      |
| `request_limit` | integer | Total requests your plan includes. `0` means unmetered.                   |
| `usage_percent` | number  | `request_count / request_limit * 100`.                                    |
| `is_active`     | boolean | `false` if the key has been disabled (billing issue, support hold, etc.). |
| `message`       | string  | How to change plan or limits; always points at support.                   |

### Legacy response (v10–v50)

Older API versions split the counters into `core_*` (tax lookups) and
`geo_*` (geocoded lookups). All new keys use the v60 shape. If you're
still on a legacy key, you'll see:

```json
{
  "is_active": true,
  "core_request_count": 4215,
  "core_request_limit": 100000,
  "geo_request_count": 4215,
  "geo_request_limit": 100000,
  "geo_enabled": true,
  "core_usage_percent": 4.215,
  "geo_usage_percent": 4.215,
  "message": "Contact support@zip.tax to modify your account"
}
```

> **Info**
>
> For v60 keys the counters are unified. Every lookup counts against
> `request_limit`, regardless of whether it was by address or by
> coordinates.

## Patterns

### Show a usage widget in your dashboard

Poll once when the page loads, not on every render:

```javascript
async function loadUsageWidget() {
  const res = await fetch(
    "https://api.zip-tax.com/account/v60/metrics",
    { headers: { "X-API-KEY": process.env.ZIPTAX_KEY } }
  );
  const { request_count, request_limit, usage_percent } = await res.json();

  document.querySelector("#used").textContent = request_count.toLocaleString();
  document.querySelector("#limit").textContent = request_limit.toLocaleString();
  document.querySelector("#bar").style.width = `${usage_percent}%`;
}
```

### Alert before you run out

Check metrics on a schedule (hourly or daily is plenty) and page your
team when you cross a threshold:

```python
def check_quota():
    res = requests.get(
        "https://api.zip-tax.com/account/v60/metrics",
        headers={"X-API-KEY": os.environ["ZIPTAX_KEY"]},
    )
    m = res.json()

    if m["usage_percent"] >= 80:
        page_team(f"Ziptax usage at {m['usage_percent']:.0f}%")
    if not m["is_active"]:
        page_team("Ziptax key is deactivated")
```

### Debug an unexpected 108

If you're getting 108s but your plan shouldn't hit the rate limit,
`/account/v{N}/metrics` can confirm your key is active and reachable.
Remember: 108s come from the **per-minute** `request_rate`, not from
`request_limit`, so a low `usage_percent` here doesn't rule out a 108.
Check the `X-RateLimit-Limit` header on the 108 response instead.

## Authentication and errors

The `/account/v{N}/metrics` endpoint uses the same API key auth as
`/request/v*`: either `X-API-KEY` header or `key=` query parameter.
If the key is missing, you'll get an HTTP 400 with a short error body:

```json
{
  "error": "API key is required as a query parameter",
  "message": "The parameter 'key' is required"
}
```

If the key is valid but not found in Ziptax's account database, the
response is still HTTP 200 with zeros across the board and
`is_active: false`. Check `is_active` before trusting the numbers.

## Related

#### [Rate Limiting & Errors](rate-limiting-errors)

The per-minute limit that's separate from plan quota.

#### [Response Codes](response-codes)

Every application-level code Ziptax can return.