GLOSSARY

What is a Rate Limit?

A rate limit is a server-enforced ceiling on how many requests one caller may make in a given window. Exceed it and the server answers 429 Too Many Requests instead of doing the work, usually with headers that say how much budget is left and when it resets.

Free forever plan · No credit card required · Cancel anytime

Quick definition

A rate limit is a server-enforced ceiling on how many requests one caller may make in a given window. Exceed it and the server answers 429 Too Many Requests instead of doing the work, usually with headers that say how much budget is left and when it resets.

In a single sentence: a rate limit is the API telling you how fast it is willing to be useful to you.

What it means

A rate limit is a ceiling on requests per unit of time, enforced per caller. It exists for three reasons that are worth separating, because they imply different limits.

  • Capacity. Shared infrastructure has a finite throughput, and one caller in a tight loop can consume all of it.
  • Cost. Some calls spend money downstream. On a data API, a live platform read costs real upstream capacity that a cached catalogue read does not.
  • Fairness and abuse. Limits are also what stop a credential from being used to bulk-scrape a corpus it was sold access to on different terms.

When you cross a limit, the correct status is 429 Too Many Requests, defined in RFC 6585 section 4. It is worth being precise about the neighbours: 403 means you are not allowed to make this call at all, 402 means the plan behind the key cannot pay for it, and 503 means the server itself is unwell. Only 429 means "you, specifically, are going too fast".

The algorithms, and why the choice is visible to you

Five implementations dominate, and each has a signature you can feel from the client side.

  • Fixed window. Count requests per calendar minute, reset at the boundary. Cheap, and wrong at the edges: a 600-per-minute limit permits 600 requests at 11:59:59 and another 600 at 12:00:00, so 1,200 land inside one second. That boundary burst is why nobody protects a fragile backend with a fixed window alone.
  • Sliding window log. Store a timestamp per request and count the ones inside the trailing window. Exact, and expensive in memory for a busy key.
  • Sliding window counter. Weight the previous window's count by how much of it still overlaps. Close to exact at a fraction of the storage. This is what most gateways ship.
  • Token bucket. A bucket of capacity C refills at R tokens per second; each request takes one token. Allows a burst of up to C and then settles to R. This is the model that matches how a published limit like "600 a minute" usually behaves in practice.
  • Leaky bucket. Requests queue and drain at a fixed rate. Smooths output rather than permitting bursts. Common in front of hardware and message pipelines.

The token bucket formula is worth carrying in your head because you can implement the client-side mirror of it in five lines: tokens = min(capacity, tokens + elapsed_seconds * refill_rate), then take one token per call and sleep when the bucket is empty.

Limits stack, and each one has its own remedy

Real APIs rarely enforce a single number. The Pinlyx Data API stacks four independent ceilings, and a request can be refused by any of them:

  1. Burst. Requests per minute, from your tier. Refusal: 429 with rate_limited. Clears within the minute.
  2. Daily quota. Requests per day, from your tier. Refusal: 429 with quota_exceeded. Clears at 00:00 UTC and not a second earlier.
  3. Live reads. A separate, much smaller per-minute ceiling for calls that go to the platform right now rather than to the catalogue.
  4. Plan budget. Requests included in the billing period. Refusal: 402 payment_required. Waiting never helps; the X-Plan-* headers say how far past the line you are.

The tier table below makes a point that surprises most people the first time they do the arithmetic: on every tier except the top one, the daily quota binds long before the per-minute ceiling does. On the free tier, 30 requests a minute sustained would be 43,200 a day against a 1,000-a-day quota, so a full-speed job exhausts its day in a little over half an hour. The per-minute number is a burst allowance, not a budget.

Limits that are not yours

A 429 does not always mean you were greedy. Live reads on the five platforms scraped directly draw on an upstream budget shared across every caller, and the capability endpoint publishes each source's ceiling up front rather than making you discover it in production. LinkedIn company pages are capped at four reads a minute across the entire system, because the pages are heavy and LinkedIn blocks anything faster. No plan upgrade moves that number.

This is the case where retrying harder is actively counterproductive, and it is why a good API tells you which ceiling you hit rather than returning a bare 429. Design your client to read the code and the message, not just the status.

Reading the headers

Every response, successful or not, carries the accounting. That is deliberate: it lets a client slow down before it hits the wall rather than after. A worker that checks X-RateLimit-Remaining and adds a small sleep once it drops under ten percent of the limit will essentially never see a 429, and will finish sooner than one that sprints, bounces, backs off and retries.

Designing the client side

Four habits separate an integration that lives comfortably inside a limit from one that fights it constantly.

  • Mirror the limit locally. A shared token bucket in front of your HTTP client, sized to the published ceiling, turns bursts into a queue instead of a wall of 429s.
  • Cap concurrency separately from rate. Ten requests a second with unbounded concurrency can still open two hundred sockets during a slow patch.
  • Honour Retry-After, then add jitter. Every client that was refused at the same moment will otherwise wake at the same moment.
  • Page wide. Metering counts HTTP calls, not rows. Walking a list at 500 rows per page instead of 50 costs a tenth of the budget for the same data. See cursor pagination.

Related concepts

  • Retry-After: the header that tells you exactly how long to wait.
  • Exponential backoff: what to do when there is no Retry-After.
  • Jitter: why a synchronised retry is a second outage.
  • Flood wait: Telegram's own rate-limit response, with the same shape and a different name.
  • MTProto: the protocol whose limits sit behind every Telegram send.

How Pinlyx handles it

Every Data API response publishes its own accounting, so nothing runs out silently: burst and daily limits from your tier, plan budget from what you bought, and a Retry-After on every 429. The two 429 codes are separated on purpose so a client can tell a one-minute pause from a wait until midnight. Inside the product, the same discipline applies to outbound messaging, where a per-account rate limiter paces sends and handles platform flood waits with automatic backoff.

