Skip to content
adapters.io

Stripe API rate limits explained: 100 requests per second, the read allocation nobody plans for, and the six different problems behind one 429

12 min read Integration The Adapters team

Last updated August 2026

Field mapping auto-plugged · tap a port to rewire

5 sample records ready

Stripe allows 100 requests per second per account in live mode and 25 in a sandbox, with most individual endpoints capped at 25. Those are the numbers everyone quotes. The reason Stripe integrations actually break is that six genuinely different problems all return HTTP 429, and the fixes for them contradict each other. Here are the current limits, read from Stripe documentation on 23 August 2026, how to tell the six apart in one header, and seven changes that fix them.

Key takeaways

  • 100 requests per second live, 25 in a sandbox. Scored per Stripe account across every endpoint, with individual endpoints capped at 25 unless documented otherwise.
  • Read the Stripe-Rate-Limited-Reason header first. Five documented values, and the remedy for a rate value is the wrong remedy for a concurrency value.
  • A 429 with no reason header is not a rate limit. It is a lock timeout on a single object, and the fix is serializing writes rather than slowing down.
  • Reads are allocated at 500 per transaction over 30 days. A ratio, not a rate, with a 10,000 monthly floor. This makes low-volume accounts the constrained ones.
  • Data, Reporting and Tax endpoints are excluded. Which is Stripe telling you where bulk analytical reads are supposed to go.

What are the Stripe API rate limits?

Stripe allows 100 requests per second per account in live mode and 25 per second in a sandbox. Individual endpoints are capped at 25 requests per second unless documented otherwise, and the Search API at 20 reads per second. Exceeding any of them returns HTTP 429 with a Stripe-Rate-Limited-Reason header naming which limit you hit.

The detail worth internalizing is that the global limit is scored per Stripe account, not per API key and not per integration. Your checkout, your dunning job, your nightly warehouse load and whatever your ops team wired up in Zapier all draw from the same 100 requests a second. A backfill that runs politely in isolation can throttle your live payment flow if you schedule it during business hours, and the error will surface in the part of the system you were not touching.

Stripe API limits at a glance

Stripe API rate limits, endpoint-specific limits and read request allocations, with the value and why each matters
Limit Value Detail
Global rate limit, live mode 100 requests/second Per Stripe account across every endpoint. Your checkout traffic and your backfill draw on the same budget
Global rate limit, sandbox 25 requests/second A quarter of live. Stripe explicitly discourages load testing in a sandbox because you will hit limits production would not have
Individual endpoint default 25 requests/second Unless documented otherwise. You can sit at 40 requests a second overall and still be throttled on one hot endpoint
Search API 20 reads/second Stripe points data-intensive analytical work at Sigma or Data Pipeline rather than at Search
Files API 20 read + 20 write/second Matters if you pull dispute evidence or scheduled report files on a cron
Payouts API 15 creates/second, 30 concurrent The concurrency figure is scored per business rather than per API key, so platforms reach it first
PaymentIntents updates 1,000 per object/hour Per individual PaymentIntent. A retry loop hammering one object exhausts this without touching the global limit
Subscription invoicing 10/minute, 20/day per sub New invoices per subscription. Billing migration scripts routinely trip the daily ceiling, not the per-minute one
Subscription quantity updates 200 per sub/hour Seat-based and usage-based billing that writes counts frequently has to batch them
Connect account creation 30/second live, 5/second sandbox Marketplace onboarding backfills need pacing that steady-state production never revealed
Read request allocation 500 per transaction Not a rate. An average over a rolling 30 day window, tied to how many transactions you process
Minimum read allocation 10,000 per month The floor every account gets regardless of transaction count. Small accounts live here
Write request allocation None Writes are still rate limited per second, but they carry no monthly allocation at all

One honest note on sourcing. Stripe publishes precise numbers for the per-second rate limits and for the read allocation, and it documents that concurrency limits exist as a separate mechanism, but it does not publish a concurrency number the way it publishes the 100 and the 25. We would rather say that plainly than pick a figure and present it as documented. Size your worker pool empirically, watch for the two concurrency reason codes in the next section, and treat the absence of a published number as a signal that the ceiling depends on how heavy your requests are.

Why is my Stripe integration returning 429 errors?

Read the Stripe-Rate-Limited-Reason header before changing anything, because five different problems produce the same status code and the fixes are opposites. Global rate means slow down overall. Endpoint concurrency means send fewer simultaneous requests to one endpoint. And a 429 with no such header is not a rate limit at all, it is a lock timeout on a single object.

This is the single most useful thing to know about the Stripe API, and it is why so much published advice about Stripe 429s is unhelpful. "Add exponential backoff" is correct advice for two of the six cases on the list below, roughly neutral for two more, and actively wrong for the read allocation, which never produces a 429 in the first place. Distinguishing them once, in your error handling, retires the whole class of bug.

The six problems that all look like one 429

