Fix AWS Lambda 429 TooManyRequestsException: Concurrency Math Explained

Logeshwaran.C

If your Lambda function is throwing TooManyRequestsException: Rate exceeded (HTTP 429), it means Lambda blocked the request because letting it through would have pushed you past a concurrency limit — not because something is "broken." Start by comparing the ConcurrentExecutions and Throttles metrics in CloudWatch against your account's default limit of 1,000 concurrent executions per Region. Here's the part almost nobody expects: a function can throw this error on its very first invocation, with zero real traffic, if its reserved concurrency was ever set to 0 — by you, by a teammate, or by a deploy script three months ago that nobody remembers writing.

⚡ Quick Answer

Check first → CloudWatch metrics ConcurrentExecutions (Maximum) and Throttles (Sum) for your function and Region

Check the kill switch → a function's Reserved Concurrency setting, especially if it's been quietly set to 0

Check the shared pool → the account-wide default of 1,000 concurrent executions per Region, shared by every function you have

If none of those explain it, the throttle is probably coming from an API your function calls, not from Lambda itself — jump to Cause 4. For the actual math behind all of this, see the concurrency formula.

Jake found out about this the expensive way. His phone shop runs a tiny Lambda function behind a "text me when my repair is ready" button on his site. On the Saturday before a local college's graduation weekend, forty customers hit that button inside about ninety seconds — and half of them got nothing. No text, no error page, just silence. He assumed his code had a bug. It didn't. His account had quietly been throttling him the entire time, and he'd never once looked at a CloudWatch graph to know it.

This post walks through exactly what causes a 429 from Lambda, the actual arithmetic AWS uses to decide whether to throttle you, and — in order from "free and takes two minutes" to "requires a support ticket and patience" — every real fix. It also covers the cases most articles skip entirely: what happens once you leave the default zip-file function and start using VPCs and container images, how to check and fix this from code instead of the console, and what to do when you've done everything right and you're still getting throttled because the bottleneck was never Lambda in the first place.

What TooManyRequestsException Actually Means

Concurrency is a term for how many copies of your function are running at the exact same moment — think of it like how many cash registers Jake has open at once. One register can only ring up one customer at a time; if fifteen customers show up and he only has five registers staffed, ten of them wait in line. Lambda works the same way: it calls each running copy of your function an execution environment. When a new request comes in, Lambda either reuses an idle execution environment or spins up a brand-new one.

Every AWS account has a limit on how many execution environments can exist at once, in a given AWS Region, across every single function you own. When a new request would push you past that ceiling — or past a smaller limit you set yourself — Lambda doesn't queue the request and make it wait. For synchronous calls, it immediately rejects it with HTTP status code 429 and the error name TooManyRequestsException, with the message text Rate exceeded (sometimes shown as Request throughput limit exceeded depending on which API surface reports it). Nothing ran. No partial execution, no half-finished database write from that specific call — the request was turned away at the door.

🙋‍♂️ Jake's Reality Check

"Wait — so my function code was fine the whole time? It never even ran?"

Correct. A 429 means Lambda refused to start your function at all. If you're staring at your handler code looking for a bug, you're looking in the wrong place — the problem lives in your account's concurrency settings, not your code.

That distinction matters because it changes where you go to fix it. A bug in your code produces an error from inside a running function — a stack trace, an exception, a timeout. A throttle happens before your function code ever executes. You'll never see it in application logs that only log what your handler does, which is exactly why so many people spend hours reading their own source code for a problem that isn't there.

Which of These Four Things Is Actually Throttling You

There isn't one single "Lambda throttling" bug. There are four separate, mechanically different limits that all happen to produce the exact same error message. Diagnose the wrong one and you'll waste an afternoon fixing a limit that was never the problem.

