Monitor AWS Service Quota Usage and Get Alerts

Long aisle between server racks in a dimly lit data center

Photo by Brett Sayles on Pexels

To monitor AWS service quota usage, pair each quota in Service Quotas with its CloudWatch usage metric, then alarm on the metric math expression m1/SERVICE_QUOTA(m1)*100 when it passes a threshold such as 80 percent. For Lambda, compare ClaimedAccountConcurrency with the concurrent executions quota. Service Quotas Automatic Management can also notify you at 80 and 95 percent.

This guide is for platform engineers and developers who have been paged by a LimitExceeded or TooManyRequestsException once and don’t want it to happen again. You’ll learn how to monitor AWS service quota usage with the console, the AWS CLI and a TypeScript report that covers several regions, how to set alerts that fire before the limit, and where ChatWithCloud gives you a faster answer to “how close are we?”.

What are AWS service quotas, and why watch usage?

A service quota is the maximum number of resources or operations an account can use, usually per region. Each has an AWS default value, and adjustable quotas can have a higher applied value after an approved increase. Increases take time to review, so the useful signal is the trend toward the limit, not the moment you hit it.

The Site Reliability Engineering book calls this saturation, one of its four golden signals for monitoring distributed systems: how full a service is, measured against its most constrained resource, with a target below 100 percent because systems degrade before they’re completely full. A quota is exactly such a constraint, just one enforced by AWS instead of by physics.

Where does quota usage data come from?

Source What it gives you Best for
Service Quotas console and API Default and applied values, quota codes, adjustability, a utilization graph for supported quotas Looking up limits and requesting increases
CloudWatch AWS/Usage namespace ResourceCount or CallCount metrics with Service, Type, Resource and Class dimensions Alarms and dashboards
SERVICE_QUOTA() metric math The quota value for a usage metric, as a time series Usage-as-percentage alarms
Service-specific metrics For Lambda, ClaimedAccountConcurrency in AWS/Lambda Lambda concurrency
Service Quotas Automatic Management Opt-in notifications at 80% and 95%, optional automatic increase requests Broad coverage with little setup

Not every service publishes usage metrics. According to the CloudWatch documentation, services that integrate usage metrics with Service Quotas include EC2, DynamoDB, CloudWatch, CloudWatch Logs, ECR, Elastic Load Balancing, Fargate, KMS and Data Firehose, among others. For a quota without a usage metric, you count resources yourself (for example with a scheduled script like the one that reports EC2 instances by type, launch time and region) and compare against the Service Quotas value.

Prerequisites

  • AWS CLI v2 and a profile that can read Service Quotas and CloudWatch, plus create alarms and SNS topics for the setup steps.
  • The quota code for each limit you care about. Two common ones: L-B99A9384 (Lambda concurrent executions) and L-1216C47A (EC2 Running On-Demand Standard instances, measured in vCPUs).
  • Node.js 18+ with tsx for the TypeScript report.

How to monitor AWS service quota usage and get alerts

  1. Find the quota and its usage metricCall get-service-quota. The response includes Value, Adjustable and, for integrated quotas, a UsageMetric block with the namespace, metric name, dimensions and recommended statistic.
  2. Create a notification targetCreate an SNS topic and subscribe your email, chat integration or on-call tool to it.
  3. Create the alarm on usage as a percentageUse metric math: the usage metric as m1 (hidden), and m1/SERVICE_QUOTA(m1)*100 as the alarm’s expression, with a threshold of 80.
  4. Repeat per regionMost quotas and all these alarms are regional. An alarm in us-east-1 says nothing about eu-west-1.
  5. Review and request increases earlyWhen an alarm fires, request the increase from Service Quotas the same day rather than waiting for the limit.

Here are the commands for the EC2 On-Demand Standard vCPU quota:

Look up the quota and create the alarm

aws service-quotas get-service-quota --service-code ec2 --quota-code L-1216C47A \
  --query "Quota.{name:QuotaName,value:Value,adjustable:Adjustable,metric:UsageMetric}"

