Parameter Store vs Secrets Manager: when $0.40 is worth it
Both side by side in a tested CDK repo. A Secrets Manager secret costs $0.40 a month; a standard SecureString costs nothing to store.

On this page
If you're weighing Parameter Store vs Secrets Manager on cost, start with storage. AWS Secrets Manager charges $0.40 a month per secret. A standard SecureString parameter in AWS Systems Manager Parameter Store costs nothing to store. That $0.40 buys rotation, cross-account sharing and the option to replicate to other Regions, at another $0.40 per replica. We built both side by side in one AWS Cloud Development Kit (CDK) stack, read them from AWS Lambda two ways, and priced every call.
This article is for developers who keep database passwords and API keys in AWS and want to stop guessing. You get the real prices, the traps that turn "free" into a bill, a least-privilege AWS Key Management Service (KMS) policy, and a decision table.
Key takeaways
- A standard SecureString costs $0 to store. A secret in Secrets Manager costs $0.40 a month, and each replica costs the same again.
- Parameter Store's free reads stop at 40 per second, shared across the account and Region. Turning on higher throughput makes every read cost $0.05 per 10,000.
- Every decrypted read is a KMS Decrypt call, in Parameter Store and in Secrets Manager. KMS bills $0.03 per 10,000 after 20,000 free each month, and its pricing page says that covers AWS managed keys such as
aws/ssmtoo. - A 5-minute cache in Lambda cuts reads from one per invocation to at most 8,640 per value per environment per month.
- Scope
kms:Decryptwithkms:ViaServiceand the encryption context, or one role can decrypt any value it can read under that key.
| At a glance | |
|---|---|
| Level | Intermediate |
| Time to read | 9 minutes |
| Cost to run the repo | About $1.40 a month in the first year ($1 KMS key, $0.40 secret) |
| Code | InfraTales/secrets-cost-choice |
| Tested with | Node.js 24, AWS CDK 2, 50 automated tests, cdk-nag, 13 mutation checks |
Parameter Store vs Secrets Manager cost on AWS
These are us-east-1 prices from the AWS Price List API, checked in October 2026:
| Item | Price |
|---|---|
| Secrets Manager secret | $0.40 per month, prorated by the hour. Each replica counts as a secret |
| Secrets Manager API calls | $0.05 per 10,000 |
| Parameter Store standard parameter | $0 to store. Reads are free at standard throughput |
| Parameter Store with higher throughput on | $0.05 per 10,000 reads |
| Parameter Store advanced parameter | $0.05 per month, plus $0.05 per 10,000 reads |
| KMS customer managed key | $1 per month. The first two rotations add $1 each |
| KMS requests | $0.03 per 10,000, after 20,000 free a month |
Twenty secrets in Secrets Manager cost $8 a month before anyone reads them. The same twenty values as standard SecureString parameters with the AWS managed key (aws/ssm) cost $0 to store. Parameter Store counts each parameter in a response as one read, so a GetParameters call that returns ten values counts as ten.
What the $0.40 buys you
The $0.40 pays for features that Parameter Store lacks, or offers only in its paid advanced tier:
| Feature | Secrets Manager | Parameter Store |
|---|---|---|
| Automatic rotation | Managed rotation for Amazon Relational Database Service (RDS), Aurora, Redshift and DocumentDB. Managed external secrets for keys from supported partners. A Lambda function for anything else | None |
| Another account reads it | Resource policy on the secret, plus a customer managed key | Advanced tier, shared through AWS Resource Access Manager, plus a customer managed key |
| Copies in other Regions | Replica secrets | None |
| Largest value | 64 KB | 4 KB standard, 8 KB advanced |
| Read limit | 10,000 GetSecretValue calls per second |
40 per second by default, shared |
Rotation matters most. Secrets Manager can rotate a secret as often as every four hours. It keeps the old version under the AWSPREVIOUS label, next to the new AWSCURRENT one.
Our demo secret stands in for a third-party API key from a company that isn't a Secrets Manager integration partner. Only that company can issue a new key, so you can't automate its rotation. For a secret like that, you pay $0.40 a month for a feature you can't use. That's the case the title is about.
If the issuer is an integration partner, it's a different story. Secrets Manager's managed external secrets rotate those keys for you, with no Lambda function in your account.
What the repo builds
The stack has five parts:

