Amazon CloudWatch — Metrics: Core Concepts¶
Anatomy of a Metric¶
A metric is a time-ordered set of datapoints. It is uniquely defined by a namespace, a metric name and zero or more dimensions. Each datapoint adds a timestamp, a value (or a set of values) and an optional unit.
| Part | Rules |
|---|---|
| Namespace | 1–255 ASCII characters: letters, digits, . - _ / # : and space; must not start with :. There is no default. Do not start your own with AWS/ (reserved for AWS services). Metrics in different namespaces never mix |
| MetricName | 1–255 characters |
| Dimensions | Name/value pairs that are part of the metric identity; up to 30 per metric. Name: 1–255 ASCII characters, not starting with :. Value: 1–1024 ASCII characters, at least one non-whitespace character |
| Timestamp | Up to two weeks in the past and up to two hours in the future; defaults to the time CloudWatch receives the datapoint. Send UTC |
| Value / Values + Counts / StatisticValues | One number, many numbers with how often each occurred, or a pre-aggregated set (02). Finite numbers between -2^360 and 2^360; NaN and ±Infinity are rejected |
| Unit | One of 27 units (Seconds, Milliseconds, Bytes, Percent, Count, Count/Second, None, ...). Default None |
| StorageResolution | 60 (standard, default) or 1 (high resolution) |
Metrics exist only in the Region where they were created, cannot be deleted, and expire on their own: a metric with no new datapoints for 15 months disappears, and datapoints older than 15 months are dropped on a rolling basis.
Dimensions and Dimension Combinations¶
flowchart TB
NS["namespace<br/>QA/Orders"] --> NAME["metric name<br/>Latency"]
NAME --> A["Service=api"]
NAME --> B["Service=api, Env=test"]
NAME --> C["Service=worker"]
A --> DP1["datapoints: timestamp, value, unit"]
B --> DP2["datapoints: timestamp, value, unit"]
C --> DP3["datapoints: timestamp, value, unit"]
Each unique combination of dimensions is a separate metric, even with the same name. You can only read statistics for combinations you published.
| Published | Readable | Not readable |
|---|---|---|
Server=Prod, Domain=Rio |
Server=Prod, Domain=Rio |
Server=Prod alone, Domain=Rio alone, no dimensions |
- CloudWatch does not aggregate across dimensions for custom metrics. If you need a total, publish it as its own metric (no dimensions, or only
Service), or sum several series with metric math. - AWS services behave differently: for some (
AWS/EC2) CloudWatch can aggregate across dimensions when you query without any. - The metric math
SEARCHfunction can fan out over many metrics in one expression. - High cardinality is the main cost and quota trap. A request ID, user ID, order ID, pod name or timestamp as a dimension creates a new custom metric per value. Keep dimension values to a small, closed set (service, route template, status class, environment). Put identifiers in log fields instead.
- Several processes can publish to the same namespace, name and dimensions: CloudWatch treats them as one metric and aggregates their datapoints.
Units, Statistics and Percentiles¶
Datapoints with a unit are aggregated separately per unit. A query without Unit returns all of them, each with its unit; a query with a unit that matches nothing returns null results. CloudWatch never converts units.
| Statistic | Meaning over one period |
|---|---|
SampleCount |
Number of datapoints (with Counts, the sum of the counts) |
Sum |
Total of the values: use for counters and error counts |
Average |
Sum / SampleCount |
Minimum, Maximum |
Extremes: use Maximum for ages and depths |
p50, p99, p99.9, ... |
Percentile with up to ten decimal places; asked through ExtendedStatistic in alarms and GetMetricStatistics, and as Stat in GetMetricData |
Alarms also accept tm90, tc90, ts90, wm90, IQM and PR(n:m) as extended statistics.
Percentiles need raw datapoints:
- A custom metric supports percentiles when you publish raw values (single
ValueorValues/Counts). - A statistic set supports them only if
SampleCountis 1 andMin,Max,Sumare equal, orMinequalsMaxandSum = Min × SampleCount. - Percentiles are not available if any value of the metric is negative.
- Not every AWS metric supports them; the concepts page lists API Gateway, Application Load Balancer, EC2, ELB, Kinesis, Lambda and RDS, and the Bedrock guide uses
p50onAWS/Bedrockmetrics.
Periods, Resolution, Retention and Rollup¶
A period is the length of time one statistic covers. For high-resolution metrics a period can be 1, 5, 10, 20, 30 seconds or any multiple of 60; for standard-resolution metrics it must be a multiple of 60. Aggregated datapoints are stamped with the start of their period: data from 19:00 to 20:00 is stamped 19:00. A value for the current period can still change while samples keep arriving.
| Standard resolution | High resolution |
|---|---|
| Granularity of one minute | Granularity of one second (StorageResolution=1, custom metrics only) |
| AWS services publish this way by default | Retrievable with periods of 1, 5, 10, 30 seconds or any multiple of 60 |
| Alarm period is a multiple of 60 s | Alarm periods of 10 or 30 s are possible (higher alarm price); every PutMetricData call costs money, so frequent calls cost more |
| Datapoint period | Available for |
|---|---|
| Under 60 s (high-resolution) | 3 hours |
| 60 s | 15 days |
| 300 s (5 min) | 63 days |
| 3600 s (1 h) | 455 days (15 months) |
Data is rolled up as it ages: after 15 days one-minute data is only retrievable with a 5-minute period, after 63 days with a 1-hour period. StartTime is rounded down accordingly (to the minute for data newer than 15 days, to a 5-minute clock interval between 15 and 63 days, to the hour beyond 63 days), and a GetMetricStatistics request with a start time older than 3 hours must use a period that is a multiple of 60, 300 or 3600 respectively or it returns no datapoints.
Delays and Missing Data¶
| Situation | What to expect |
|---|---|
New metric after PutMetricData |
Statistics readable after up to 2 minutes; visible in list_metrics after up to 15 minutes |
| Datapoint timestamped 3–24 hours ago | Can take up to 2 hours to become readable |
| Datapoint timestamped 24 hours ago or earlier | Can take at least 48 hours to become readable |
| Metric with no datapoints for two weeks | Not returned by list_metrics; still readable with get_metric_data |
| Custom datapoints with timestamps other than the current UTC time | Alarms can show INSUFFICIENT_DATA or fire late |
| Current period | Values change while more samples arrive; compare completed periods |
| Idle resources | No datapoints at all, not zeros (below) |
AWS services publish only while a resource is active, so "no data" is a normal state:
| Service | Rule |
|---|---|
| SQS | Metrics are reported while the queue is active; some (SentMessageSize) appear only after the first message |
| SNS | A topic is active for up to six hours after the last API call on it |
| ECS | Metrics exist only for clusters and services with tasks in RUNNING |
| EC2 | Basic monitoring: 5-minute datapoints; detailed monitoring: 1-minute. Status checks: 1-minute at no charge |
Choose an alarm period at least as long as the metric resolution (300 s for basic EC2 monitoring). If your own metric is intermittent, publish 0 for quiet periods so alarms and averages see them (03).
Vended Metrics for the Services in This Section¶
| Service | Namespace and dimensions | Metrics worth alarming on |
|---|---|---|
| SQS | AWS/SQS, QueueName |
ApproximateNumberOfMessagesVisible (Count; backlog, and the metric AWS recommends for a DLQ), ApproximateAgeOfOldestMessage (Seconds), ApproximateNumberOfMessagesNotVisible (in flight), NumberOfEmptyReceives; counters NumberOfMessagesSent, Received, Deleted |
| SNS | AWS/SNS, TopicName |
NumberOfMessagesPublished, NumberOfNotificationsDelivered, NumberOfNotificationsFailed, NumberOfNotificationsFilteredOut, NumberOfNotificationsRedrivenToDlq (all Count, statistic Sum) |
| EC2 | AWS/EC2, InstanceId or AutoScalingGroupName |
CPUUtilization (Percent), StatusCheckFailed and StatusCheckFailed_Instance / _System / _AttachedEBS (0 or 1), NetworkIn, NetworkOut. No memory or disk: install the CloudWatch agent (namespace CWAgent, for example mem_used_percent, disk_used_percent) |
| ECS | AWS/ECS, ClusterName + ServiceName |
CPUUtilization, MemoryUtilization (service level, Fargate and EC2); with Container Insights (ECS/ContainerInsights): RunningTaskCount, DesiredTaskCount, PendingTaskCount, CpuUtilized, MemoryUtilized (labelled Megabytes, actually MiB) |
| Bedrock | AWS/Bedrock, ModelId |
Invocations, InvocationLatency (ms, whole request), InvocationClientErrors, InvocationServerErrors, InvocationThrottles, InputTokenCount, OutputTokenCount, TimeToFirstToken (streaming APIs only), EstimatedTPMQuotaUsage (approximate) |
Notes that bite in tests and alarms:
- SQS and the "visible" metrics are approximate because of the distributed design. For a DLQ use
ApproximateNumberOfMessagesVisible;NumberOfMessagesSentdoes not count messages moved there by redrive.ApproximateNumberOfMessagesNotVisiblecan briefly report non-zero on an empty queue, so alarm on several consecutive datapoints. - SNS
NumberOfNotificationsFailedcounts differently per endpoint type: once per message for SQS, email, SMS and mobile push when delivery stops, and for every failed attempt (retries included) for HTTP/S. - EC2 alarms with stop, terminate, reboot or recover actions should treat missing data as
missingand fire only fromALARM. - Bedrock publishes each metric both with and without
ModelId; if a request fails before the model is known, only the series without the dimension counts it, so a per-model error alarm can undercount. Throttled requests are excluded fromInvocationsand from both error counts; how many throttles you see depends on SDK retry settings. Requests billed at a long-context rate also appear underModelId+ContextWindow, as a subset of theModelIdseries. - ECS Container Insights needs setup per cluster, bills as custom metrics, and from 1 October 2026 does not bill
RunningTaskCount.
Quotas That Matter to Test Code¶
| Quota | Default |
|---|---|
PutMetricData: metrics per request / payload / values per datum |
1000 / 1 MB (gzip accepted) / 150 |
PutMetricData requests |
500 per second per Region (adjustable) |
| Dimensions per metric | 30 |
GetMetricData: queries per request / datapoints per request |
500 / 100,800 (MaxDatapoints default) |
GetMetricData requests; datapoints per second (last 3 hours / older) |
500 per second; 180,000 / 396,000 |
GetMetricStatistics: datapoints per call / requests |
1,440 / 400 per second |
ListMetrics: results per page / requests |
500 / 25 per second |
PutMetricAlarm, SetAlarmState, DeleteAlarms |
3 requests per second each |
DescribeAlarms / DescribeAlarmHistory |
9 / 20 requests per second |
PutDashboard |
10 requests per second |
Alarm evaluation range (Period × EvaluationPeriods) |
1 day for periods under 1 hour, 7 days otherwise |
| Alarm actions per state | 5 |
All quotas are per account and Region; the Service Quotas console (--service-code monitoring) shows the current values. A parallel test suite that creates alarms shares the 3-per-second limit with everyone else in the account.
What You Pay For¶
| Item | Billing dimension |
|---|---|
| Custom and detailed-monitoring metrics | Per metric per month, with volume tiers; every unique dimension combination counts |
PutMetricData |
Per request |
GetMetricData, GetMetricStatistics |
Per metrics requested / per request (the free tier does not cover them) |
| Alarms | Per alarm metric per month; high-resolution, composite, anomaly detection, Metrics Insights, PromQL and log alarms have their own rates |
| Dashboards | Per dashboard per month, prorated hourly; 3 are free (up to 50 metrics each) |
| Metrics extracted from logs (EMF, metric filters) | Billed as custom metrics, plus the log ingestion |
| Container Insights | Per custom metric (ECS) |
| OpenTelemetry metrics | Per GB ingested |
Free tier highlights per month: 10 custom metrics, 10 standard-resolution alarm metrics, 3 dashboards and 1 million metric API requests (GetMetricData and similar read calls are always charged). Check the pricing page before budgeting; it changes.
Quick Rules¶
- Dimensions: few, closed, low-cardinality. Identifiers belong in logs.
- Publish raw values (
Values/Counts) when you will ask for percentiles. A statistic set cannot answer them. - Always set
Unit, and query with the same unit you published. - Use
Maximumfor depths and ages,Sumfor counters,Averageonly for rates and utilisation. - Never read the current period as final. Query completed periods, or accept that the value moves.
- Choose an alarm period at least as long as the metric resolution, and remember that standard EC2 metrics are 5 minutes.
- Idle means no data. Decide
TreatMissingDataper metric; publish zeros for your own intermittent metrics. - Remember retention. One-minute data lives 15 days; a regression dashboard that looks back 30 days sees 5-minute data.
- Do not delete-and-recreate metrics in tests. You cannot delete them; use a unique namespace or dimension value per run for isolation, and keep the number of those small.
- Respect the quotas in CI: 3 alarm writes per second, 500
GetMetricDatarequests per second.
See also¶
- Amazon CloudWatch — Overview
- Amazon CloudWatch — boto3 in Python
- Amazon CloudWatch — Alarms & Dashboards
- Amazon SQS — Queues, Consumers & Testing
- Amazon SNS — Topics, Subscriptions & Fan-out
- Amazon EC2
- Amazon ECS — Containers on Fargate, Services & Testing
- Amazon Bedrock
- AWS Foundations — boto3, Credentials & Local Testing