aws sns create-topic --name quota-alerts
aws sns subscribe --topic-arn arn:aws:sns:eu-west-1:123456789012:quota-alerts \
  --protocol email --notification-endpoint [email protected]

aws cloudwatch put-metric-alarm \
  --alarm-name ec2-standard-vcpu-quota-80pct \
  --metrics file://ec2-vcpu-quota-alarm.json \
  --comparison-operator GreaterThanThreshold --threshold 80 \
  --evaluation-periods 3 --datapoints-to-alarm 3 \
  --treat-missing-data notBreaching \
  --alarm-actions arn:aws:sns:eu-west-1:123456789012:quota-alerts
ec2-vcpu-quota-alarm.json

[
  {
    "Id": "usage",
    "MetricStat": {
      "Metric": {
        "Namespace": "AWS/Usage",
        "MetricName": "ResourceCount",
        "Dimensions": [
          { "Name": "Service", "Value": "EC2" },
          { "Name": "Type", "Value": "Resource" },
          { "Name": "Resource", "Value": "vCPU" },
          { "Name": "Class", "Value": "Standard/OnDemand" }
        ]
      },
      "Period": 300,
      "Stat": "Maximum"
    },
    "ReturnData": false
  },
  {
    "Id": "pct",
    "Expression": "usage/SERVICE_QUOTA(usage)*100",
    "Label": "Standard On-Demand vCPU quota used (%)",
    "ReturnData": true
  }
]

The Service Quotas console can create the same alarm for you from a quota’s detail page, under Amazon CloudWatch alarms. A standard alarm costs $0.10 per alarm metric per month in US East (N. Virginia) as of September 2026, according to the CloudWatch pricing page.

Check Lambda concurrency quota usage

Lambda’s account concurrency quota defaults to 1,000 per region, and new accounts start with a reduced quota that AWS raises based on usage. ConcurrentExecutions alone understates how close you are, because reserved and provisioned concurrency are “claimed” even when idle. The AWS Lambda documentation recommends ClaimedAccountConcurrency (unreserved concurrent executions plus allocated concurrency) and this formula:

Lambda concurrency utilization

Utilization = (ClaimedAccountConcurrency / SERVICE_QUOTA(ConcurrentExecutions)) * 100%

If you set 600 reserved concurrency on one function and 200 provisioned on another, ClaimedAccountConcurrency never drops below 800, so only 200 remain for everything else. That’s the number that decides when other functions start to throttle, and when to investigate Lambda throttles and errors with CloudWatch.

Track EC2 and Lambda quotas across regions with a script

Alarms tell you when something crosses a line. A report tells you where you stand everywhere. This script uses the AWS SDK for JavaScript v3 to read each quota’s applied value (falling back to the AWS default), fetch the peak usage over the last 24 hours, and print utilization per region.

quota-report.ts

import {
  ServiceQuotasClient,
  GetServiceQuotaCommand,
  GetAWSDefaultServiceQuotaCommand,
} from "@aws-sdk/client-service-quotas";
import { CloudWatchClient, GetMetricDataCommand } from "@aws-sdk/client-cloudwatch";
import type { Dimension } from "@aws-sdk/client-cloudwatch";

interface QuotaCheck {
  label: string;
  serviceCode: string;
  quotaCode: string;
  namespace: string;
  metricName: string;
  dimensions: Dimension[];
}

const CHECKS: QuotaCheck[] = [
  {
    label: "Lambda concurrent executions",
    serviceCode: "lambda",
    quotaCode: "L-B99A9384",
    namespace: "AWS/Lambda",
    metricName: "ClaimedAccountConcurrency",
    dimensions: [],
  },
  {
    label: "EC2 On-Demand Standard vCPUs",
    serviceCode: "ec2",
    quotaCode: "L-1216C47A",
    namespace: "AWS/Usage",
    metricName: "ResourceCount",
    dimensions: [
      { Name: "Service", Value: "EC2" },
      { Name: "Type", Value: "Resource" },
      { Name: "Resource", Value: "vCPU" },
      { Name: "Class", Value: "Standard/OnDemand" },
    ],
  },
];