Cheat sheet · the published tiers

Which ceiling actually binds you.

The first three columns are the API's published tiers. The last one is the arithmetic nobody does until the job stops at lunchtime.

TierPer minutePer dayLive per minuteBinding ceiling
free301,0005Daily quota, after 33 minutes at full burst
standard12025,00020Daily quota, after 3 h 28 m at full burst
pro600250,00060Daily quota, after 6 h 57 m at full burst
unlimited6,00010,000,000600Per-minute burst. 6,000 a minute is only 8.64 M a day
Cheat sheet · anatomy of a 429

The same status, two different problems.

GET /data-api/v2/accounts/instagram/nasa HTTP/1.1
Host: pinlyx.com
Authorization: Bearer psk_live_...

HTTP/1.1 429 Too Many Requests
Retry-After: 12
X-RateLimit-Limit: 30
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1756547412
X-Quota-Limit: 1000
X-Quota-Remaining: 814
X-Request-Id: req_9f2c41a8b3d5

{
  "error": {
    "code": "rate_limited",
    "message": "Rate limit exceeded. Retry in 12 seconds.",
    "request_id": "req_9f2c41a8b3d5"
  }
}

code: rate_limited

The minute's burst is spent. It clears when X-RateLimit-Reset arrives, which the example above puts 12 seconds out. Sleep, add jitter, continue. Note that X-Quota-Remaining is still 814, so the day is fine.

code: quota_exceeded

Same 429, same headers, completely different remedy. The day is gone until 00:00 UTC. A 30-second retry loop started at noon makes 1,440 requests that cannot succeed. Stop the job and schedule it for tomorrow.

The message repeats the wait as prose. Do not parse it. Branch on error.code, which is stable and enumerated, and read the number from Retry-After.

Every accounting header, and what it means
X-RateLimit-LimitRequests allowed in the current one-minute window.
X-RateLimit-RemainingRequests left in the current one-minute window.
X-RateLimit-ResetUnix seconds at which the current window resets.
X-Quota-LimitRequests allowed today, from your tier.
X-Quota-RemainingRequests left in today's quota.
X-PlanPlan id behind the key.
X-Plan-LimitRequests included in the current billing period.
X-Plan-RemainingIncluded requests left in the current billing period.
X-Plan-OverageRequests served beyond the included allowance this period.
X-Plan-Period-EndWhen the current billing period ends, ISO 8601.
Retry-AfterSeconds to wait before retrying. Sent with 429.
X-Request-IdUnique id for this request, echoed in meta.request_id.
Watch out for

Six ways teams fight their own rate limit.

  • Treating every 429 the same. rate_limited clears in a minute; quota_exceeded clears at midnight UTC.
  • Ignoring Retry-After and using a hard-coded sleep, which is either wasteful or immediately refused again.
  • Retrying a 402 payment_required. No amount of waiting funds a plan.
  • One limiter per worker process against one shared key, which multiplies the ceiling by the worker count.
  • Never reading X-RateLimit-Remaining, so the client only ever discovers the limit by hitting it.
  • Starting every scheduled job at exactly 00:00 UTC, when the daily quota resets and every other client starts too.

Rate limits: FAQ

What engineers ask the first time a batch job stops at lunchtime.

RFC 6585 section 4, "Additional HTTP Status Codes", published in 2012. It is not in RFC 9110, which defines core HTTP semantics; 429 remains an extension status code. That matters in practice because some older frameworks and proxies still treat any 4xx they do not recognise as a hard client error and refuse to retry it. RFC 9110 does define the Retry-After header that a 429 should carry, in section 10.2.3.
Both come back as 429 in the Pinlyx Data API, and they need opposite reactions. rate_limited means the burst ceiling for the current minute is spent and it clears within that minute, so a short backoff works. quota_exceeded means the daily quota is spent and it does not clear until 00:00 UTC. Retrying a quota_exceeded every 30 seconds from noon burns about 1,440 pointless requests before midnight and fixes nothing. Branch on error.code, not on the status.
On this API it is per key, which is the detail most teams get wrong operationally. Running twelve worker processes that each hold their own local limiter set to the published ceiling means twelve times the ceiling arriving at the server. Either share one limiter behind a queue, divide the ceiling by the number of workers, or mint a key per worker so the accounting matches reality.
Because some limits are not yours. Live reads on the five platforms scraped directly draw on an upstream budget shared with the rest of the product, and a spent upstream budget is a 429 that names the source and its ceiling. LinkedIn company pages are capped at four reads a minute across every caller, which is a property of the source rather than of your plan, and no tier upgrade changes it. Slow down instead of retrying harder.
No, and confusing the two wastes days. 402 payment_required means the plan behind the key has spent its included requests and overage is off, capped or unfunded; 402 subscription_inactive means the billing period lapsed. Neither clears by waiting. The X-Plan headers on that response tell you exactly how far past the line you are. A retry loop pointed at a 402 will run until it gives up.
Read X-RateLimit-Remaining on every response and slow down before it reaches zero, rather than waiting for the wall. A token bucket on the client that mirrors the server ceiling, plus a concurrency cap, plus jitter on the start of scheduled batches, removes almost every 429 from a well-behaved integration. The 429 handler is then a safety net rather than the primary flow-control mechanism.
Ready to ship

Nothing runs out silently. Every response says where you stand.

Burst, daily quota, live reads and plan budget, each with its own header and its own error code, on every call to the Pinlyx Data API.

Free forever plan · GDPR-ready · No credit card required

We value your privacy

We use cookies to improve our site, analyze traffic, and personalize ads. You can accept all, reject non-essential, or customize your choices. Read our Cookie Policy.