DEV Community

Cover image for Amazon Cognito Pricing: Tiers, MAU Counting, and the Defaults That Inflate the Bill
Rafagross
Rafagross

Posted on Originally published at rafael-gross.com

Amazon Cognito Pricing: Tiers, MAU Counting, and the Defaults That Inflate the Bill

Cognito is one of those services nobody budgets for. The user pool goes in early, the free tier covers development, and for a while the line item on the bill is zero. Then the product grows, or someone runs a migration, or a batch job starts touching every user, and the Cognito charge becomes a number that needs explaining.

Usually that number has little to do with traffic. It comes from decisions made once, early, by whoever created the pool: which tier it runs on, what the application does with tokens, how users were imported, and which jobs read user data. This post goes through the billing model, the places where it surprises people, and the settings that keep it predictable.

All prices below come from the Amazon Cognito pricing page, checked on October 12, 2026. Every dollar figure is calculated from those published rates, and the inputs are shown so you can redo the math with your own numbers. Rates change, so check the page before you quote any of this in a planning document.

How Cognito Bills

User pools are billed per monthly active user (MAU), and the rate depends on the pool's feature tier. There are three.

Tier Price per MAU above the free tier Free tier What it adds
Lite $0.0055 up to 100,000 MAU, then $0.0046 10,000 MAU Password sign-in, social and SAML/OIDC federation, MFA with SMS or authenticator apps, Lambda triggers
Essentials $0.015 10,000 MAU Managed login with the visual editor, passwordless and passkeys, email MFA, access token customization, refresh token rotation
Plus $0.020 None Threat protection: adaptive authentication, compromised credential detection, exportable user activity logs

A few things sit outside that table:

  • Federated users who sign in through SAML 2.0 or OIDC are priced separately: $0.015 per MAU, with a free tier of 50 MAU, whatever tier the pool is on.
  • Machine-to-machine authorization is billed per successful token response, with no free tier.
  • Identity pools are free.
  • SMS and email go out through Amazon SNS and Amazon SES and are billed by those services, not by Cognito.

The Default Tier Is Not the Cheap One

New user pools are created on Essentials unless you say otherwise. Essentials costs $0.015 per MAU. Lite costs $0.0055, and $0.0046 past 100,000 MAU. The default is almost three times the price of the cheapest tier.

At 100,000 MAU, after the free tier:

Tier Calculation Monthly
Lite 90,000 × $0.0055 $495
Essentials 90,000 × $0.015 $1,350
Plus 100,000 × $0.020 $2,000

Line chart of monthly Cognito cost from 0 to 500,000 MAU. At 500,000 MAU: Lite $2,335, Essentials $7,350 (the default for new pools), Plus $10,000, Lite with legacy advanced security features $14,585

Essentials is a reasonable choice when you use what it adds. If your application signs users in with a password or a social provider and you built your own login screens, you are probably paying for passkeys and a managed login editor that no one uses. Check the tier on existing pools before anything else in this post. It is the cheapest fix there is.

What Makes a User Billable

This is the part that produces the bills people don't expect. The pricing page defines an MAU as a user for whom your application generates an identity operation during the calendar month. The list is longer than most people assume:

  • sign-up, sign-in, and sign-out
  • token refresh
  • password change and attribute updates
  • administrative creation or update of the user
  • an attribute query on the user through AdminGetUser

Once a user is counted, nothing else about them adds cost that month. Subsequent sessions are free, users with no identity operation are free, and with multi-Region replication the same user is counted once across Regions.

Log view of six identity operations for three users in one month. Only one user opened the app, but a nightly AdminGetUser sync and a background token refresh made the other two billable, for 3 MAU in total

Read the last two items on that list again. They are things your code does, not things your users do. That is where the four patterns below come from.

Batch jobs that read every user

A nightly job enriches a reporting table, or syncs user attributes to a CRM, and it calls AdminGetUser once per user. Every user it reads becomes an MAU for the month, including the ones who haven't opened the app in a year.

Take a pool with 300,000 users, of whom 60,000 actually sign in during a given month:

Essentials Lite
Only real users counted (60,000) $750 $275
Nightly job reads all 300,000 $4,350 $1,415

The fix is to stop asking Cognito for data you already have. The ID token carries the user's attributes at sign-in. If a job needs attributes for users who are not signed in, keep your own copy in a table and update it from a Lambda trigger or from your application when attributes change.