const WARN_AT = 80;
const regions = process.argv.slice(2);
if (regions.length === 0) regions.push(process.env.AWS_REGION ?? "us-east-1");

async function quotaValue(client: ServiceQuotasClient, check: QuotaCheck): Promise<number | undefined> {
  const params = { ServiceCode: check.serviceCode, QuotaCode: check.quotaCode };
  try {
    const res = await client.send(new GetServiceQuotaCommand(params));
    return res.Quota?.Value;
  } catch (err) {
    if ((err as { name?: string }).name !== "NoSuchResourceException") throw err;
    const res = await client.send(new GetAWSDefaultServiceQuotaCommand(params));
    return res.Quota?.Value;
  }
}

async function peakUsage(client: CloudWatchClient, check: QuotaCheck): Promise<number> {
  const endTime = new Date();
  const startTime = new Date(endTime.getTime() - 24 * 3600 * 1000);
  const res = await client.send(
    new GetMetricDataCommand({
      StartTime: startTime,
      EndTime: endTime,
      MetricDataQueries: [
        {
          Id: "usage",
          MetricStat: {
            Metric: { Namespace: check.namespace, MetricName: check.metricName, Dimensions: check.dimensions },
            Period: 300,
            Stat: "Maximum",
          },
        },
      ],
    }),
  );
  const values = res.MetricDataResults?.[0]?.Values ?? [];
  return values.length ? Math.max(...values) : 0;
}

async function main(): Promise<void> {
  for (const region of regions) {
    const quotas = new ServiceQuotasClient({ region });
    const cloudwatch = new CloudWatchClient({ region });
    for (const check of CHECKS) {
      const [limit, peak] = await Promise.all([quotaValue(quotas, check), peakUsage(cloudwatch, check)]);
      if (!limit) {
        console.log(`${region.padEnd(15)} ${check.label.padEnd(32)} quota value unavailable`);
        continue;
      }
      const pct = (peak / limit) * 100;
      const flag = pct >= WARN_AT ? "  <-- request an increase" : "";
      console.log(`${region.padEnd(15)} ${check.label.padEnd(32)} ${peak} / ${limit} (${pct.toFixed(1)}%)${flag}`);
    }
  }
}

main().catch((err) => {
  console.error(err);
  process.exit(1);
});
Run it across three regions (sample output is illustrative)

npm install @aws-sdk/client-service-quotas @aws-sdk/client-cloudwatch
AWS_PROFILE=platform-readonly npx tsx quota-report.ts us-east-1 eu-west-1 ap-southeast-2

# us-east-1       Lambda concurrent executions     840 / 1000 (84.0%)  <-- request an increase
# us-east-1       EC2 On-Demand Standard vCPUs     212 / 640 (33.1%)
# eu-west-1       Lambda concurrent executions     95 / 1000 (9.5%)
# eu-west-1       EC2 On-Demand Standard vCPUs     0 / 64 (0.0%)
# ap-southeast-2  Lambda concurrent executions     0 / 1000 (0.0%)
# ap-southeast-2  EC2 On-Demand Standard vCPUs     0 / 64 (0.0%)

Add entries to CHECKS for other quotas; aws service-quotas list-service-quotas --service-code <code> lists codes and their usage metrics. More scripts in this style live in the AWS practical examples hub with TypeScript scripts, and if you’re porting an older v2 quota checker, the AWS SDK v2 to v3 converter produces a draft to review.

Should you use Service Quotas Automatic Management?

Automatic Management is an opt-in feature that monitors supported quotas and notifies you through AWS Health, with delivery to email, Slack or the AWS Console Mobile Application, and events you can route with EventBridge. It has two modes: Notify Only, which alerts at 80% and 95% utilization, and Notify and Auto-Adjust, which also files increase requests for adjustable quotas. Auto-adjust approval isn’t guaranteed, and a rejected automatic request needs a manual follow-up.