Cause What's actually maxed out Tell-tale sign
1. Account-wide limit Your Region's default 1,000-execution pool, shared by every function ConcurrentExecutions across ALL functions sits near 1,000
2. Reserved concurrency A per-function cap you (or someone) set explicitly, possibly 0 Only THIS function throttles; others in the account are fine
3. Burst scaling rate How fast NEW execution environments can be created Throttling spikes for seconds, even though total concurrency is nowhere near your limit
4. Downstream API Something your function calls — DynamoDB, an external API, a database pool Zero data points on the Throttles metric, yet you still see errors

The fastest way to tell them apart is exactly what AWS's own support engineers check first: open CloudWatch, pull up the Throttles metric with the Sum statistic for your function, and the ConcurrentExecutions metric with the Maximum statistic. If Throttles has data points, Lambda itself throttled you — go figure out which of causes 1 through 3 it was. If Throttles is empty but you're still seeing failures, the rejection is happening one layer down, inside your function's own code, at cause 4.

The Concurrency Math AWS Wants You To Do

Before you touch a single setting, you need one formula. AWS's own documentation gives it plainly:

Concurrency = (average requests per second) × (average request duration in seconds)

🕐 Ethan walks Jake through it

"Think of it like this," Ethan said, sketching on a napkin. "If your function takes one full second to run, and 100 people hit it in the same second, you need 100 registers open — 100 concurrency. But if it only takes half a second, those same 100 people only need 50 registers, because each register finishes and picks up a second customer before the second is over. Duration is the whole game. Shave your function's runtime in half, and you've effectively doubled your concurrency headroom without asking AWS for anything."

Run the numbers yourself: at 100 requests/second with a 1-second average duration, concurrency is 100. Cut duration to 500 ms and concurrency drops to 50 for that same 100 requests/second. Double the traffic to 200 requests/second at 250 ms each, and concurrency is still 50 — the math doesn't care whether the pressure comes from more requests or from each one taking longer; it only cares about the product of the two.

Here's where people get tripped up: concurrency and requests-per-second are not the same number, and Lambda enforces limits on both. A function with an average duration under 100 milliseconds is the classic trap. Take a function that runs in 50 ms and gets hit with 20,000 requests/second. By the formula, concurrency is only 20,000 × 0.05 = 1,000 — right at the default account limit, so it looks fine on paper. But Lambda also enforces a hard cap of 10 requests per second, per execution environment, for synchronous calls — meaning the total invocation ceiling is 10 times your concurrency limit, or 10,000 requests/second at the default. That 20,000 requests/second workload gets throttled at 10,000, with half its traffic rejected, even though the "concurrency" side of the ledger looked completely healthy.

⚠️ What this actually breaks

If your function's average duration is under ~100 ms, raising your concurrency quota alone will not fix a throttling problem caused by high requests-per-second. You need to raise the concurrency quota and understand that the requests-per-second ceiling moves with it — it's always 10x whatever your concurrency number is, at both the account level and the function level.

Cause 1: You Hit the Account-Wide 1,000 Limit

By default, every AWS account gets a total concurrency limit of 1,000 concurrent executions per Region — and that pool is shared across every function you have deployed in that Region, not per function. If you've got a nightly batch job, three API endpoints, and a Slack bot all running as separate Lambda functions in us-east-1, they're all drawing from the same 1,000-unit bucket. A traffic spike on one can throttle a completely unrelated function that happens to live in the same account and Region.

You can check your current account-level number yourself with one CLI command:

aws lambda get-account-settings

The response includes a block that looks like this:

{
    "AccountLimit": {
        "ConcurrentExecutions": 1000,
        "UnreservedConcurrentExecutions": 900
    }
}

ConcurrentExecutions is your total account-level ceiling. UnreservedConcurrentExecutions is what's left over for functions that haven't been given their own reserved slice — which, by design, Lambda never lets drop below 100, even if every other function in the account has reserved concurrency configured. That floor of 100 exists specifically so a brand-new, unconfigured function always has somewhere to run.

Cause 2: Reserved Concurrency Is Working Exactly As Configured

Reserved concurrency sets both a floor and a ceiling for one specific function. Set it to 50, and that function can never use more than 50 execution environments — even if the rest of the account is sitting nearly idle — but it also can never be starved below 50 by some other function's traffic spike. Nobody else can borrow that capacity, and your function can't borrow anyone else's.

