# Rate limits & quotas

> What each plan allows, how usage is counted, the headers that tell you where you stand, and how to stay clear of throttling.

Page: https://developers.zactonz.com/apis/rate-limits/

Every key carries three limits from its account's plan: requests per minute, units per day and units per month. The console shows live usage for each, and every response carries headers with the same numbers.

## Plans

| Plan | Requests per minute | Units per day | Units per month | Concurrent requests | Upload size |
|---|---|---|---|---|---|
| Free | 30 | 500 | 10,000 | 1 | 2 MB |
| Starter | 120 | 5,000 | 100,000 | 2 | 10 MB |
| Pro | 300 | 25,000 | 500,000 | 4 | 20 MB |
| Business | 600 | 100,000 | 2,000,000 | 8 | 50 MB |

Requests per minute and concurrency apply per key. Daily and monthly units apply to the whole account, across all of its keys. Larger volumes are arranged through support on a custom plan.

## Units

A unit is the cost of one call. Cheap lookups cost one unit, work that runs a headless browser costs more:

| Cost | Endpoints |
|---|---|
| 1 | QR Encoder, QR Decoder, Drive Direct Link, Barcode, SSL Inspector, DNS Lookup, WHOIS, Link Preview |
| 2 | Translator, Mail Verifier, Domain Email Health, Web Page to Markdown |
| 3 | Image Convert |
| 5 | Screenshot, PDF, OG Image, Link Preview and Markdown with `render=1` |

Only successful calls are charged. A request rejected with a `4xx` status costs nothing, and a cached answer costs the same as a fresh one.

## Headers

```text
X-RateLimit-Limit: 300
X-RateLimit-Remaining: 287
X-RateLimit-Reset: 1789891200
X-Quota-Day-Limit: 25000
X-Quota-Day-Remaining: 24310
X-Quota-Month-Limit: 500000
X-Quota-Month-Remaining: 498211
```

`X-RateLimit-Reset` is the Unix time the minute window rolls over. `GET /auth/me/` returns the same figures without spending a request against your quota.

## When a limit is hit

The reply is `429` with a JSON body and a `Retry-After` header in seconds:

```json
{ "status": 429, "message": "Rate limit exceeded: 300 requests per minute" }
```

| Message | What happened | What to do |
|---|---|---|
| Rate limit exceeded | More requests than the plan allows in this minute. | Wait for `Retry-After`, then resume. |
| Too many concurrent requests for this key | More calls in flight than the plan allows. | Queue calls in your backend so no more than the plan's concurrency run at once. |
| Quota exceeded for this plan | The day's or month's units are used up. | Wait for the reset, or move to a larger plan in the console. |
| Too many requests | Too many failed key lookups from one address. | Fix the key; valid keys never count toward this. |

Accounts on a soft-overage arrangement receive `X-Quota-Exceeded: soft` instead of a `429` and are billed for the excess.

## Bursts

Spread calls out rather than firing them all at once. The host in front of the APIs also watches for floods: a run of more than 20 requests to the same endpoint with under two seconds between them, all over one connection, is answered with an HTML `403` for the next ten seconds. Bulk jobs should pause for two seconds after every 20 calls to one endpoint, and treat an HTML `403` as a signal to back off for ten seconds before resuming. Size worker pools to the plan's concurrency, not to the number of items in the queue.

## Timing

Some endpoints do real work per call. Set client timeouts accordingly:

- **Screenshot**, **PDF**, **OG Image**, and `render=1` on Link Preview and Markdown: a headless browser session, typically 3 to 20 seconds.
- **Mail Verifier** and **Domain Email Health** with `probe=1`: one SMTP conversation per address or mail server, up to about 40 seconds.
- **WHOIS**: a registry round trip, usually under 3 seconds, longer for registries without RDAP.

## Caching what you get back

- Barcode, OG Image, Image Convert, QR and Screenshot files stay at their returned URL for the period stated in `expires_in`; there is no need to regenerate the same content.
- SSL, DNS, WHOIS, Email Health, Link Preview and Markdown answers are cached on our side for a short period; pass `fresh=1` where the reference page offers it to bypass the cache.