It’s the quickest way to get broad coverage. Keep explicit CloudWatch alarms for the handful of quotas that would cause an outage, so the alert goes to the same on-call path as your other alarms.

Permissions needed

The report needs only read access. The setup commands need a few write actions, which you can grant to a separate role:

quota-monitoring-policy.json

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "ReadQuotasAndUsage",
      "Effect": "Allow",
      "Action": [
        "servicequotas:GetServiceQuota",
        "servicequotas:GetAWSDefaultServiceQuota",
        "servicequotas:ListServiceQuotas",
        "cloudwatch:GetMetricData",
        "cloudwatch:DescribeAlarms"
      ],
      "Resource": "*"
    },
    {
      "Sid": "CreateQuotaAlarms",
      "Effect": "Allow",
      "Action": [
        "cloudwatch:PutMetricAlarm",
        "sns:CreateTopic",
        "sns:Subscribe"
      ],
      "Resource": "*"
    }
  ]
}

Drop the second statement for a read-only monitoring profile. To draft a policy from your own quota scripts, use the IAM policy generator for TypeScript code, then review the generated policy for least privilege before you attach it.

Troubleshooting and common mistakes

  • SERVICE_QUOTA() returns nothing. The metric isn’t a usage metric integrated with Service Quotas, or its dimensions don’t exactly match the UsageMetric returned by get-service-quota.
  • No data in AWS/Usage. Usage is zero, or the resource was never used in that region. Set --treat-missing-data notBreaching so the alarm doesn’t flap.
  • Alarm in the wrong region. Create one alarm per region per quota. Global quotas are the exception; request increases for them from US East (N. Virginia).
  • Lambda looks fine but functions throttle. Check reserved concurrency on individual functions and ClaimedAccountConcurrency, not only ConcurrentExecutions.
  • Rate quotas. API request-rate limits (throttling of control-plane calls) show up as CallCount metrics for some services, and as retries in your SDK logs for others.

The ChatWithCloud shortcut

ChatWithCloud is a command-line tool that answers questions about your AWS account in plain English. Instead of looking up quota codes and dimensions, ask “What’s our Lambda concurrency quota in this region, and what was peak claimed concurrency today?” or “Which EC2 quotas are above 70 percent?”. It writes AWS SDK code, runs it on your machine with your profile, and explains the result; the walkthrough of how ChatWithCloud runs SDK code locally shows the loop. Set up a read-only profile with the guide to connect ChatWithCloud to your AWS account.

It fits the moment an alert fires: pair it with the workflow to troubleshoot AWS infrastructure with an AI CLI, or ask AI about Lambda errors and throttles when concurrency is the suspect. Limits to keep in mind: one profile and one region per session, answers can be wrong, the generated code uses SDK v2, and it would create an alarm or request an increase without a confirmation step if your profile allows it. Stay on a read-only profile for questions, and see the ChatWithCloud security model for what is sent to the model.

Frequently asked questions

How do I get an alert when an AWS service quota is nearly reached?

Create a CloudWatch alarm on m1/SERVICE_QUOTA(m1)*100 with a threshold of 80, where m1 is the quota’s usage metric, and send it to an SNS topic. Or turn on Service Quotas Automatic Management for 80% and 95% notifications.

Can I monitor every quota this way?

No. Only quotas with an integrated usage metric work with SERVICE_QUOTA(). For others, count the resources with a script and compare against the value from get-service-quota.

How do I check Lambda concurrency quota usage?

Compare the maximum of ClaimedAccountConcurrency with the L-B99A9384 quota value for the region. The script above does this, and the Lambda console’s concurrency graphs show the same metrics.

Are service quotas per region?

Most are. Some are global (account-level), and those can only be increased from US East (N. Virginia) in the commercial partition. Check the quota’s description in Service Quotas.

Related guides

Ask your AWS account in plain English

Your first 15 runs are free, with no OpenAI key needed.

npx chatwithcloud