Here's the part that catches people out: setting reserved concurrency to 0 is a documented, intentional way to fully stop a function from running. AWS's own console literally offers it as a kill switch — "To intentionally throttle a function, set its reserved concurrency to 0. This stops your function from processing any events until you remove the limit." If a teammate did this during an incident last quarter to stop a runaway function, and nobody ever set it back, that function will throttle 100% of its invocations forever, with a completely healthy account otherwise. This is the single most common cause of "why is only THIS ONE function throttling, when I have plenty of account concurrency left over."

You can check and fix it with two commands:

aws lambda get-function-concurrency --function-name my-function

aws lambda put-function-concurrency --function-name my-function \
    --reserved-concurrent-executions 100

There's also a math ceiling here worth knowing: you can reserve up to your account's unreserved concurrency value minus 100. AWS keeps that 100-unit buffer reserved for functions that don't have their own explicit setting, so you can never accidentally reserve all 1,000 units to a single function and leave nothing for anything else in the account.

✅ Why this is the one to check first

Checking reserved concurrency takes ten seconds and costs nothing, versus opening a support ticket for a quota increase that can take days. If only one function is throttling and the rest of your account looks healthy, this is almost always where the answer lives.

Cause 3: You're Scaling Faster Than the Burst Rate Allows

Even if your total account concurrency limit is nowhere near being reached, Lambda still caps how quickly it will create brand-new execution environments in response to a sudden spike. This is separate from the 1,000-unit account ceiling — it's about the rate of climb, not the total altitude.

For each function, in each AWS Region, the concurrency scaling rate is 1,000 new execution environment instances every 10 seconds — or 10,000 additional requests per second every 10 seconds, whichever limit you'd hit first. Lambda doesn't bank unused capacity either: if you use none of that allowance in one 10-second window, you don't get 2,000 the next window. It resets to 1,000 every time. Importantly, this scaling rate applies per function, so one function ramping up hard doesn't eat into another function's ability to scale.

Jake's Saturday-graduation spike is a textbook case: forty requests arrived within about two seconds after being essentially idle. Lambda had to provision execution environments from scratch, and while 40 is nowhere close to 1,000, a handful of those requests landed in the same fraction of a second where Lambda hadn't finished spinning up new environments yet — enough to throttle a few unlucky ones.

🕐 What changed between versions

  • Before: functions initially scaled at an account-wide rate of only 500 to 3,000 concurrent executions in the first minute (depending on Region), then just 500 more per minute after that — and this rate was shared across every function in the account, so one busy function could slow another's ability to scale.
  • Now: each function scales independently, by up to 1,000 execution environment instances every 10 seconds, regardless of what any other function in the account is doing.
  • What that means for you: a "noisy neighbor" function spiking in traffic no longer throttles an unrelated function's ability to scale — but the 1,000-per-10-seconds ceiling still applies to each function individually, so an extremely sudden, extremely large spike on one function can still outrun it.

You'll recognize this pattern on a CloudWatch graph by zooming into a 1-minute window: you'll see a stair-step pattern in ConcurrentExecutions climbing in chunks, with a corresponding spike in Throttles right at each step, even though the overall concurrency number never gets anywhere close to your account limit.

Cause 4: It's Not Lambda At All

Here's the popular advice that's frequently wrong: "you're getting TooManyRequestsException, so raise your Lambda concurrency limit." Sometimes that's correct. Often it isn't — because the throttling error you're seeing came from a completely different AWS service that your Lambda function calls, wrapped in a similar-looking exception, and no amount of Lambda concurrency will touch it.

The tell is exactly what we flagged in the diagnosis table: check the Throttles CloudWatch metric for the Lambda function itself. If it shows zero data points during the window when your errors occurred, Lambda never throttled anything — your function started, ran, and then made an API call (to DynamoDB, to a third-party REST API, to another Lambda function, to a database connection pool) that got rejected for its own, separate reason. Your function's own error handling or SDK is what's surfacing that as a "TooManyRequestsException"-shaped error.