The five documented Stripe rate limit reason values plus lock timeouts and the read allocation, with the signal, cause and correct fix for each
Reason What you see Actual cause The fix
global-rate 429 with that reason header Total requests per second across the account exceeded 100 in live mode Lower the overall request rate with a client-side token bucket, not just retries
endpoint-rate 429 on one endpoint while others are fine One endpoint exceeded its own per-second limit, usually 25 Pace that endpoint independently. A global slowdown wastes capacity elsewhere
global-concurrency 429 at a low request rate Too many requests in flight at the same moment, account wide Bound the worker pool. Sleeping between requests does not reduce concurrency
endpoint-concurrency 429 on list or expanded calls Too many simultaneous long-running requests against one endpoint Reduce expansion depth and page size before you reduce request rate
resource-specific 429 across several endpoints of one resource A limit spanning a whole resource, such as subscription create and update together Treat the resource as one shared budget rather than per endpoint
No reason header 429 with code lock_timeout Not a rate limit. Another request or Stripe process holds a lock on that object Serialize writes to the same object. Stripe SDKs already auto-retry this case
Read allocation No 429. Contact from Stripe instead Averaging more than 500 GET requests per transaction over 30 days Stop polling for what webhooks report, and move bulk reads to Data Pipeline or Sigma

The last row is the one that never surfaces as an incident, because it does not throw. The read allocation is a thirty day average rather than an instantaneous limit, so an integration can drift over it for weeks while every request returns 200. You find out because Stripe contacts you, not because your monitoring caught it. That makes it worth instrumenting deliberately: count your GET requests, count your transactions, and watch the ratio against 500.

Does Stripe limit how many times you can read the API?

Yes, and it is not a rate limit. Stripe allocates read requests as a ratio to transaction count: an average of 500 GET requests per transaction over a rolling 30 day period, with a floor of 10,000 read requests a month for every account. Write requests have no allocation. Data, Reporting and Tax product endpoints are excluded from the calculation.

Work the arithmetic and the surprising part appears. An account processing 100 transactions in thirty days gets 50,000 reads. An account processing 100,000 transactions gets fifty million. The constraint scales with your business, which means the accounts most likely to breach it are the small ones running a chatty integration, not the large ones. Every team we have seen reason about this assumed the opposite, and built a polling loop sized for a volume they had not reached yet.

Connect platforms get a separate treatment worth knowing about. Each connected account has its own 500-per-transaction allocation for requests it initiates, and the platform has a distinct allocation for requests it makes on behalf of connected accounts, calculated against the aggregate transaction count across them. A marketplace that reads every connected account's data from platform credentials is drawing on one pooled budget, not on each seller's individual one.

How do you avoid Stripe rate limits?

Seven changes, in the order we would make them. The first two cost an afternoon and resolve most of what teams describe as a Stripe throttling problem.

Fix 01

Read the reason header before you write a single line of retry logic

Stripe returns a Stripe-Rate-Limited-Reason header on every genuine rate limit, with five documented values, and the correct response differs by value. Two of them are rate problems that exponential backoff solves. Two are concurrency problems that backoff barely touches, because pausing between requests does not change how many are in flight at once. One is a resource-wide budget. Log the header value next to the status code and you turn a recurring guessing game into a lookup.

Fix 02

Treat a 429 with no reason header as a completely different bug

If a 429 arrives without that header it is not rate limiting at all. It is code lock_timeout, meaning another API request or an internal Stripe process is holding a lock on the object you tried to touch, and your request timed out waiting for it. Stripe does not even assign these a request ID. The fix is to serialize mutations against the same object rather than firing them concurrently. Concurrent work against different objects is fine, which is the distinction that makes this tractable.

Fix 03

Bound your worker pool, because rate and concurrency are separate budgets

Concurrency limits count requests in progress at a given instant, not requests per second, and they reset on completion rather than on a clock tick. List calls and calls using expansions are the usual culprits because they run long and hold a slot the whole time. A pool of fifty threads each making one slow list request is fifty in-flight requests, even though the request rate is trivial. Cap the pool, and cap it lower for anything that expands nested objects.

Fix 04

Stop polling for things webhooks would have told you

The read allocation is the limit nobody plans for, because it is not a rate. Stripe allows an average of 500 GET requests per transaction over a rolling thirty days, with a floor of 10,000 reads a month. Process 200 transactions and you have 100,000 reads. An integration that walks every customer hourly to check for changes can spend that on discovering nothing happened. Webhooks push the change to you and cost nothing against the allocation. You still fetch the changed object, which is a fraction of the traffic.

Fix 05

Move bulk analytical reads off the API entirely

Stripe excludes Data, Reporting and Tax product endpoints from the read allocation, which is a strong hint about intended usage. If the job is analytics, Data Pipeline shares your full dataset into Snowflake, Redshift, Databricks or BigQuery, and Sigma runs SQL over it inside the Dashboard. Neither touches your read budget. Paging the Search API to build a monthly revenue report is the expensive way to do something Stripe would rather you did another way.

