Photo by Loom Solar on Unsplash
To call SSM GetParameter with AWS SDK v3, create an SSMClient from @aws-sdk/client-ssm and send GetParameterCommand with the parameter Name and WithDecryption: true for SecureString values. Read Parameter.Value. Use GetParametersCommand for up to 10 names at once and paginateGetParametersByPath for a whole hierarchy.
This guide is for Node.js and TypeScript developers who keep configuration in AWS Systems Manager Parameter Store and need to read it from a service, a script or a Lambda function. You’ll get a small helper module for the ssm get parameter AWS SDK v3 calls, covering single values, batches and paths, with decryption, a TTL cache and error messages that say what to fix, plus the permissions and quotas that decide whether it holds up under load.
If the value is a credential that should rotate, Parameter Store may be the wrong home. The comparison below says when to use Secrets Manager instead, and the companion guide shows how to get a Secrets Manager secret value with AWS SDK v3.
Which Parameter Store API should you call?
| Command | Use it for | Limits |
|---|---|---|
GetParameterCommand |
One value by name or ARN, optionally name:version or name:label |
Throws ParameterNotFound if missing |
GetParametersCommand |
A known list of names | 1 to 10 names per call; missing names come back in InvalidParameters |
GetParametersByPathCommand |
Everything under /app/prod/, optionally recursive |
MaxResults 1 to 10 per page; paginate with NextToken |
Parameter types matter when you read them. String comes back as is. StringList is one comma-separated string that you split yourself. SecureString is encrypted with AWS KMS and comes back as ciphertext unless you pass WithDecryption: true; the flag is ignored for the other two types, so it’s safe to always set it.
Prerequisites
- Node.js 20 or later, TypeScript and
tsx. - The
@aws-sdk/client-ssmpackage. - Parameters in the same Region as your client. Parameter names are case-sensitive. If the SDK picks up unexpected credentials, the guide to AWS SDK v3 credential providers shows the resolution order.
How to get SSM parameter values with AWS SDK v3, step by step
- Name parameters as a hierarchyFor example
/orders-api/prod/db-host. Paths let you load a service’s whole config in one paginated call and scope IAM by prefix. - Create one SSMClient per processAt module level, with the Region from the environment or profile.
- Always pass WithDecryption: trueIt decrypts
SecureStringvalues and is ignored for other types. - Batch known namesGroup them into requests of 10 with
GetParametersCommandand checkInvalidParameters. - Load paths at start-upUse
paginateGetParametersByPathwithRecursive: trueand map names to keys relative to the path. - CacheRead each value once per few minutes, not once per request. The throughput quota below is shared by all readers in the account and Region.
Example: a parameter helper with a TTL cache
// params.ts
// Read SSM Parameter Store values with AWS SDK v3: one, several, or a whole path, with a TTL cache.
import {
SSMClient,
GetParameterCommand,
GetParametersCommand,
paginateGetParametersByPath,
ParameterNotFound,
ParameterVersionNotFound,
} from "@aws-sdk/client-ssm";
// One client per process; the SDK retries throttling errors with backoff (maxAttempts total tries).
const ssm = new SSMClient({ maxAttempts: 5 });
/** One value. Name: "/app/prod/db-host", or with a version "/app/prod/db-host:3" or label "/app/prod/db-host:live". */
export async function getParam(name: string): Promise<string> {
const { Parameter } = await ssm.send(new GetParameterCommand({ Name: name, WithDecryption: true }));
if (Parameter?.Value === undefined) throw new Error(`${name} has no value`);
return Parameter.Value;
}
/** Several values by name, 10 per request (the API maximum). Missing names are reported, not thrown. */
export async function getParams(names: string[]): Promise<{ values: Map<string, string>; missing: string[] }> {
const values = new Map<string, string>();
const missing: string[] = [];
for (let i = 0; i < names.length; i += 10) {
const out = await ssm.send(new GetParametersCommand({ Names: names.slice(i, i + 10), WithDecryption: true }));
for (const p of out.Parameters ?? []) if (p.Name && p.Value !== undefined) values.set(p.Name, p.Value);
missing.push(...(out.InvalidParameters ?? []));
}
return { values, missing };
}
/** Everything under a path, as { "db-host": "...", "feature/new-ui": "..." } relative to the path. */
export async function getParamsByPath(path: string): Promise<Record<string, string>> {
const prefix = path.endsWith("/") ? path : `${path}/`;
const result: Record<string, string> = {};
const pages = paginateGetParametersByPath(
{ client: ssm, pageSize: 10 },
{ Path: path, Recursive: true, WithDecryption: true },
);
for await (const page of pages) {
for (const p of page.Parameters ?? []) {
if (p.Name && p.Value !== undefined) result[p.Name.slice(prefix.length)] = p.Value;
}
}
return result;
}
// In-memory cache: one API call per key per TTL, shared by concurrent callers.
type Entry = { value: Promise<string>; expires: number };
const cache = new Map<string, Entry>();
export function getCachedParam(name: string, ttlMs = 5 * 60_000): Promise<string> {
const hit = cache.get(name);
if (hit && hit.expires > Date.now()) return hit.value;
const value = getParam(name);
cache.set(name, { value, expires: Date.now() + ttlMs });
value.catch(() => cache.delete(name)); // don't cache failures
return value;
}
/** Turn the errors you'll see into messages that say what to fix. */
export function explainParamError(err: unknown, name: string): string {
if (err instanceof ParameterNotFound) return `${name}: not found in this Region/account (names are case-sensitive)`;
if (err instanceof ParameterVersionNotFound) return `${name}: that version or label doesn't exist`;
if (err instanceof Error && err.name === "AccessDeniedException") return `${name}: IAM denied ssm:GetParameter`;
if (err instanceof Error && err.name === "ThrottlingException") return `${name}: throttled; cache or raise throughput`;
return `${name}: ${err instanceof Error ? err.name : String(err)}`;
}
The cache stores the promise rather than the value, so concurrent callers on a cold start share one API call, and failures are removed so they don’t stick for the full TTL. Loading a service’s configuration then looks like this:
// load-config.ts
import { getParamsByPath, getParams, explainParamError } from "./params.js";
const path = process.env.CONFIG_PATH ?? "/orders-api/prod";
try {
const config = await getParamsByPath(path);
const keys = Object.keys(config).sort();
console.log(`Loaded ${keys.length} parameters from ${path}: ${keys.join(", ")}`);
const { values, missing } = await getParams([`${path}/db-host`, `${path}/feature-flags`, `${path}/typo`]);
console.log(`db-host = ${values.get(`${path}/db-host`) ?? "(missing)"}`);
if (missing.length > 0) console.warn(`Not found: ${missing.join(", ")}`);
} catch (err) {
console.error(explainParamError(err, path));
process.exitCode = 1;
}
npm install @aws-sdk/client-ssm
npm install --save-dev tsx typescript
AWS_PROFILE=dev AWS_REGION=eu-west-1 CONFIG_PATH=/orders-api/prod npx tsx load-config.ts
# Loaded 4 parameters from /orders-api/prod: db-host, db-name, feature-flags, queue-url
# db-host = orders.cluster-abc123.eu-west-1.rds.amazonaws.com
# Not found: /orders-api/prod/typo
Never log the loaded object as a whole: SecureString values are plain text once decrypted. Log keys, as the script does.
SecureString decryption and KMS permissions
By default, SecureString parameters use the AWS managed key aws/ssm. With that key you don’t need separate KMS permissions to decrypt. If a parameter uses a customer managed key, the caller also needs kms:Decrypt on that key, and the key policy must allow it. That’s also why throughput for SecureString parameters can be further limited by KMS request quotas.
A disabled or deleted key breaks every parameter encrypted with it. Before retiring a key during a cleanup such as the one to find unused customer managed KMS keys, check whether Parameter Store still uses it. Keys you keep for SecureString parameters should rotate; the script to find KMS keys without automatic rotation and enable it covers them.
Which IAM permissions does GetParameter need?
Each API has its own action. In a parameter ARN, a name such as /orders-api/prod/db-host appears as parameter/orders-api/prod/db-host, without a doubled slash. For GetParametersByPath, grant the path itself as well as everything under it:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ReadOrdersApiConfig",
"Effect": "Allow",
"Action": ["ssm:GetParameter", "ssm:GetParameters", "ssm:GetParametersByPath"],
"Resource": [
"arn:aws:ssm:eu-west-1:123456789012:parameter/orders-api/prod",
"arn:aws:ssm:eu-west-1:123456789012:parameter/orders-api/prod/*"
]
},
{
"Sid": "DecryptWithCustomerManagedKey",
"Effect": "Allow",
"Action": "kms:Decrypt",
"Resource": "arn:aws:kms:eu-west-1:123456789012:key/1234abcd-12ab-34cd-56ef-1234567890ab"
}
]
}
Path access includes every level below it. The API reference states that a caller allowed a path can read all levels under it, and that an explicit IAM deny on /a/b doesn’t stop a recursive GetParametersByPath on /a from returning /a/b. Put values that need different access under different top-level paths.
To build the list from your own code, use the guide to find the IAM actions your AWS SDK for JavaScript code needs or the IAM policy generator for TypeScript code, then trim the result to the paths your service actually reads.
Throughput limits and caching
Parameter Store’s default throughput is 40 transactions per second, shared by GetParameter, GetParameters and GetParametersByPath in an account and Region. With higher throughput enabled, the per-API maximums are 10,000 TPS for GetParameter, 1,000 for GetParameters and 100 for GetParametersByPath. Exceed the quota and you get ThrottlingException: Rate exceeded.
As of September 2026 in us-east-1 (AWS Price List, published 11 September 2026), higher throughput costs $0.05 per 10,000 API interactions, and advanced parameters cost $0.05 per parameter per month plus $0.05 per 10,000 API interactions. Standard parameters hold up to 4 KB and advanced ones up to 8 KB; an account can have 10,000 standard parameters per Region.
Caching is usually cheaper than raising the quota. Forty Lambda execution environments that each call GetParameter once a second can use the whole 40 TPS on their own; with a 5-minute cache, the same fleet makes a handful of calls. The SDK retries throttling with backoff; tune that with the guide to configure retry and timeout settings in AWS SDK for JavaScript v3, and track the quota with the steps to monitor AWS service quota usage and get alerts.
In Lambda: the Parameters and Secrets extension
The AWS Parameters and Secrets Lambda Extension serves the same values from a local cache at http://localhost:2773/systemsmanager/parameters/get?name=%2Forders-api%2Fprod%2Fdb-host&withDecryption=true. The name must be URL-encoded, and the request needs the function’s session token in the X-Aws-Parameters-Secrets-Token header, exactly as in the Secrets Manager guide linked above. SSM_PARAMETER_STORE_TTL sets the cache lifetime, from 0 to 300 seconds (the default is 300). The extension doesn’t notice changes before the TTL expires, so after you update a value, running functions keep the old one for up to five minutes. When a function fails on stale or missing config, the guide to investigate Lambda errors with CloudWatch helps you find it.
Parameter Store vs Secrets Manager
- Use Parameter Store for configuration: hostnames, feature flags, queue URLs, table names for code such as the DynamoDB query patterns for AWS SDK v3, and low-risk secrets that don’t rotate. Standard parameters at standard throughput carry no Parameter Store charge.
- Use Secrets Manager for credentials that need automatic rotation, cross-account access or fine-grained audit logging, which is what the Systems Manager documentation itself recommends.
- One client for both:
GetParametercan read a Secrets Manager secret through the reserved/aws/reference/secretsmanager/prefix withWithDecryption: true. It works withGetParameterandGetParametersonly, notGetParametersByPath, and the caller still needs access to the secret.
Whichever you choose, keep values out of code and out of deploy-time environment variables, the practice the Twelve-Factor App’s config chapter argues for. Scripts that still contain keys are exactly what a converter’s secret check catches, as explained in whether it’s safe to paste AWS code into an AI converter. Values already deployed that way turn up with the scripts to scan Lambda environment variables for secrets and scan ECS task definitions for plaintext secrets.
Troubleshooting common errors
ParameterNotFound. Wrong Region, wrong account, or a case mismatch in the name. Shared parameters from another account must be requested by full ARN.ParameterVersionNotFound. The:versionor:labelselector doesn’t exist. Parameter Store keeps the last 100 versions.AccessDeniedException. The resource ARN in the policy doesn’t match the name, often because of the leading slash, or a KMS key policy blocks decryption. The steps to troubleshoot AWS IAM access denied errors go layer by layer.- The value looks like random text. You read a
SecureStringwithoutWithDecryption: true. ThrottlingException: Rate exceeded. Add caching first; enable higher throughput if the load is real.
Limits of this approach
The helper reads configuration at runtime; it doesn’t watch for changes, so a new value reaches a running process only after the TTL. It doesn’t handle cross-Region reads: create one client per Region if you need that. Migrating older code? The free AWS SDK v2 to v3 converter rewrites new AWS.SSM().getParameter(...).promise() calls; review the output and test it before you ship.
ChatWithCloud can list what’s stored, for example “Which Parameter Store parameters under /orders-api use a customer managed KMS key?”, from a read-only profile. The AWS results it reads are sent to the AI model to write the answer, so don’t ask it to print decrypted values; the ChatWithCloud security model lists exactly what is sent.
Frequently asked questions
How do I get an SSM parameter in Node.js with SDK v3?
Install @aws-sdk/client-ssm, then await client.send(new GetParameterCommand({ Name: "/app/prod/db-host", WithDecryption: true })) and read Parameter.Value.
How do I get all parameters under a path?
Use paginateGetParametersByPath with Path, Recursive: true and WithDecryption: true, and collect Parameters from every page.
Why does GetParameters not throw for a missing name?
It returns the names it couldn’t find in InvalidParameters instead of failing the whole request. Check that list.
How do I read a specific version of a parameter?
Append it to the name, as in /app/prod/db-host:3, or use a label such as /app/prod/db-host:live.
Related guides
Ask your AWS account in plain English
Your first 15 runs are free, with no OpenAI key needed.
npx chatwithcloud