⚠️ What raising your limit won't fix

If your Lambda function calls a database that caps out at 100 simultaneous connections, setting your Lambda concurrency limit to 5,000 doesn't create more database connections — it creates 5,000 functions competing for the same 100 slots, which usually makes the downstream throttling worse, not better, because more of your invocations are now racing for the same scarce resource at once.

The fix here is exponential backoff with jitter inside your function code: when a downstream call fails with a throttling-style error, wait a randomized, increasing amount of time before retrying instead of hammering it again immediately. AWS's own guidance is explicit that this is the correct pattern for exactly this scenario, and it also recommends spreading out any scheduled or batch-triggered calls over time rather than firing them all at once, so you don't create a self-inflicted spike.

The Fixes, Cheapest to Most Drastic

Work down this list in order. Most people fix this at step 2 or 3 and never need to go further.

  1. Check reserved concurrency isn't set to 0. Free, takes ten seconds, run get-function-concurrency for the affected function.
  2. Confirm the throttle is really from Lambda, not downstream. Free, prevents you from raising the wrong limit and wasting a support ticket.
  3. Add exponential backoff with jitter around any downstream API calls in your function code. Free, and it's the correct fix if Cause 4 applies — it will not fix a genuine Lambda-side throttle, but it should exist in production code either way.
  4. Shorten your function's average duration. Free, and by the concurrency formula, this directly reduces how much concurrency the same traffic requires — trim unnecessary work, reuse connections outside the handler, avoid unnecessary cold-start-heavy dependencies.
  5. Configure reserved concurrency deliberately, not as a leftover 0. Free, protects a critical function from being starved by noisy neighbors elsewhere in the account, and protects downstream dependencies from being overwhelmed by capping how far this specific function can scale.
  6. Add a dead-letter queue for asynchronous invocation sources. Free to configure (though the destination queue itself may have its own small cost), and it stops throttled or repeatedly failing async events from silently vanishing.
  7. Use provisioned concurrency for latency-sensitive, spiky traffic. Costs money, since you're paying to keep environments pre-warmed, but it eliminates cold-start-driven throttling for predictable spikes.
  8. Request an account-level concurrency quota increase. Free to request, but takes time to be approved and doesn't fix a downstream bottleneck or a reserved-concurrency-0 misconfiguration.

Notice the order: the free code-and-configuration fixes come before the "ask AWS for more" step, because a quota increase on a function that's throttling due to reserved concurrency set to 0, or due to a downstream database's own connection limit, changes nothing at all.

Reserved vs. Provisioned Concurrency vs. a Quota Increase

These three get confused constantly because they all sound like "give my function more room," but they solve different problems.

Setting What it does Costs extra? Fixes cold starts?
Reserved concurrency Sets a fixed floor/ceiling for one function, carved out of the account pool No No — environments still start cold on demand
Provisioned concurrency Pre-initializes a set number of environments so they respond instantly Yes Yes, up to the provisioned amount
Account quota increase Raises the total 1,000-unit shared pool for the whole Region No (self-service request) No

Provisioned concurrency has its own quirk worth knowing before you rely on it: it doesn't come online the instant you configure it. Lambda takes roughly a minute or two to finish allocating it, at a rate of up to 6,000 execution environments per minute per function. If you request 5,000 units of provisioned concurrency for a flash-sale launch that starts in thirty seconds, none of that capacity exists yet when the sale opens — schedule it ahead of time.

Also worth knowing: if the provisioned concurrency you've set for a function's versions and aliases adds up to that function's reserved concurrency, every invocation runs on provisioned concurrency — but it also throttles the unpublished $LATEST version entirely, since you can't allocate more provisioned concurrency than the reserved amount allows.

SQS, S3, and Other Event Sources: Throttling Behaves Differently

Everything above assumes a synchronous invocation — something calling your function and waiting on the response, like an API Gateway request. Asynchronous invocations, and functions triggered by an event source like SQS, S3, or EventBridge, behave differently in one important way.

