AWS Step Functions pricing is a short rate card. The bill it produces is a different number, and everything between the two is counting.
Two decisions dominate: the workflow type, immutable once chosen, and how your fan-out is structured. The second usually happens by accident.
Here is what generates a billable transition, where the crossover sits, what a Map state costs, and the two IaC properties that move it. Every rate is us-east-1.
What a Step Functions workflow actually costs
Standard has one Step Functions billing dimension: the state transition, at $0.000025 each in us-east-1, or $0.025 per 1,000, with no volume tiering.
Express has two: $1.00 per million requests plus a tiered duration charge in GB-seconds. Duration rounds up to the nearest 100 ms, and memory bills in 64 MB chunks.
| Dimension | Standard | Express (us-east-1) |
|---|---|---|
| Execution start | One state transition | $0.000001 per request |
| Each step that runs | $0.000025 per transition | No transition charge exists |
| Duration | Not billed, idle included | $0.00001667 per GB-second to 3,600,000, then $0.00000833, then $0.00000456 |
| Free tier | 4,000 transitions monthly | No free band |
| Execution history | 90 days at no charge | CloudWatch Logs, vended logs rate |
The free tier covers Standard transitions only and does not expire with the 12 month AWS Free Tier term. At production scale it is noise, 0.001% of the 400,000,000 transitions below.
Those rates are not universal: transitions cost 50% more in sa-east-1 and 10% less in three Asia Pacific regions, while Express tier boundaries are identical everywhere. The AWS Step Functions pricing page holds the rate card; run your own numbers in the AWS Step Functions Pricing Calculator.
What counts as a billable state transition
Underestimates start here. A transition is counted each time a step runs, and "step" is broad.
Counting follows nodes traversed, not arrows drawn: AWS's branching example has 10 arcs and bills 9 transitions, because 9 nodes run.
| Situation | Billed as a state transition? | What to do about it |
|---|---|---|
Any step running, Choice and Pass included | Yes | Count them too |
| The first state and the terminal state | Yes, like any other | Already in your count |
A Retry attempt | Yes, every attempt | Alarm on retry rate |
Standard idle in Wait or .waitForTaskToken | No, up to one year | Express bills that time |
| Starting each Inline Map iteration | No, but its inner states are | Count states x items |
| Each child a Distributed Map starts | Yes, one per child | Batch with ItemBatcher |
| A redrive, plus every state it reruns | Yes | Treat it as a rerun |
Parallel states are where the published rules stop. AWS publishes one rule for counting transitions: every step that runs is counted, by nodes traversed. It publishes no shortcut formula for a Parallel state with N branches. Count the nodes your branches execute, then check against your first month's state transition metric.
Standard or Express: where the cheaper option flips
Once you can count transitions, the type decision is arithmetic. Four gates come first.
AWS sets three: complete within 5 minutes, tolerate at-least-once execution if you call it asynchronously, and use neither .waitForTaskToken nor .sync. The fourth is what you give up: GetExecutionHistory, redrive, Activities and Distributed Map, plus a CloudWatch Logs bill.
Compare per execution, never per unit. One Standard execution costs several transitions; one Express execution costs a request plus its duration. Putting "$1.00 per million" beside "$25.00 per million" compares two units and drops duration.
Express vs Standard cost at high throughput
Take 50,000,000 monthly executions of an 8 state workflow in us-east-1. As Standard that is 400,000,000 transitions, 399,996,000 billable after the free tier: $9,999.90 per month.
As Express at 1.2 s and 64 MB billed, requests cost $50.00. GB-seconds come to 50,000,000 × 1.2 × (64 ÷ 1024) = 3,750,000, crossing the first tier. The first 3,600,000 at $0.00001667 is $60.01, the next 150,000 at $0.00000833 is $1.25, so duration is $61.26. That is $111.26 per month, 89.9 times cheaper, before logs.
When Express costs more than Standard
Per execution, Standard costs states × $0.000025 and Express costs $0.000001 + billed_seconds × $0.0000010419, that constant being (64 ÷ 1024) GB × $0.00001667/GB-s at minimum billed memory and the tier one rate. Set the two equal:
D_breakeven (seconds) = (states × $0.000025 − $0.000001) ÷ $0.0000010419
| States per execution | Express duration at which Standard becomes cheaper (64 MB, us-east-1) | Verdict |
|---|---|---|
| 2 | 47.0 s | A slow 2 state workflow loses |
| 4 | 95.0 s | Watch anything doing real I/O |
| 8 | 191.0 s | Standard wins past about 3 minutes |
| 12 | 287.0 s | Marginal at the 5 minute limit |
| 14 | 335.0 s | Past the 300 s Express maximum |
Past the 4,000 free transitions, at or above roughly 13 states no legal Express duration makes Standard cheaper at 64 MB; more memory raises the Express rate, as do logs. The table holds only inside the first GB-second tier: past 3,600,000 a month the blended Express rate falls and the line swings back.
Behind an API, add the API Gateway request charge, then price each mode in the Step Functions calculator.
Map states: why a 100-item array multiplies the bill
Add a Map state and the count stops tracking the steps you drew: a Distributed Map over 100 items bills 100 parent transitions before any child works.
Inline Map vs Distributed Map
Inline Map keeps the work in one execution and adds no transition for starting an iteration. The states inside each iteration are steps that run, so budget items x states per iteration; AWS publishes no worked example. It runs at most 40 iterations at once and queues the rest; the 256 KiB payload, and on Standard the 25,000 event history, are hard limits that fail the execution.
Distributed Map charges one transition per child, and bills in layers: the parent's transitions, always Standard because Express parents cannot run Distributed mode; the children under their own type; and what they call.
In AWS's own fan-out example, a five state Standard parent over 1,000,000 S3 objects bills 1,000,005 transitions, or $24.90 after the free tier. Express children at 30 s and 64 MB add $1.00 of requests and $31.25 of duration: $57.15 per month.
Setting ItemBatcher.MaxItemsPerBatch to 100, at an assumed 0.5 s per item so each child runs about 50 s, collapses the children into 10,000. Parent transitions drop from $24.90 to $0.15 and requests from $1.00 to $0.01, taking the total from $26.42 to $0.68 at that per-item rate. The GB-second charge does not move, because the compute did not.
Two caveats travel with that saving. A bigger batch grows the child's payload, and under AWS's memory estimate of 50 MB + definition size + execution data × Parallel or Map steps it can cross the next 64 MB chunk and double the billed GB-seconds: $0.68 becomes $1.20. Three limits then bind independently, so take the smallest: MaxItemsPerBatch 10,000, the 300 second child, and 256 KiB of payload. Price parent and children as separate calculator runs.
A Retry on a Distributed Map reruns all children in a new Map Run, not the failed ones, so a retried 1,000,000 item run re-bills 1,000,000 children. Use ToleratedFailureCount with per-child Retry blocks.
Express workflows bill twice: Step Functions and CloudWatch Logs
Batching fixes the transition count. The other half is usually bigger.
Express records no execution history. Seeing what happened means logging to CloudWatch Logs, at the Standard class vended logs rate: $0.50 per GB for the first 10 TB in us-east-1, plus $0.03 per GB-month stored, against separate 5 GB allowances. Every figure below assumes that class.
Past the allowance, logging overtakes the workflow here:
bytes_per_execution = ($0.000001 + billed_seconds × $0.0000010419) ÷ $0.50 × 1,073,741,824
At 1.2 s and 64 MB in us-east-1 that is 4,832 bytes, about 4.7 KiB. One TaskStateEntered and TaskStateExited pair carrying a few KB clears it alone. The 5 GB allowance absorbs roughly the first 1.1 million executions.
| Logging configuration | Assumed log output per execution | All-in monthly at 50M executions (us-east-1) | Keep or change |
|---|---|---|---|
level = ALL, include_execution_data = true | 8 KiB | $310.79 | Change, logs dominate |
level = ALL, include_execution_data = false | 2 KiB | $159.15 | Change unless you need timelines |
level = ERROR, 0.5% of executions failing | 1.5 KiB per failure | $111.26 | Keep, ingestion stays free |
level = OFF | none | $111.26 | Keep if you never debug |
Those log sizes are assumptions; AWS publishes no per-execution figure. Storage bills compressed bytes, so applying $0.03 to an uncompressed month makes that term an upper bound, and logs live forever unless you set retention. Measure IncomingBytes and StoredBytes.
Two knobs control it. Dropping level from ALL to ERROR removes the high volume entered and exited events while keeping failures, though Express log delivery is best effort, not a guaranteed audit trail. Setting includeExecutionData: false drops input, output and assignedVariables, so the bill and the payload exposure fall together.
Console-created Express state machines log by default. Anything created through the API, CLI, CloudFormation, CDK or Terraform is OFF.
Four charges your estimate probably missed
Logging is the biggest of these.
Retry storms are the quiet one. A 500,000 execution, 6 state Standard workflow in us-east-1 costs $74.90 a month; if 10% retry one state once, that becomes $76.15. An outage retrying everything three times needs no code change.
A Choice plus Wait plus Task poll costs 3 transitions per cycle. A .waitForTaskToken callback or .sync integration costs 1 and does not bill the idle wait. Both are Standard only.
What the workflow calls is usually larger. One transition invoking a function is $0.000025 plus a Lambda request plus GB-seconds, at $0.20 per million in us-east-1. Size the EventBridge rule that starts it and an ECS task through .sync the same way. Tags reach execution ARNs, not the functions invoked.
Region changes the rate. In sa-east-1 transitions cost 50% more while Express sits at the us-east-1 rate: the same 3,000,000 transition workload is $74.90 in Virginia and $112.35 in São Paulo, so Express argues harder there.
Set the two cost knobs in code, then watch them
Both decisions are made once, in code. Workflow type and log level move the bill by one to two orders of magnitude.
const stateMachine = new sfn.StateMachine(this, 'OrderFanout', {
stateMachineType: sfn.StateMachineType.EXPRESS, // billing dimension 1
definitionBody: sfn.DefinitionBody.fromChainable(definition),
timeout: cdk.Duration.minutes(5), // the Express hard maximum
logs: {
destination: logGroup, // /aws/vendedlogs/states/order-fanout
level: sfn.LogLevel.ERROR, // billing dimension 2
includeExecutionData: false, // drops input and output payloads
},
});
The CDK default is STANDARD, so Express is an explicit choice.
resource "aws_sfn_state_machine" "order_fanout" {
name = "order-fanout"
role_arn = aws_iam_role.sfn.arn
type = "EXPRESS" # changing this forces replacement
definition = local.fanout_definition
logging_configuration {
log_destination = "${aws_cloudwatch_log_group.sfn.arn}:*" # must end with :*
level = "ERROR"
include_execution_data = false
}
}
The provider default is STANDARD, and logging_configuration defaults to OFF. Prefix the log group with /aws/vendedlogs/states, or an account already at ten CloudWatch Logs resource policies cannot create the state machine.
Workflow type is immutable, so changing it destroys and recreates the machine. Reuse the name and the ARN survives; take a new one, as a parallel migration does, and every caller, EventBridge rule and API Gateway integration needs repointing.
A Terraform plan or CDK diff shows the workflow shape before anything runs, but a transition count needs your assumptions: which branch a Choice takes, how many items a Map processes, plus volume, retries and duration. I would rather argue about stateMachineType in review than migrate an ARN.
Then watch the console's usage and billing dashboard, IncomingBytes on the log group, and cost allocation tags.
Frequently Asked Questions
Is Step Functions cheaper than orchestrating in a single Lambda function?
Does the Step Functions free tier cover Express workflows?
Do synchronous Express workflows cost more than asynchronous ones?
Can I switch an existing state machine from Standard to Express?
What AWS Step Functions pricing comes down to
Five things for your next state machine review:
- Standard bills one dimension, counting every state that runs, retries and redrives included.
- Express bills two, and at 64 MB the cheaper option flips below roughly 13 states.
- Distributed Map bills one transition per child, so batching is the biggest fan-out lever.
- On Express, CloudWatch Logs is often the larger line past the allowance.
- Both knobs are IaC properties, and one cannot change after creation.
If logging is the next number to size, Amazon CloudWatch pricing covers it.
CloudBurn
Price Your Workflow Before You Build It
CloudBurn's Step Functions calculator prices Standard from executions and state transitions, and Express from requests, duration and memory, in your region.