# Rate limits > Limits are per workspace, per minute, and set by plan. Every response tells you where you stand, and a 429 tells you exactly how long to wait. Source: https://getlaunchlist.com/help/docs/api/rate-limits --- Limits are **per workspace, per minute**, not per key. Creating more keys does not buy more throughput. | Plan | Requests per minute | | ------- | ------------------- | | Free | 60 | | Collect | 300 | | Launch | 300 | | Grow | 1000 | ## Reading your budget Every response carries two headers: ```text RateLimit-Limit: 300 RateLimit-Remaining: 287 ``` Read these rather than guessing. They are the only thing that tells you where you actually stand. > [!WARNING] `RateLimit-Reset` is a timestamp, and only appears on a 429 > A successful response does not carry it at all, so do not build a loop that expects one. When it > does appear, its value is an **absolute Unix timestamp** for when the window clears, not a count > of seconds. Sleeping for that number sleeps for decades. **Use `Retry-After`**, which is already > in seconds and is on every `429`. ## When you hit the limit ```json { "error": { "type": "rate_limit_error", "code": "rate_limit_exceeded", "message": "Rate limit exceeded for the Launch plan. Check the RateLimit-* headers and retry after the reset; higher plans carry higher limits.", "request_id": "req_01k2p8w3q6z9y0abcdefgh7jkm" } } ``` The response carries `Retry-After` in seconds. **Wait that long, then retry.** A fixed delay either wastes time or hammers us again; the header is exact. ```js if (response.status === 429) { const wait = Number(response.headers.get('Retry-After') ?? 1); await new Promise((resolve) => setTimeout(resolve, wait * 1000)); // then retry } ``` ## Limits that are not the plan limit The per-workspace budget above is the one you will normally meet. Narrower limiters sit alongside it, and each answers a `429` of its own, so a request can be refused while `RateLimit-Remaining` still looks healthy. **Unauthenticated requests: 30 per minute, per IP address.** This covers the service index at `/v1` and the [OpenAPI document](/help/docs/api/openapi), which need no key. It is generous for a tool importing the spec and tight enough that the two endpoints without a key cannot be leaned on. **Redeeming and confirming invitations is limited separately.** `POST /v1/invitations/redeem`, `/v1/invitations/exchange` and `/v1/invitations/confirm` each spend from the workspace budget **and** from a layered limiter on top of it: 10 a minute per invitation code, 20 a minute per identity, 60 a minute per source IP address across every project on the platform, plus a per-workspace ceiling. Redemption is the one endpoint where guessing pays, so it is bounded on several axes at once. A real signup flow meets at most one of them; an attacker has to beat all of them. A bulk integration running from a single egress IP address will meet the 60-a-minute per-IP axis first. **Resending a verification email is limited separately.** `POST /v1/contacts/{contact}/resend_verification` re-issues a real email on every call, so it carries its own ceiling of 10 a minute per workspace on top of your plan's request budget. This is the limit a script re-confirming a list will meet, not the plan limit. **Importing is limited separately.** `POST /v1/imports` spends from the workspace budget **and** from its own ceiling of 5 a minute and 30 an hour per workspace, because one call queues a whole import. **Broadcast tests and sends are limited separately.** `POST /v1/broadcasts/{broadcast}/test` is limited to 12 a minute per workspace, and `prepare` and `send` to 10 a minute, each on top of your plan's request budget. **Repeatedly presenting a bad key is limited per IP address.** After 30 failed authentication attempts we answer `429` with the code `too_many_requests` rather than another `401`. This one is worth handling separately, because unlike every other `429` it carries **no `Retry-After` and no `RateLimit-*` headers**. Treat it as a signal that the credential is wrong, not that you are going too fast: retrying harder will not fix it. Wait a minute and fix the key. ## Staying under it **Do not poll.** Use [webhooks](/help/docs/plugins/webhook) to be told when something happens instead of asking repeatedly whether it has. **Page in 100s.** One request for 100 contacts costs one request from your budget, where four pages of 25 cost four. **Use `/count` for counts.** It reads a maintained counter rather than paging. **Pace bulk work.** A full sync that fires as fast as it can will spend its entire minute in the first two seconds and then stall. Spread it. > [!NOTE] The limit is shared across everything using the workspace > A nightly sync and a live signup integration draw on the same budget. Capture usually matters > more than the sync, so pace the sync to keep it from starving capture. ## Next - [Errors](/help/docs/api/errors) - [Pagination](/help/docs/api/pagination) - [Webhooks](/help/docs/plugins/webhook): the alternative to polling ## Common questions ### What are the LaunchList API rate limits? Per workspace, per minute: 60 on Free, 300 on Collect and Launch, and 1000 on Grow. Every response carries RateLimit-Limit and RateLimit-Remaining showing what is left. ### What should I do when I get a 429? Wait the number of seconds in Retry-After, then retry. Do not retry immediately and do not use a fixed delay; the header tells you exactly how long the window has left. ### Is the rate limit per API key or per workspace? Per workspace. Creating extra keys does not raise your limit, so a background sync and a live integration share the same budget and should be paced accordingly.