Lambda automatically retries a failed asynchronous invocation up to two times, without you writing any retry code. That's a real safety net, but it isn't unlimited: if a function has genuinely insufficient capacity to work through a backlog, events can sit queued for hours before Lambda gets around to them — and if they're still unprocessed when they age out, they're simply dropped, unless you've configured somewhere for them to land. That's exactly what a dead-letter queue is for: attach one to the function's asynchronous invocation configuration, and any event that gets discarded after repeated throttles or failures lands there instead of vanishing, so you can inspect and reprocess it later.

⚠️ What this actually breaks

For Amazon SQS specifically, you cannot attach a dead-letter queue to the Lambda function's async config for this purpose — the dead-letter queue has to be configured on the SQS queue itself. Configuring it in the wrong place is a common reason people think they've protected against data loss when they haven't.

One more edge case worth flagging by name because almost nobody documents it: if your Lambda function is triggered by an Amazon MQ event source mapping, there's a hardcoded default maximum concurrency that reserved and provisioned concurrency settings cannot override — five concurrent instances for Apache ActiveMQ, and just one for RabbitMQ. If you're wondering why your MQ-triggered function refuses to scale past a tiny number no matter what you set, that's why; you have to contact AWS Support directly to request a higher default for that specific event source type.

VPCs, Container Images, and Other Edge Cases That Change the Math

Everything so far assumes a plain function with no networking wrinkles. Attach it to a VPC, package it as a container image, or run it with an unusually long duration, and a fifth cause enters the picture — one that looks exactly like a concurrency throttle but has a completely different root fix.

VPC-attached functions and the ENI ceiling

When a Lambda function needs to reach resources inside a VPC — an RDS database, an ElastiCache cluster, an internal service — Lambda creates network interfaces (Hyperplane ENIs) to make that connection. Elastic network interface quotas apply per VPC, regardless of Region, and the default is 500 — a quota shared with other services like Amazon EFS in that same VPC. If you're running several concurrency-hungry, VPC-attached functions in the same VPC, you can hit the ENI ceiling before you ever get near your 1,000-execution Lambda concurrency limit, and scaling will stall in a way that looks identical to a plain throttle in your application logs but shows up as a VPC networking limit instead.

Container image functions and cold starts

Functions packaged as container images are stored in Amazon ECR rather than counted against Lambda's own zip-file storage quota, and the maximum uncompressed image size, including all layers, is 10 GB. A larger image generally means a longer cold-start Init phase, and a longer Init phase adds directly to your function's average duration — which, by the concurrency formula from earlier, means the exact same request volume now needs more concurrency to serve without throttling. If you migrated a function from a .zip archive to a container image and suddenly started seeing more throttling at the same traffic level, check whether your image size grew along with it.

Long-running and Managed Instances functions

Standard Lambda functions can run for up to 15 minutes (900 seconds) per invocation. For functions using AWS Lambda Managed Instances — invoked asynchronously or through most event source mappings, with the exception of Amazon MQ and Amazon DocumentDB — that maximum extends to 90 minutes (5,400 seconds). A function that legitimately needs to run for tens of minutes consumes one full unit of concurrency for that entire duration, which means a handful of these long-running invocations can eat a surprisingly large slice of your account's 1,000-unit pool compared to a fleet of sub-second functions doing similar total work. If a batch-processing function is holding concurrency for a long time, reserved concurrency on it specifically prevents it from crowding out faster, latency-sensitive functions sharing the same account.

Automating Concurrency Checks Instead of Clicking Through the Console

If you're managing more than a handful of functions, checking reserved concurrency one function at a time in the console doesn't scale. Everything shown earlier as a CLI command — get-account-settings, get-function-concurrency, put-function-concurrency, and delete-function-concurrency to remove a reserved setting entirely — can be scripted and run against every function in an account in a loop, so a misconfigured 0 doesn't sit unnoticed for months the way it did in Jake's case.