The pricing page names AdminGetUser explicitly. It says nothing either way about ListUsers. I would not switch a job to ListUsers and assume it's free without watching a full month of billing first.

Token refresh on a timer

Token refresh counts as an identity operation. That's fine when a user is actually using the app, because they are already counted. It becomes a cost when the app refreshes tokens on its own. A mobile app that refreshes on launch or in a background task turns every installed copy into an MAU every month, whether the person did anything or not.

Refresh when an API call needs a fresh token, not on launch and not on a schedule.

Migrations that create everyone at once

Administrative creation counts. If you move 500,000 users into a new pool with AdminCreateUser in one month, that month bills 500,000 MAUs: $7,350 on Essentials, even if most of those users never come back.

The Migrate User Lambda trigger avoids this. Cognito calls it when a user who doesn't exist in the pool tries to sign in or reset their password, and the trigger creates the user from your old system at that moment. You only ever create, and pay for, the users who return.

There is a catch. The trigger needs the user's password, so it only fires for password flows that send it: USER_PASSWORD_AUTH, ADMIN_USER_PASSWORD_AUTH, and USER_AUTH with a password, plus forgot-password. It does not fire for SRP sign-in, which many client libraries use by default, or for passwordless sign-in. If your clients use SRP, they have to switch flows for the migration window, or the trigger never runs.

Load tests against a real pool

Synthetic users are users. A load test that signs in 50,000 test accounts after the free tier is used up costs $750 on Essentials for that month. Which leads to the next problem: the free tier probably isn't where you think it is.

The Free Tier Is Smaller Than It Looks

It is defined per account or per AWS organization. If your accounts sit in one organization, plan on staging, development, and production sharing a single 10,000 MAU allowance rather than getting 10,000 each. A load test in a sandbox account can use up the free tier that production was counting on.

Plus has none. On Plus you pay from the first user.

Federated users get 50. For users who sign in through SAML or OIDC, the free tier is 50 MAU and the rate is $0.015 on every tier, Lite included. A pool of 30,000 SSO users costs $449.25 a month. If someone estimated it with Lite's direct sign-in rate, they budgeted about $110.

Older pools have a better deal, and you can lose it. User pools created on or before November 22, 2024 that stay on Lite keep a free tier of 50,000 MAU. Accounts that had at least one MAU in the twelve months before that date can also create new Lite pools that count against the same 50,000.

That makes "upgrading" an older pool expensive in a way that isn't obvious. A legacy pool with 60,000 MAU on Lite pays for 10,000 users: $55. Move it to Essentials and it pays for 50,000: $750. Same users, about thirteen times the bill. There was a promotion that let existing accounts move to Essentials at the old price, but it ended on November 30, 2025.

Switching Tiers Mid-Month

When a pool changes tier during a month, each MAU is billed at the highest-priced tier that was enabled while that user was active.

That turns "let's try Plus for a couple of days" into an expensive experiment. Take a Lite pool with 400,000 MAU. Someone enables Plus on day 12 to evaluate threat protection and switches back on day 14. 150,000 users happened to sign in during those two days.

Calculation Monthly
150,000 users active on days 12 or 13 Plus, no free tier: 150,000 × $0.020 $3,000
250,000 users active only on other days Lite: 90,000 × $0.0055 + 150,000 × $0.0046 $1,185
Total $4,185
Same month, Lite throughout $1,875

November 2026 calendar for a 400,000 MAU Lite pool with Plus enabled on November 12 and 13. The month bills $4,185 instead of $1,875

Evaluate tiers in a separate pool with test users. Never in the production pool.

Machine-to-Machine Tokens

For the client credentials flow, Cognito charges per successful token response: $0.00225 in US East (N. Virginia), with no free tier. The pricing page says there's no additional charge for the number of app clients registered. What moves the bill is how often they ask for a token.

The most expensive version of this is a service that requests a new token before every call instead of caching it. At 10 requests per second, that's about 25.9 million token responses in a 30-day month, or $58,320.

Even with caching, token lifetime moves the bill. Take 200 workers that each fetch a token when the previous one expires:

Access token validity Token responses per month Monthly
5 minutes 1,728,000 $3,888
15 minutes 576,000 $1,296
60 minutes 144,000 $324

Access token validity is configured per app client. Pick it on purpose, cache the token until shortly before it expires, and if you run many workers for the same client, share one cached token between them instead of letting each fetch its own.

