The Developer Tools Podcast with Fexingo · 2026-06-30 · 9 min
Key moments - from our scoring
Substance score
65 / 100
Five dimensions, 20 points each
Rate limit headers are supposed to help developers avoid throttling, but they often create false confidence instead. Lucas and Luna dissect why X-RateLimit-Remaining is unreliable: fixed-window algorithms reset at arbitrary boundaries, the headers don't account for concurrent requests from other clients, and inconsistent implementation across GitHub, Twitter, Stripe, and other major APIs means developers can't rely on them universally. The 'remaining' counter doesn't tell you when the window resets - only X-RateLimit-Reset does - forcing developers to parse Unix timestamps and do time-zone math. For token bucket models, which some APIs now use, 'remaining' actually represents current tokens, but developers have learned to distrust it anyway. The pair advocate for either a standardized header set like RateLimit-Limit, RateLimit-Remaining, and RateLimit-Reset with a new RateLimit-Policy field describing the algorithm, or better use of the existing Retry-After header in 429 responses. They recommend API designers adopt token bucket algorithms with honest token counts and next-available-token timestamps, while developers should implement client-side throttling with exponential backoff rather than trusting headers blindly.
Fixed-window rate limiters decrement 'remaining' in real time but reset at arbitrary boundaries (e.g., on the hour). A client backing off when it sees 'remaining: 100' at 10:58 will suddenly have 5000 tokens available at 11:00, but has no visibility into when the reset happens - only the X-RateLimit-Reset Unix timestamp tells you that.
Each client sees a different 'remaining' value because the counter decrements across all users, but each client makes throttling decisions independently. This causes thundering herd effects where all clients stop at the same moment or all resume simultaneously after reset, defeating the purpose of distributed rate limiting.
Rate limit headers like X-RateLimit-Remaining are meant to help you avoid hitting the 429 limit proactively, while Retry-After is sent in a 429 response to tell you when retry is safe. Many APIs don't include Retry-After in 429s, leaving clients without guidance on how long to wait.
Token bucket models provide continuous refill (e.g., 10 tokens per second) so 'remaining' becomes an accurate, actionable number - if you see 'remaining: 1' and the bucket refills at 10 tokens/second, you know you can retry in 100 milliseconds rather than waiting until an arbitrary hour boundary.
The IETF draft proposes RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset, and RateLimit-Policy headers to describe whether the algorithm is fixed-window or token-bucket. For token buckets specifically, 'remaining' should reflect current tokens and 'reset' should indicate when the next token arrives, not the full refill time.
Our reviewer’s read on each dimension, with quotes from the episode.
The episode surfaces genuine technical problems with rate-limit header semantics (fixed vs. sliding windows, concurrent request issues, token bucket confusion) that a developer building API integrations would encounter. However, the discussion lacks depth on implementation patterns and edge cases; much of it rehearses known problems rather than proposing novel solutions beyond the standard IETF proposal.
The 'remaining' field in a token bucket is actually meaningful - it's the number of tokens left right now. But developers have been conditioned to mistrust it because of the fixed-window approach.
If you're building an integration that runs on many machines, each sees a different 'remaining' number, and they all make decisions independently.
The core observation - that rate-limit headers are semantically misleading - is valid but not novel; the fixed-window vs. token-bucket distinction and the concurrent-request thundering-herd problem are well-documented in API design circles. The suggested solutions (IETF draft, token bucket + accurate reset times, Retry-After) are standard proposals, not fresh thinking.
Maybe the solution is for the industry to converge on a standard set of headers that work for both fixed-window and token-bucket algorithms.
The current approach of 'remaining' that resets at arbitrary boundaries is worse than not having it, because it leads to false confidence.
Lucas and Luna are knowledgeable conversationalists with hands-on experience debugging rate-limit behavior across APIs (GitHub, Stripe, Twitter), but neither is presented as a senior infrastructure engineer or API platform operator who has *designed* these systems at scale. They discuss problems competently but speak as consumers, not builders of rate-limiting infrastructure.
I remember reading about GitHub's API rate limiting. They use a combination: unauthenticated requests get 60 per hour, authenticated get 5000.
I've seen that too. For example, the Stripe API returns a 'Request-Id' header...
The episode names real APIs (GitHub, Stripe, Twitter) and references actual header fields (X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset), with concrete examples like GitHub's 60/5000 split and Stripe's Request-Id header. However, it lacks quantified impact (e.g., how often do rate-limit misreads cause outages?) and doesn't cite data or case studies of real failures.
Take the classic set: X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset. They're used by GitHub, Twitter, Stripe, you name it.
unauthenticated requests get 60 per hour, authenticated get 5000.
Luna asks clarifying follow-ups (e.g., 'deliberately?', 'what about concurrent requests?') and builds on Lucas's points constructively, showing genuine engagement with the problem space. However, neither host pushes back on weak claims, challenges assumptions, or digs into implementation trade-offs. The conversation feels collegial but lacks the tension of genuine disagreement or probing.
Wait, you mean the headers are lying? Like, deliberately?
Right, the sliding window versus fixed window debate. I've seen that trip up teams.
Computed from the transcript - who did the talking, and the words that came up most.
In this episode of The Developer Tools Podcast, Lucas and Luna dive into a subtle but pervasive problem in API design: rate limit headers that lie. They break down how common headers like X-RateLimit-Remaining can actually mislead developers into building unreliable integrations, and why some teams are switching to a token-bucket model with accurate leak rates. Along the way, they examine GitHub's actual rate limit behavior and propose a better pattern for communicating throttling. Plus, a quick note on how listener support helps keep the show ad-free. #APIRateLimiting #RateLimitHeaders #DeveloperExperience #API #XRateLimitRemaining #GitHub #TokenBucket #APIThrottling #SoftwareEngineering #APIErrors #TechPodcast #BusinessAndTechnology #FexingoBusiness #BusinessPodcast #DeveloperTools #APIStandards #Reliability #APIHealth Keep every episode free: buymeacoffee.com/fexingo
Transcribed and scored by The B2B Podcast Index.
Lucas: There's this assumption that when an API tells you how many requests you have left, that number is honest. And it turns out, for a lot of popular APIs, it's actually not. Luna: Wait, you mean the headers are lying? Like, deliberately?
Lucas: Not maliciously, but structurally. Take the classic set: X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset. They're used by GitHub, Twitter, Stripe, you name it. But the way 'remaining' is calculated often has a time-window problem.
Luna: Right, the sliding window versus fixed window debate. I've seen that trip up teams. Lucas: Exactly. Many APIs implement a fixed window - say, 5000 requests per hour, reset on the hour.
And they decrement the 'remaining' counter in real time. So at 10:58, if you've used 4900, remaining says 100. But then the clock hits 11:00, and it resets to 5000. Your client, which was backing off because it saw 100 remaining, could slam the API immediately.
Luna: Which is actually fine, but the problem is that 'remaining' doesn't tell you when the window resets - only the Unix timestamp in the Reset header does. So developers have to manually compute whether hitting the limit is likely. Lucas: Right. And the bigger issue is that 'remaining' isn't a reliable predictor of future rate limit hits because it doesn't account for concurrent requests from other services or users.
If you're building an integration that runs on many machines, each sees a different 'remaining' number, and they all make decisions independently. Luna: So you get thundering herd problems. Every client sees 'remaining: 1' and they all stop - or all resume at the reset time. Lucas: Precisely.
And some APIs have started moving to a token bucket model, where you get a steady refill rate instead of a rigid window. But they still expose the same headers. The 'remaining' field in a token bucket is actually meaningful - it's the number of tokens left right now. But developers have been conditioned to mistrust it because of the fixed-window approach.
Luna: I remember reading about GitHub's API rate limiting. They use a combination: unauthenticated requests get 60 per hour, authenticated get 5000. But the 'remaining' header is accurate within that window. The issue is that the window is an hour, so if you blast through 4900 requests in the first ten minutes, you're stuck for 50 minutes.
Lucas: Right, and GitHub does include a 'X-RateLimit-Reset' header that gives you a Unix timestamp. So technically, you can compute exactly when you'll be unblocked. But think about the developer experience: you have to parse that timestamp, handle time zone math, and schedule a retry. That's a lot of boilerplate for something that should be simple.
Luna: And then there's the issue of rate limit headers being optional or inconsistent across endpoints. I've seen APIs that only include them on certain routes, so your client can't rely on them universally. Lucas: That's a whole other dimension. If you're building a generic SDK, you can't assume headers are present.
So you end up implementing your own local rate limiter as a fallback, which defeats the purpose of the server telling you what's allowed. Luna: It feels like we're at a point where the standard headers need an upgrade. Maybe something like a 'X-RateLimit-Policy' header that describes the algorithm and parameters in machine-readable format. Lucas: There have been proposals, like the IETF draft on rate limit headers.
That draft suggests headers like 'RateLimit-Limit', 'RateLimit-Remaining', and 'RateLimit-Reset', but also 'RateLimit-Policy' that describes the window type. But adoption has been slow. Most big APIs still use the old X-prefixed headers. Luna: And even if they adopt the new names, the semantics might still be confusing.
If a token bucket, 'remaining' is the current token count. But if the bucket refills at, say, 10 tokens per second, seeing 'remaining: 1' doesn't mean you'll be blocked for an hour - it means you'll have more tokens in 100 milliseconds. Lucas: Exactly. And that's where the 'Reset' header falls apart for token buckets.
The reset timestamp is usually the time when the token bucket fully refills, which could be minutes or hours away. But the developer just wants to know when they can make the next request. So they either poll aggressively or implement a crude sleep. Luna: There's actually a better pattern: return a 'Retry-After' header with seconds until the next request is allowed.
That's already a standard HTTP header, but many APIs don't use it for rate limiting - they use it for throttling after a 429 response. Lucas: Right. And that's a key distinction. The rate limit headers are meant to help you avoid hitting the 429, but the 429 response itself is where Retry-After is most useful.
However, some APIs don't even include Retry-After in 429s, so the client has no guidance on when to retry. Luna: Let's talk about what a good rate limit communication looks like. I think a combination of a token bucket model with a 'X-RateLimit-Remaining' that's accurate, plus a 'X-RateLimit-Reset' that indicates when the next token will be available - not the full refill time. Lucas: That would be a big improvement.
If the bucket refills at 10 tokens per second, then after you use a token, 'remaining' drops by one, and 'reset' gives you a timestamp 100 milliseconds from now. That's actionable. The client can schedule a retry precisely. Luna: And that's exactly the kind of thing that makes developer experience better.
You've got a clear, deterministic way to handle throttling without guessing. Lucas: You know, talking about building better developer tools like this, I think we have a nice example of how small design decisions have big downstream effects. And it's conversations like these that keep this show going. Luna: Yeah, exactly.
And for those of you who find these deep dives helpful, there's a simple way to support us. We keep the show free of ads, and listener support makes that possible. Lucas: If you'd like to chip in, you can at buy me a coffee dot com slash fexingo. That's B-U-Y M-E A c o f f e e dot com slash F-E-X-I-N-G-O.
No pressure, but it helps us keep doing episodes like this. Luna: Absolutely. And we're grateful for every bit of support. So back to rate limit headers - one thing I've seen work well is when APIs expose a 'X-RateLimit-Hint' header that suggests a safe polling interval.
Lucas: I've seen that too. For example, the Stripe API returns a 'Request-Id' header that you can use to correlate requests, but they don't have rate limit hints. GitHub, on the other hand, has a 'X-Poll-Interval' header for some endpoints. It's inconsistent.
Luna: Maybe the solution is for the industry to converge on a standard set of headers that work for both fixed-window and token-bucket algorithms. Something like: 'RateLimit-Limit', 'RateLimit-Remaining', 'RateLimit-Reset', and 'RateLimit-Refill'. Lucas: That would be comprehensive. But it also adds complexity.
If you're a simple API with a fixed 5000-per-hour limit, you don't need a refill rate. So maybe the headers should be optional and only present when relevant. Luna: Agreed. The key is that whatever you expose, it should be truthful and actionable.
The current approach of 'remaining' that resets at arbitrary boundaries is worse than not having it, because it leads to false confidence. Lucas: Let's wrap up with a concrete recommendation. If you're designing an API today, consider using a token bucket algorithm and expose the actual token count and the time until the next token. And always include Retry-After in your 429 responses.
Your developers will thank you. Luna: And if you're a developer consuming APIs, don't trust 'remaining' blindly. Understand how the rate limiter works, or implement your own client-side throttling with exponential backoff. Lucas: Good advice.
That's all for today. Thanks for listening.
Other episodes covering the same guests and topics, from across The B2B Podcast Index.