API Rate Limiting: Why Integrations Fail Silent and How to Fix It
September 22, 2026
HTTP 429: The Error That Doesn't Always Feel Like an Error
Here's an uncomfortable truth about rate limits: the failure that actually hurts you almost never shows up as an error on anyone's screen. It shows up as silence.
RFC 6585 defines HTTP 429, "Too Many Requests," as the standard response when a client sends too many requests in a given window. Simple enough. What's not simple is what happens next inside a background job or a webhook handler that nobody's watching. The 429 comes back, the job logs it or it doesn't, and the process just stops. No user sees a broken checkout. Nobody gets an error toast. The integration looks alive because the server's still running. It's just not doing the thing it's supposed to do anymore.
The frustrating part is that most APIs try to warn you before this happens. Per Stripe's rate limiting documentation, responses typically include headers like X-RateLimit-Remaining and Retry-After, specifically so clients can slow down before they hit a wall. Almost nobody reads them. They sit in the response object, unused, while the code marches forward at full speed until a 429 stops it cold.
Why Your Integration Works in Testing but Breaks at Scale
This part catches people off guard every time, and I get why. You build the integration, test it against five or ten calls a day, everything's green, you ship it. A few weeks into production it quietly stops syncing, and nobody can explain why the code that worked fine in staging suddenly doesn't.
The code didn't change. Your volume did. Test environments almost never generate enough traffic to graze a rate limit, so that failure path never gets exercised. Production traffic can dwarf test traffic, and that's exactly when limits you never noticed start biting.
It also matters which API you're talking to, because "rate limit" isn't one universal number. Per Stripe's documentation, their limits run around 100 requests per second on standard plans. Shopify's public APIs sit closer to roughly 2 requests per second. Free tiers on smaller services often cap out somewhere between 10 and 60 requests per minute. Design one integration pattern and assume every provider behaves the same, and you're setting yourself up for a bad surprise. Know your actual tier before you architect around it.
Synchronous Calls vs. Queues: Why Async Architectures Survive Rate Limits
Here's a trade-off worth being honest about: synchronous API calls are simpler to write and easier to reason about. That's exactly why so many integrations start that way. The problem is a synchronous call has nowhere to hide from a 429. It blocks the user's request right there, so the first rate limit you hit becomes a broken page or a failed action someone actually experiences.
Queues handle this differently. As Martin Fowler's writing on distributed systems patterns puts it, decoupling requests from responses lets retry logic run independently, isolating transient failures from whatever the user is doing. The request goes into the queue, a worker picks it up, and if that worker hits a 429 it can back off and try again without anyone waiting on it.
This isn't theory for me. On a marketplace platform I built and still maintain, background jobs handle most of the syncing between systems. The gap between "a job quietly retries" and "a user-facing request just fails" has been the gap between a non-event and a support ticket. If your integration talks to a third-party API from inside a request-response cycle, you're one busy afternoon away from learning this the hard way.
Exponential Backoff with Jitter: The Industry-Standard Retry Pattern
Once things run through a queue, the retry strategy matters as much as the queue itself. The pattern Google, AWS, and Stripe all converge on is exponential backoff: wait one second, then two, then four, then eight, doubling instead of retrying immediately. Google Cloud's retry strategy documentation describes exactly this, progressively longer waits paired with random jitter to dodge the thundering herd problem.
Jitter is the part people skip, and it's the part that actually saves you. If every failed job retries at the same interval, you get waves of requests slamming the API in unison, which just re-triggers the rate limit you were trying to recover from. A small random offset on each backoff spreads those retries out so the queue drains naturally instead of colliding with itself over and over.
Without jitter, a retry storm can make a temporary rate limit look like a full outage. With it, the limit resets and clears on its own while your queue quietly catches up.
The Monitoring Gap: Why Request Counts Lie
This is the one I see missed the most, even on integrations that were otherwise built well. Teams monitor how many requests they send. That number tells you almost nothing on its own.
What you need is visibility into what came back. Send 100 requests and log only the successes, and a system returning 95 200s and 5 silent 429s looks fine on a dashboard that just counts "requests sent." Nothing there tells you five of those requests failed and never got retried. That's the blind spot that lets data quietly stop syncing while every graph in your monitoring tool stays green.
The fix: track status codes explicitly, all of them, not just the happy path, alongside queue depth over time. A queue that's slowly growing instead of draining is usually the earliest sign something's rate limited upstream, well before anyone notices the actual data gap. I've traced more than one stalled sync back to exactly this: a background job eating silent 429s for a while before anyone realized the numbers had stopped moving. The sync always "looked fine" until someone actually opened the queue.
Building a Rate-Limit-Resilient Integration: Four Practical Moves
If you're designing or fixing an integration that talks to an external API, here's where I'd put the effort:
- Read the rate-limit headers on every response and adjust your pacing before you hit the hard limit, not after.
- Apply exponential backoff with jitter to all retries, not just the ones triggered by a 429. Transient errors at scale deserve the same treatment.
- Put an async queue between your application and the external API, whether that's Redis-backed, Bull, Celery, or a managed cloud task queue, so retries happen without blocking a real user.
- Monitor queue depth and response status codes together. A climbing queue or a spike in 429s will tell you hours before your data flow visibly stops.
None of these four are exotic. They're just easy to skip when the integration is small, and easy to regret once it isn't. If you're building or untangling a webhook or API integration and want a second set of eyes before it breaks in production instead of after, I cover related ground in Webhooks Explained: How Your Tools Actually Talk to Each Other and API Integration Mistakes That Cause the Most Support Headaches, or just get in touch and we'll look at what's actually happening in your queue.
So here's the question worth sitting with: if your integration started silently dropping five percent of its requests tomorrow, would anything in your monitoring actually tell you?
FAQ
What's the difference between a rate limit and throttling?
Rate limits are hard caps that block requests once you exceed them (HTTP 429). Throttling is a soft slowdown that degrades service quality but keeps some requests flowing. Both need monitoring, but rate limits demand immediate retry logic while throttling just needs patience.
Should I retry on a 429, and how many times?
Yes, always retry 429s with exponential backoff. Set a maximum retry count (typically 3 to 5) to avoid infinite loops. Most APIs signal wait time via the Retry-After header, so respect it. If you hit max retries, log the failure and alert. Silent drops are worse than failures.
Can I negotiate higher rate limits with my API provider?
Often yes, especially on paid tiers or with higher-volume partnerships. Check your provider's tier options first, Stripe, Shopify, and most major APIs offer higher limits on production plans. Still constrained? Ask support about dedicated endpoints or multi-account fan-out strategies.
How do I know if I'm hitting rate limits if my logs don't show 429 errors?
Check your queue depth and monitor response codes explicitly, not just request counts. Use APM tools or log aggregation to search for 429s across all services. If queue depth climbs or data flow pauses while requests look normal, you've got silent rate-limit failures, likely in background jobs.