# Rate limits

> Anonymous callers get 60 counts an hour and 600 a day. Every response reports your remaining quota in the IETF RateLimit headers, and a 429 says how long to wait.

Source: https://emailmovers.com/mydatahub/docs/rate-limits
Updated: 17 October 2026

Limits depend on who you are, and every response tells you where you stand. Anonymous callers get 60 counts an hour and 600 a day per IP.

Free and paid accounts get more, per organisation. The limits protect the counts budget, because a count runs a real query and an order spends credits.

> **Not yet live.** MyDataHub v2 is built and tested locally and is not yet live. This page describes the contract the API is tested against, and the launch date is to confirm.

## Limits by tier

| Tier | Counts | Other limits | Status |
| --- | --- | --- | --- |
| anonymous | 60 an hour and 600 a day per IP and fingerprint | Challenge after 20; at most 6 filters an audience; no single company counts, free text title search or radius under 5 miles; counts under 100 and breakdown cells rounded to the nearest 5 | confirmed |
| free | 60 a minute and 1,000 a day per organisation | Counts under 100 and breakdown cells rounded to the nearest 5 | to confirm |
| paid | Published per plan | Exact counts | confirmed |

The anonymous counts figures are confirmed. The free account figures are to confirm, and paid limits are published per plan.

Every request, whatever it does, is also limited. These request limits are the current build's own and are to confirm.

| Tier | Counts | Any request |
| --- | --- | --- |
| Anonymous, per IP | 60 an hour, 600 a day | 120 a minute |
| Free account, per organisation | 60 a minute, 1,000 a day | 300 a minute |
| Paid, per organisation | 600 a minute | 1,200 a minute |

> **To confirm.** A managed challenge after 20 anonymous counts (Cloudflare Turnstile) is an edge control planned for the hosted service. It is not part of the API itself, and the anonymous bucket today is the IP address alone, without a client fingerprint.

## The headers

Every response carries the [IETF `RateLimit` and `RateLimit-Policy` fields](https://datatracker.ietf.org/doc/html/draft-ietf-httpapi-ratelimit-headers), errors included. A 429 and a 503 also carry `Retry-After`.

| Header | Example | Meaning |
| --- | --- | --- |
| `RateLimit-Policy` | `"counts-hourly";q=60;w=3600, "counts-daily";q=600;w=86400, "requests";q=120;w=60` | One item for each window that applies to you: its name, the quota `q` and the window `w` in seconds |
| `RateLimit` | `"counts-hourly";r=59;t=3522, "counts-daily";r=599;t=50322, "requests";r=119;t=42` | One item for each of those windows: the quota remaining `r` and the seconds until it resets `t` |
| `Retry-After` | `42` | Seconds to wait before retrying, on 429 and 503 |

Each list holds one item for every window the request is counted against, separated by commas, so read the item by its name. The names are `counts-hourly`, `counts-daily`, `counts-per-minute` and `requests`. A call that is not counted against the counts budget carries only the `requests` item.

A 429 returns the [rate-limited](https://emailmovers.com/mydatahub/docs/errors/rate-limited.md) problem, with `retry_after_seconds` repeating the header.

## What uses the counts budget

An operation spends from the counts budget when it asks the engine about data: counting, previewing and quoting, and every call that looks up a record, whether or not it finds one. That includes suppression checks, enrichment, objections, suppression uploads and CRM overlap checks.

| Call | Extra charge |
| --- | --- |
| Preview with `shuffle` true | One more unit, and it is logged |
| `POST /v1/suppressions/check` | One more unit for each further 100 ids |
| `POST /v1/contacts/enrich` with no match | A key may make 100 a day. A match costs a credit, which is its own brake |
| `POST /v1/objections` with scope `emailmovers` | One unit, and at most 200 a day for an organisation |

The catalogue lookups are on purpose outside the counts budget: filter values, title expansion and location resolution. They answer from the catalogue, and a type-ahead calls them on every keystroke. They are still limited by the request limit.

## Anonymous controls

Besides the rate limits, an anonymous caller is limited to 6 filters in one audience. The premium filter, `contact.premium` in `[true]`, is not counted, since the app and a compiled brief add it themselves. Single-company counts, free text title search and a radius under 5 miles need a free account, and the answer is [account-required](https://emailmovers.com/mydatahub/docs/errors/account-required.md).

Counts under 100 and every breakdown cell are rounded to the nearest 5. See [Counts and breakdowns](https://emailmovers.com/mydatahub/docs/counts.md).

Behind a proxy, the client address is read from the right of `X-Forwarded-For`, never from the left, which the client controls.

## Retrying well

1. On 429, wait for `Retry-After`, then retry. Do not retry sooner.
2. On 503 or 500, wait for `Retry-After` if present, otherwise back off with jitter, and retry with the same `Idempotency-Key`.
3. Read `RateLimit` before a burst, and spread requests across the window.
4. Cache what you can. A count by id answers `304` to `If-None-Match` without running the engine, and the catalogue, plans and datasets carry an `ETag`.

## Next steps

- See the status codes of every operation in the [REST reference](https://emailmovers.com/mydatahub/docs/reference/rest.md).
- Read [Errors](https://emailmovers.com/mydatahub/docs/errors.md) for `rate-limited` and `service-unavailable`.