For infrastructure defined as code rather than clicked through the console, reserved concurrency is a first-class, declarative setting rather than something bolted on afterward. In AWS CloudFormation, the AWS::Lambda::Function resource exposes a ReservedConcurrentExecutions property directly on the function definition — set it once in your template, and every deploy carries the same intentional value forward instead of depending on someone remembering to click "Edit" in the console after the fact. That single line is often the actual fix for the "someone set it to 0 during an incident and forgot" scenario: once it's in source control, a stray console change gets reverted the next time the stack deploys, rather than lingering silently.

✅ Why this is worth the setup time

A console change to reserved concurrency has no audit trail beyond CloudTrail logs you have to go looking for. A CloudFormation-managed value shows up as a diff in your next pull request. If your team has ever asked "wait, who set this to 0?" and gotten no answer, that's the argument for moving it into your templates.

Third-Party Monitoring Tools: When They Help and When Not to Bother

Plenty of observability vendors sell dashboards that layer on top of Lambda's CloudWatch metrics, adding nicer graphs, cross-account rollups, and Slack alerting. For a team already paying for one of those tools to watch other services, wiring in ConcurrentExecutions and Throttles alongside everything else is a reasonable use of that existing subscription.

But it's worth being honest about what those tools actually add here: they're a nicer window onto the same two CloudWatch metrics this entire post has been pointing at. If you're running a small number of functions and don't already have a third-party observability platform in place, a native CloudWatch alarm on Throttles — configured directly against the account, at no extra cost beyond the alarm itself — solves the "find out before a customer does" problem just as well, without adding a new vendor, a new bill, or a new place to check during an incident. Reach for a third-party dashboard when you're already consolidating many services into one view; don't add one solely to watch this specific error.

Who Can Even Change These Settings: The Access-Control Angle

Before you fix a reserved-concurrency-0 incident, it's worth asking who's able to cause the next one. Reserved concurrency is changed through the Lambda API operations PutFunctionConcurrency, GetFunctionConcurrency, and DeleteFunctionConcurrency — and whoever holds the matching IAM permissions for those actions can silently throttle a production function to zero at any time, console or CLI, with no code deploy involved. That's a meaningfully different blast radius than a typical code change, because it bypasses code review entirely.

If your team hasn't looked at who holds those specific permissions, it's a five-minute check worth doing: broad, account-wide Lambda administrative access almost always includes them by default, even for engineers who were only granted that role to deploy code, not to individually reconfigure production concurrency. Narrowing who can call PutFunctionConcurrency directly reduces how often "someone set it to 0 during an incident and forgot" can happen at all — which pairs naturally with the CloudFormation approach above, since a team without console write access to this setting can only change it through a reviewed template change.

The Requests-Per-Second Ceiling, Explained One More Way

It's worth restating this because it's the single most common reason a raised concurrency quota doesn't actually fix anything: at both the account level and the function level, Lambda enforces a requests-per-second limit equal to 10 times the corresponding concurrency quota. This isn't a separate, unrelated number you also have to track — it's mechanically tied to whatever concurrency figure you're already working with.

🙋‍♂️ Jake's Reality Check

"So if I ask AWS to bump my concurrency to 2,000, does my requests-per-second limit go up automatically too?"

Yes. Doubling your concurrency quota from 1,000 to 2,000 also doubles your total invocation ceiling from 10,000 requests/second to 20,000, since the second number is always calculated as 10x the first. You don't request them separately, and you don't need to.

It's important to note what this doesn't mean, too. It's incorrect to conclude that any single execution environment can only handle 10 requests per second on its own — Lambda isn't measuring per-environment throughput at all. It only tracks two totals across your entire function or account: overall concurrency, and overall requests per second. The "10" multiplier connects those two totals to each other; it doesn't describe any individual instance's speed.

For asynchronous invocations, this particular ceiling disappears — each execution environment can serve an unlimited number of asynchronous requests, so the total invocation limit for async traffic is governed purely by available concurrency, not by a separate requests-per-second cap. That's one more reason the fix for a throttling problem depends heavily on whether your function is being called synchronously or asynchronously in the first place.