Costs That Don't Show Up on the Cognito Line

SMS

SMS for MFA, sign-up confirmation, password recovery, and phone verification is sent through Amazon SNS and billed at SNS rates, which vary a lot by destination country. None of it appears in a Cognito estimate.

SMS also attracts abuse. Sign-up and forgot-password flows that send a text message can be driven by bots to generate messages to numbers the attacker profits from. Three things help:

  • Set the SNS account spend limit for SMS deliberately. The default is $1 a month, which protects you right up until someone raises it to fix a delivery problem and doesn't think about the new number. SNS can take a few minutes to stop sending once the limit is reached, so expect a little overshoot.
  • Associate AWS WAF with the user pool and rate limit the sign-up and recovery endpoints.
  • Prefer authenticator apps or email codes over SMS where your users allow it. Email one-time codes for MFA need Essentials or Plus, so weigh that against what you'd save on SMS.

Legacy advanced security features

Pools that enabled advanced security features (ASF) before the tiers existed still pay ASF as an add-on to Lite, with no free tier: $0.05 per MAU for the first 50,000, $0.035 for the next 50,000, and $0.02 after that.

At 100,000 MAU that is $495 for Lite plus $4,250 for ASF, $4,745 in total. Plus includes the same protections for $2,000. AWS's own example at 950,000 MAU puts Lite with ASF at $25,655 and Plus at $19,000. If any pool still has AdvancedSecurityMode set to AUDIT or ENFORCED on Lite, moving it to Plus is a straight saving.

Multi-Region replication

Replication is an add-on billed per MAU, $0.0045 on Essentials, and it is charged whether or not you ever fail over. For a pool with 500,000 MAU, that's $2,250 a month on top of a $7,350 base, about 31% more. It may be worth it. It should be a decision, not something that was switched on during a resilience review and forgotten.

Controls

Set the tier in code

Leaving the tier out means Essentials. Write it down.

resource "aws_cognito_user_pool" "main" {
  name           = "main-users"
  user_pool_tier = "LITE"
}
Enter fullscreen mode Exit fullscreen mode
aws cognito-idp update-user-pool \
  --user-pool-id us-east-1_EXAMPLE \
  --user-pool-tier LITE
Enter fullscreen mode Exit fullscreen mode

update-user-pool replaces any settings you don't pass with their defaults, so run it with the pool's full current configuration or make the change in your infrastructure code instead. In the AWS CDK, the property is featurePlan: cognito.FeaturePlan.LITE.

Find out what you have

This lists every pool in a Region with its tier and any legacy ASF setting:

for id in $(aws cognito-idp list-user-pools --max-results 60 \
    --query 'UserPools[].Id' --output text); do
  aws cognito-idp describe-user-pool --user-pool-id "$id" \
    --query 'UserPool.[Id,Name,UserPoolTier,UserPoolAddOns.AdvancedSecurityMode]' \
    --output text
done
Enter fullscreen mode Exit fullscreen mode

User pools are regional, so repeat it with --region for every Region you use. Anything on Essentials or Plus should have a reason. Anything on Lite with ASF set to AUDIT or ENFORCED should probably be on Plus.

Put a number on it

In the console, go to Billing and Cost Management → Budgets → Create budget, choose a cost budget, and filter by the service Amazon Cognito. Set the alert a little above your normal month. Most of the patterns in this post show up as a step change, so a budget alert catches them in the month they happen rather than when the invoice lands. AWS Cost Anomaly Detection is worth enabling alongside it.

Checklist

  1. Every user pool has its tier set in code, and the tier matches the features it uses.
  2. No scheduled job calls AdminGetUser across the user base.
  3. Clients refresh tokens when they need them, not on launch or on a timer.
  4. Bulk migrations use the Migrate User trigger instead of AdminCreateUser.
  5. Tier evaluations and load tests run against a separate pool, and nobody assumes each account has its own free tier.
  6. M2M clients cache tokens, and the access token validity was chosen on purpose.
  7. No pool runs Lite with legacy ASF, SMS has a spending limit, and the pool has WAF in front of sign-up.

Checklist card in the style of an aviation normal procedure, grouping Cognito cost controls into before creating a pool, before go-live, before a migration, and monthly

Nearly every item on that list is a one-time decision. That's the point of making them early. A Cognito bill rarely grows because more people use the product. It grows because of what the pool was set up to count.

Sources

Top comments (0)