Fix 06

Filter and expand deliberately rather than pulling and discarding

List endpoints page, and Stripe tells you plainly to apply filters so you page less. Every unfiltered page you fetch and then throw away is a read you paid for out of a thirty day allocation. Expansions compound the problem in the other direction: they save you a round trip but make each request slower and more resource-hungry, which is exactly what pushes you into the concurrency limiter. Expand what you will definitely use and no more.

Fix 07

Back off exponentially with jitter, and never on a fixed interval

Stripe recommends exponential backoff with randomness added, for a specific reason: a fleet of workers that all back off by the same amount retries in unison and produces a thundering herd that recreates the throttle. Add jitter so the retries spread. Above that, the more sophisticated pattern Stripe suggests is a client-side token bucket that governs traffic globally and throttles itself back when it detects sustained limiting, rather than each worker discovering the wall independently.

Should you load test against a Stripe sandbox?

Stripe says no, and gives two reasons that are easy to verify. Sandbox limits are lower than live limits, 25 requests a second against 100, so a load test will hit walls that production would never have shown you and you will tune against the wrong numbers. And a sandbox mocks the payment gateway call, so the latency profile is nothing like live.

The recommended alternative is to build a mocking layer into your own integration that you can switch on for load tests, with simulated latency sampled from real live-mode call durations. That is more work than pointing a load generator at a sandbox, and it is the only version of the exercise that produces a number you can act on. If you are preparing for a specific event such as a flash sale and think you might exceed the published limits, Stripe's documented path is to contact support in advance rather than to discover it live.

How often does Stripe Data Pipeline refresh?

Stripe shares a full load of your data on a three hour cadence, so batches close at 00:00, 03:00, 06:00 UTC and onward. That is the cadence, not the freshness. Stripe documents a typical delivery of around three hours after a batch closes, which puts P50 freshness near six hours from event creation. Plan reporting around six hours, not three.

Stripe's own freshness documentation works the example: a record created at 00:01 UTC goes into the batch that closes at 03:00, and with typical delivery it is queryable by 06:00. Comparison articles routinely quote the three hour cadence as though it were the data age, which is how a finance team ends up promising a 9 AM dashboard that cannot exist. Stripe also ships a data_load_times table inside the share whose LOADED column tells you when each table last actually updated, so you can assert freshness in a query instead of assuming it.

Data Pipeline is genuinely the lowest-effort route into Snowflake, Amazon Redshift, Databricks or BigQuery, and it does not consume your read allocation. It also runs one direction only and gives you Stripe's schema rather than yours. Where those trade-offs land for a specific destination is worked through in Stripe to Snowflake and Stripe to BigQuery, and the full vendor comparison is on Stripe integration tools.

What breaks in a Stripe sync that is not a rate limit at all

Two things, and both cost more real money than throttling does. The first is currency handling. Stripe reports amounts as integers in the smallest currency unit, so 42.50 US dollars arrives as 4250, except in zero-decimal currencies such as JPY and KRW where the integer is already the full amount. A connector with a hardcoded divide by 100 books Japanese revenue at one percent of its value, silently, forever.

The second is reconciling on the wrong object. Charges look like the natural source of truth and they are not: they exclude fees, refunds, disputes and adjustments, and a payout never corresponds to a single day of charges. Balance transactions are the only place gross, fee and net appear together against the transfer they settled into. Sum the net of every balance transaction inside a payout and it equals the payout. Sum charges and it will not, which is the origin of most unexplained month-end variances on Stripe. If the destination is a ledger rather than a warehouse, the mapping is worked through on Stripe to QuickBooks. Teams that need the underlying processor statements checked against the ledger as well usually end up turning the bank statement itself into structured rows before they can close the loop.

Where to start if the sync is already failing

Log the reason header, then look at what it says. If it is a rate value, you have a pacing problem and a token bucket fixes it. If it is a concurrency value, you have a pool-size problem and slowing down will not help. If there is no header, you have a lock contention problem and the fix is to stop writing to the same object from multiple workers. If you are seeing no 429s at all but Stripe has been in touch about usage, you have a read allocation problem and the answer is webhooks plus Data Pipeline rather than a faster poller.

Whichever of those it is, the structural fix is the same: separate the three budgets in your own code so you can reason about them independently. Rate is requests per second. Concurrency is requests in flight. Allocation is reads per transaction over thirty days. A pipeline that instruments all three tells you which wall you hit before anyone has to open a debugger. The wider question of whether to run that yourself is covered in build versus buy for integrations, and the polling decision specifically in webhooks versus polling.

Sync Stripe without fighting the throttle

Bounded concurrency, paced requests, webhook-driven change detection and per-record error logs you can actually read. Flat from $49 a month, with no per-record meter.

Try the live demo

No credit card required.