How to Actually Raise Your Limits

If you've worked through the diagnosis above and genuinely need more room — not a misconfiguration, an actual sustained traffic level above 1,000 concurrent executions — here's the process.

  1. Open the Service Quotas console for AWS Lambda in the affected Region.
  2. Find the "Concurrent executions" quota and check your current applied value against your recent peak usage (visible via the ConcurrentExecutions CloudWatch metric).
  3. Submit a quota increase request specifying the new value you need. AWS's documentation is candid that this quota can be raised into the tens of thousands for accounts that demonstrate real, sustained need.
  4. While you wait, mitigate with reserved concurrency on your most critical functions, so a temporary spike elsewhere in the account doesn't starve the function that actually matters.
  5. Once approved, re-check UnreservedConcurrentExecutions via get-account-settings to confirm the new pool is actually available before assuming the fix has taken effect.

Worth knowing before you request an increase at all: this quota is a soft limit, meaning it's explicitly designed to be raised on request — unlike some other Lambda quotas, such as the per-invocation payload size or the API request rate limits for calls like GetFunction, which are hard-capped and cannot be increased no matter what you ask for.

One mismatch AWS's own documentation calls out by name: if your Lambda function sits behind API Gateway, API Gateway's own default throttle is 10,000 requests per second, while Lambda's default concurrency limit only supports 10,000 requests per second at the maximum possible efficiency (1,000 concurrency × the 10x multiplier) — and that's only if your average duration is short enough to hit that ceiling. In practice, most real workloads need a higher Lambda concurrency limit than the API Gateway default alone would suggest, specifically because of this arithmetic.

New AWS Accounts and the Quiet Starter Limits

If you spun up a fresh AWS account recently and hit throttling almost immediately, at traffic levels nowhere near 1,000 concurrent executions, this is likely why: AWS explicitly documents that new accounts start with reduced concurrency and memory quotas compared to established ones, and raises them automatically over time based on your account's usage. It's a fraud- and abuse-prevention guardrail, not a bug, and not something you did wrong.

You don't need to guess whether this applies to you — run aws lambda get-account-settings and look at the actual ConcurrentExecutions value it returns. If it's meaningfully below 1,000, your account is on a reduced starter quota, and the fix is either to wait for it to rise naturally with continued legitimate usage, or to request an increase through Service Quotas the same way an established account would.

Monitoring So This Doesn't Happen Again

The single most useful habit here is checking whether your peak ConcurrentExecutions is getting uncomfortably close to your account-level quota before a spike, not after one. Set a CloudWatch alarm on ConcurrentExecutions at, say, 80% of your current quota, so you get warned while there's still time to request an increase or add reserved concurrency, rather than finding out from a customer's angry email.

Also watch Duration alongside memory usage in your execution logs. If the "Max Memory Used" field is consistently close to your configured memory setting, your function is memory-bound, which usually means it's running slower than it needs to — and per the formula above, slower duration directly inflates the concurrency your traffic requires, even if request volume never changes at all.

Load testing before a known traffic event — a product launch, a marketing push, Jake's graduation weekend — is the only reliable way to find out where your actual ceiling is before real customers do. AWS's own guidance frames this plainly: identify the limiting quota during a controlled test, then act on it, rather than discovering it live.

Frequently Asked Questions

What does TooManyRequestsException actually mean in Lambda?

It means Lambda refused to run your function because doing so would have exceeded a concurrency-related limit — either the account-wide default of 1,000, a per-function reserved concurrency setting, or the rate at which new execution environments can be created. Your function code never started running for that specific request.

Is "Rate exceeded" the same error as TooManyRequestsException?

Yes. TooManyRequestsException is the error name and HTTP 429 is the status code; "Rate exceeded" is the message text that comes attached to it. They're two parts of the same error, not two separate problems.

Why am I getting throttled when only a few requests are running?

Almost always because reserved concurrency for that specific function has been set to a very low number, or to 0, which fully blocks it regardless of how little actual traffic it's seeing. Check get-function-concurrency for that function before assuming your account-wide limit is the issue.