One key encrypts both stores, and two functions read them. The cached-reader function uses the AWS SDK with its own cache. The extension-reader function asks the extension. Both return a short hash of each value instead of the value, so you can compare them without exposing anything.
Pick the KMS key on purpose
KMS encrypts every SecureString. The AWS managed key aws/ssm costs nothing to store. A customer managed key costs $1 a month, and automatic rotation adds $1 for each of the first two rotations. So budget up to $3 a month per key.
The reads cost money either way. Parameter Store calls KMS Decrypt every time you call GetParameter with WithDecryption, and Secrets Manager calls it for each GetSecretValue. The KMS pricing page says you pay for requests to AWS managed keys too. The first 20,000 requests each month are free. The Secrets Manager guide disagrees for its own key: it calls aws/secretsmanager free to use, so check your bill if you rely on it.
You need your own key for two things: sharing with another account, and writing a key policy. AWS doesn't let you share an AWS managed key. We used one customer managed key for both stores. The grant is where it pays off:
role.addToPolicy(
new iam.PolicyStatement({
sid: 'DecryptViaParameterStore',
actions: ['kms:Decrypt'],
resources: [deps.key.keyArn],
conditions: {
StringEquals: {
'kms:ViaService': `ssm.${this.region}.amazonaws.com`,
'kms:EncryptionContext:PARAMETER_ARN': deps.parameterArn,
},
},
}),
);
kms:ViaService means only Parameter Store can use the key for this role. The role can't call KMS itself. The encryption context pins the grant to one parameter. Without it, the role could decrypt any value under the same key if it could also read that value. The Secrets Manager statement works the same way with kms:EncryptionContext:SecretARN.
The 40 reads per second trap
Parameter Store's free tier has a rate limit, and a single busy function can reach it. The default is 40 reads per second, shared by GetParameter, GetParameters and GetParametersByPath across the whole account and Region. Once one function uses it up, Parameter Store throttles every other app in that account and Region too.
The fix in the console is one switch called higher throughput. It raises GetParameter to 10,000 per second. It also makes every Parameter Store read in that account and Region cost $0.05 per 10,000, the same rate as Secrets Manager.
Run the numbers for a function with 10 million invocations a month that reads two values each time. That's 20 million reads, or $100 a month with higher throughput on. Each read is also a KMS Decrypt call, which adds about $60, with aws/ssm or your own key. Secrets Manager also costs $100 in API calls, plus the same $60 with your own key.
A 5-minute cache changes the math. Each Lambda execution environment reads each value at most 12 times an hour, or 8,640 times in a 30-day month. Ten environments that stay warm all month make 172,800 reads. In Secrets Manager that's $0.86, plus at most $0.52 in KMS Decrypt calls, and Parameter Store stays well under 40 per second.
Two ways to cache in Lambda
The AWS Parameters and Secrets Lambda Extension is the managed option. It runs inside the execution environment and answers HTTP calls on localhost:2773. It calls the API only when its cache is empty or expired:
// Trimmed from app/src/extension-client.ts
const body = await this.get(`/systemsmanager/parameters/get?name=${encodeURIComponent(name)}&withDecryption=true`);
// In get(): sessionToken is the function's AWS_SESSION_TOKEN
const res = await this.fetchImpl(`${this.base}${path}`, {
headers: { 'X-Aws-Parameters-Secrets-Token': this.options.sessionToken },
signal: AbortSignal.timeout(this.options.timeoutMs ?? 3000),
});
The extension refuses any call without the token header, set to the function's own session token. Its cache lives for 300 seconds at most, and AWS's docs say it refreshes only on demand after the TTL expires. Its API timeout defaults to 0, which means it waits forever, so we set 2 seconds.
The second option is a small cache in your own code. Ours is 90 lines with comments, and you can test it and set any TTL. It follows three rules: a value lives for the TTL, a failed read doesn't get cached, and parallel requests for one key share one API call. That last rule is single-flight, and it turns 25 parallel cold-start requests into one call in our tests.
One detail cost us time with the extension. CDK has a built-in helper for its layer, and its only version mapped to layer version 4. AWS's docs listed version 116. We switched to AWS's public parameter /aws/service/aws-parameters-and-secrets-lambda-extension/arm64/latest, which CloudFormation reads at deploy time.
What we got wrong first
Our first cache stored the in-flight promise after starting the load. The cleanup that removes it ran in a finally block. Three failure tests passed: a rejected load, a failed refresh and a failure shared by five waiters.
The fourth test used a loader that throws before it returns a promise. That happens when building the request fails, for example. The test failed. The finally block ran first, then the cache stored the rejected promise, and every later call got the same old error until the environment died.
The fix delays the loader by one microtask:
return Promise.resolve()
.then(load)
.then(
(value) => {
this.entries.set(key, { value, expiresAt: this.now() + this.ttlMs });
return value;
},
(err: unknown) => {
this.counters.failures += 1;
this.entries.delete(key);
throw err;
},
)
.finally(() => this.inFlight.delete(key));
Now the cache stores the promise before the loader runs, so the cleanup runs last. After that, we broke each safeguard on purpose: single-flight, the TTL check, the failure rule, both KMS conditions and the decryption flag. A test failed every time.
A second mistake showed up in the same test run. jest.isolateModules loaded a fresh copy of the AWS SDK that our mocks didn't cover, so one call went to the real AWS endpoint, which rejected the fake keys. Now a setup file points every SDK client at a closed local port.
A decision table for each value
Use this table one value at a time:
| Your value | Store it in |
|---|---|
| Config that isn't secret | Parameter Store standard String |
| A secret you can't rotate, read in one account, 4 KB or less | Parameter Store standard SecureString with aws/ssm |
| An RDS, Aurora, Redshift or DocumentDB password | Secrets Manager with managed rotation |
| An API key from a Secrets Manager integration partner | Secrets Manager managed external secret |
| A secret you rotate with your own code | Secrets Manager with a rotation Lambda function |
| A secret another account reads | Secrets Manager with a resource policy and your own KMS key |
| A secret needed in several Regions | Secrets Manager replicas, at $0.40 each |
| A value from 4 KB to 8 KB, or one that needs an expiry policy | Parameter Store advanced |
Whatever you pick, cache reads in Lambda before you pay for throughput.
What this costs to run
The repo stack costs about $1.40 a month: $1 for the key and $0.40 for the secret, both prorated by the hour. The key rotates once a year. KMS adds $1 a month for each of the first two rotations, so the key costs $2 in year two and $3 from year three. The Lambda calls fit in the monthly free allowance of 1 million requests. AWS doesn't charge for a secret marked for deletion or a key pending deletion, so cleanup stops the bill at once.
When this isn't the right fit
- You already rotate your secrets. If each secret has a rotation schedule, Secrets Manager is the right home, and this comparison doesn't save you anything.
- You need values in seconds after a change. Both caches keep a value for up to 5 minutes per environment. Lower the TTL and pay for more reads.
- SnapStart functions. Lambda doesn't set
AWS_SESSION_TOKENin every initialization mode, so the extension reader needs changes there.
Try it yourself
The repo runs without an AWS account first. npm ci and npm run check type-check, lint, bundle, run all 50 tests and synthesize with cdk-nag.
To deploy, you need a CDK-bootstrapped account. Run npm run deploy, then npm run put-parameter to create the SecureString, since CloudFormation can't create one. npm run smoke checks that both readers see the same values and that the cache gets hits.
Clean up
Run npm run destroy, then delete the parameter with aws ssm delete-parameter --name /secrets-cost-choice/db-password.
Count your secrets that have a rotation schedule today. I'd like to hear how many would move to Parameter Store after an audit like this.
Sources
Pages were read on 2026-10-28.
AWS pricing pages
- AWS Secrets Manager pricing: $0.40 per secret per month and $0.05 per 10,000 API calls; page text does not state hourly proration or replica billing
- AWS Systems Manager Pricing: Standard parameters free, advanced $0.05 a month, higher throughput $0.05 per 10,000 interactions, each returned parameter counts
- Pricing - AWS Key Management Service (KMS): $1 per key a month, first two rotations add $1 each, 20,000 free requests, requests to AWS managed keys are charged
AWS documentation
- AWS Systems Manager endpoints and quotas: 40 TPS shared by GetParameter, GetParameters and GetParametersByPath; 10,000 GetParameter TPS with higher throughput; 4 KB and 8 KB value limits
- AWS Secrets Manager quotas: 65,536-byte secret value and 10,000 GetSecretValue requests per second
- Using Parameter Store parameters in AWS Lambda functions: Extension on localhost:2773, token header, AWS_SESSION_TOKEN not set under SnapStart, TTL up to 300 s, timeout default 0, public latest-ARN parameters, layer version table
- What is AWS Secrets Manager?: The guide that calls aws/secretsmanager free; no charge for secrets marked for deletion
- AWS::SSM::Parameter: CloudFormation does not support SecureString parameters
This article first appeared on AWS Builder Center. The code is on GitHub at InfraTales/secrets-cost-choice.
Next step
Have the same problem on your stack?
Send the architecture, AWS bill concern, deploy pain, or GenAI reliability issue. I'll find the first real bottleneck and propose a small, reversible fix.
FAQ
Is Parameter Store cheaper than Secrets Manager?
To store values, yes. A standard SecureString costs $0, while a secret costs $0.40 a month, plus the same again for each replica. Reads cost money in both, because every decrypted read is a KMS Decrypt call, billed at $0.03 per 10,000 after 20,000 free each month.
What is the Parameter Store free read limit?
Free reads stop at 40 per second, shared across the account and Region. Turning on higher throughput makes every read cost $0.05 per 10,000.
Can Parameter Store rotate secrets?
No. Parameter Store has no automatic rotation. Secrets Manager has managed rotation for RDS, Aurora, Redshift and DocumentDB, managed external secrets for keys from supported partners, and a Lambda function for anything else.
When should I use Secrets Manager anyway?
Use it if you rotate your secrets. If each secret has a rotation schedule, Secrets Manager is the right home, and the cost comparison doesn't save you anything.
Related reading

Rahul Ladumor
Principal Cloud & AI Platform Architect. AWS Professional certified, 5x AWS Community Builder. I work with teams that have real users, real AWS bills, and real production pressure.
About Rahul →