Skip to content
YAS.SH
API

Rate Limits Explained: Budgets, Backoff, and Building on Them

How API rate limits work, what the budgets are, and how to build clients that respect them — 429 handling, backoff, and headroom.

yas-team5 min readapirate limitsbackoff
Rate Limits Explained: Budgets, Backoff, and Building on Them
Featured imageRate Limits Explained: Budgets, Backoff, and Building on Them

Rate limits are the API's self-defense — and the client's most important contract. Every production integration eventually meets a 429; the difference between a robust integration and a flaky one is how it behaves at that moment. This guide explains the budgets, the response format, and the client patterns that make limits invisible.

The budgets

Endpoint Limit Window
Link creation (per user) 60 1 minute
Link creation (per IP) 120 1 minute
Login (per IP) 15 1 minute
Registration (per IP) 20 1 minute
Contact form (per IP) 5 1 minute
Newsletter (per IP) 10 1 minute
Password gate (per link per IP) 10 1 minute

The dual user/IP limits matter for shared environments: an office NAT (one public IP, many users) gets more headroom, while a single user can't starve others by bursting.

The 429 contract

Limits speak RFC 9457 problem+json:

{
  "type": "https://yas.sh/docs/errors#rate_limited",
  "title": "Rate Limited",
  "status": 429,
  "code": "RATE_LIMITED",
  "detail": "Too many requests",
  "retryAfter": 37
}

Two things to honor: the status itself (429) and retryAfter (seconds). A client that sleeps exactly retryAfter and retries is compliant; a client that hammers through 429s makes the problem worse for everyone.

The client pattern: budget + jitter + backoff

The pattern that never sees a 429:

1. Budget: one create per second (60/min) — pace, don't burst
2. On 429: wait retryAfter seconds (never less)
3. On network error: exponential backoff 1s → 2s → 4s → 8s (cap 30s) + jitter
4. Retries are idempotent (Idempotency-Key per request)
async function createLinkWithRetry(body: object, maxRetries = 5) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    const res = await fetch("/api/v1/links", {
      method: "POST",
      headers: { "Content-Type": "application/json", "Idempotency-Key": crypto.randomUUID() },
      body: JSON.stringify(body),
    });
    if (res.status === 429) {
      const data = await res.json().catch(() => ({}));
      await sleep((data.retryAfter ?? 5) * 1000 + Math.random() * 500);
      continue;
    }
    return res;
  }
  throw new Error("rate limited after retries");
}

The jitter matters: synchronized retries from a batch create thundering-herd spikes. Randomize the delay by a small margin.

Why limits exist (and what they protect)

The same limits that protect the platform protect your data: a runaway loop that creates 10,000 duplicate links is cheaper to stop at 60/min than after the fact. The limits are generous for humans (1 per second sustained is far beyond interactive use) and deliberately tight for abuse paths (login, registration, contact).

Building dashboards against 429s

If your internal tooling integrates the API, surface rate-limit state: log 429s with their retryAfter, and alert if any single key exceeds 10% 429 rate — that's the signature of a misbehaving loop, not an active user.

Reading the response headers

Beyond the JSON body, the HTTP response headers carry the rate-limit state you can rely on without parsing. A production client should read and respect the standard header set:

  • RateLimit-Limit — the maximum number of requests allowed in the current window.
  • RateLimit-Remaining — how many requests are left before you hit the limit.
  • RateLimit-Reset — the Unix timestamp (or seconds remaining, depending on the convention) when the window resets.
  • Retry-After — the number of seconds to wait before your next attempt, also returned on 429.

Reading RateLimit-Remaining proactively lets you slow down before you trip the limit, rather than only reacting after a 429. A well-behaved client that respects Retry-After and paces toward RateLimit-Reset keeps integrations running smoothly even on shared or heavily loaded endpoints.

Bursting vs sustained throughput

Two different shapes of load behave very differently against the same limit:

  • Bursts — a sudden spike of many requests in a short window. These trip the 429 quickly because the window is usually one minute. Burst traffic should be spaced with a small delay (the "budget" pattern already shown).
  • Sustained throughput — a steady rate over minutes or hours. This is governed by the per-minute window repeated, and a steady sub-window pace (e.g. one request per second) sails through indefinitely.

The practical implication: if you have 1,000 links to create, do not fire them in 60 parallel requests all at once. Space them at roughly one per second, which lands well inside the per-minute budget while finishing in about 17 minutes. For longer jobs, add a simple progress check that measures the effective throughput and, if it starts degrading (headers show Remaining shrinking faster than expected), inserts a small backoff.

Common integration mistakes

Three mistakes come up repeatedly in support:

  1. Ignoring Retry-After and retrying immediately on 429. This guarantees a second 429 and makes the situation worse. Always sleep at least the returned value.
  2. Retrying on 4xx errors that are not rate limits. A 422 validation error will not succeed on retry — retry only idempotent-safe cases and rate/network errors.
  3. Parallel bursts from a batch. If your script fires 50 requests at once, the first few succeed and the rest get 429s. Add a delay or a concurrency cap, and treat 429 as a signal to slow down, not to give up.

A disciplined client treats the rate limit as a contract to cooperate with, not an obstacle to fight.

Testing your client against the limit

You should test rate-limit handling before production, not during an incident. Make a small script that issues requests faster than the limit allows on a throwaway account, and confirm your client: reads the 429, sleeps Retry-After, and eventually succeeds. Also test the idempotency behavior by sending the same request twice with the same Idempotency-Key and confirming you get one resource, not two. These two tests cover the failure modes that most often break real integrations under load.

Key numbers to remember

The limits that matter most day to day: link creation is 60 per minute per user and 120 per minute per IP, login is 15 per minute per IP, registration 20 per IP, and the contact and newsletter forms are 5 and 10 per minute per IP respectively. The practical takeaway is that interactive humans will never hit these, while automation that does not pace itself will. If you remember one rule, it is this: sleep on Retry-After, pace writes at roughly one per second, and keep every retry idempotent.

In short, cooperate with the contract: pace writes, honor Retry-After, keep retries idempotent, and read the header state so you slow down before you are limited rather than after. Integrations that treat the budget as a partner run smoothly under load.

Conclusion

Rate limits are the API's contract for shared infrastructure: explicit budgets, a precise 429 response, and retry semantics. Build clients that pace, sleep on retryAfter, back off with jitter, and stay idempotent — and the limits will never touch you. The API reference documents every limit alongside each endpoint.

Frequently asked questions

What are the rate limits on the yas.sh API?

Link creation: 60/min per user and 120/min per IP. Login: 15/min per IP. Registration: 20/min per IP. Contact and newsletter: 5–10/min per IP. Password gates: 10/min per link per IP.

What happens when I exceed a limit?

You get HTTP 429 with a problem+json body including retryAfter (seconds). Clients should wait that long and retry.

How do I avoid hitting limits in batch jobs?

Budget: 60/min means one request per second sustained. Add jitter and exponential backoff for retries, and you'll never see a 429 in practice.

Are reads rate-limited too?

List/analytics reads are not hard-limited today; the same 429 discipline applies if you ever see one.

Was this helpful? Share
Ask YAS AI
🍪 Cookies & privacy. Essential cookies keep you signed in and remember language and theme. Google AdSense and reCAPTCHA are Google technologies: AdSense runs only after Accept All; reCAPTCHA loads on sign-in and contact forms. See how Google uses data: https://policies.google.com/technologies/partner-sites cookie policy · privacy policy.
Settings