What is the default Lambda concurrency limit?

1,000 concurrent executions per AWS Region, shared across every function in that account and Region, unless you've requested a quota increase or a specific function has its own reserved concurrency carved out.

Is the 1,000 concurrency limit per function or per account?

Per account, per Region — not per function. All of your functions in that Region draw from the same 1,000-unit pool by default, which is exactly why one function's traffic spike can throttle a completely unrelated function.

What's the difference between concurrency and requests per second?

Concurrency is how many copies of your function are running at once; requests per second is how many calls arrive in a given second. They're related by the formula concurrency equals average requests per second times average duration in seconds, so a fast function needs far less concurrency than a slow one to handle the same request volume.

How do I check which resource is actually throttling me?

Check the CloudWatch Throttles metric (Sum statistic) for your Lambda function. If it has data points during the failure window, Lambda itself throttled the invocation. If it's empty but you still saw errors, the throttling happened downstream, inside an API call your function code made.

What happens if I set reserved concurrency to 0?

The function is fully blocked from processing any events at all, on purpose — this is a documented way to intentionally stop a function, and it will stay throttled indefinitely until someone raises the value above 0 again.

Does reserved concurrency guarantee my function won't throttle?

It guarantees the function has its own protected slice of concurrency that other functions can't take, but it does not raise the ceiling above whatever number you set. If traffic to that function exceeds the reserved amount, it will still throttle — reserved concurrency is a boundary, not a promise of unlimited scale.

What's the difference between reserved and provisioned concurrency?

Reserved concurrency sets a maximum and minimum number of execution environments for a function, at no extra cost, but those environments still start cold on demand. Provisioned concurrency pre-initializes a set number of environments so they respond immediately, which eliminates cold starts for that portion of traffic, but it costs extra to keep those environments warm.

How fast can Lambda actually scale up?

Each function, in each Region, can scale by up to 1,000 new execution environment instances every 10 seconds, or by 10,000 additional requests per second every 10 seconds, whichever limit is reached first. This scaling rate is independent per function, so other functions in your account scaling at the same time don't slow it down.

Will Lambda retry my request automatically after a 429?

For synchronous, direct invocations, no — Lambda does not automatically retry function errors on your behalf, so you need to build retry logic into your own calling code. For asynchronous invocations, Lambda does automatically retry a failed invocation up to two times.

Does throttling lose data for asynchronous invocations?

It can, if the function can't process events within the retry window and no dead-letter queue is configured — Lambda will eventually discard events that age out unprocessed. Attaching a dead-letter queue to the function's asynchronous configuration (or, for SQS-triggered functions, to the SQS queue itself) captures those events instead of losing them.

How do I know if the throttling is from Lambda or a downstream API my function calls?

Compare your CloudWatch Logs (which will show the error your function code reported) against the Lambda Throttles metric for the same time window. If the logs show a throttling-style error but the Lambda metric shows nothing, the rejection came from something your code called — a database, a third-party API, or another AWS service — not from Lambda itself.

How do I request a Lambda concurrency limit increase?

Open the Service Quotas console, find the Lambda "Concurrent executions" quota for the affected Region, and submit a request for the new value you need, backed by your recent peak usage from the ConcurrentExecutions CloudWatch metric.

Can new AWS accounts have lower concurrency limits than 1,000?

Yes. AWS documents that new accounts are assigned reduced concurrency and memory quotas by default, which are raised automatically over time based on legitimate account usage. Check your actual current value with aws lambda get-account-settings rather than assuming you're at the 1,000 default.

Revision note. Written September 2026, covering current Lambda concurrency, scaling, and quota behavior as published in the AWS Lambda Developer Guide. This will need a look whenever AWS changes the default 1,000-execution account quota or the 1,000-per-10-second scaling rate, since both numbers are the backbone of every fix above. If you're staring at a 429 in production right now: check the reserved concurrency setting first, it's usually the fastest answer, and you've got this.

Related