<figure class="wp-block-image aligncenter"><img src="https://flowgenius.in/wp-content/uploads/2026/01/estimating-cost-per-workflow-execution.png" alt="Step by Step Guide to solve estimating cost per workflow execution" /> <figcaption style="text-align: center;">Step by Step Guide to solve estimating cost per workflow execution</p>
<hr />
</figcaption></figure>
<p style="margin-bottom: 2em; line-height: 1.9;"><strong>Who this is for:</strong> Developers, SREs, and product teams that run serverless workflows (Step Functions, Logic Apps, Cloud Workflows, etc.) and need predictable, per‑run cost estimates. <strong>We cover this in detail in the </strong><a href="https://flowgenius.in/n8n-cost-optimization-at-scale/">n8n Cost, Scaling & Infrastructure Economics Guide.</a></p>
<hr style="margin: 55px 0;" />
<h2 style="margin-bottom: 45px; line-height: 1.3;">Quick Diagnosis</h2>
<p style="margin-bottom: 2em; line-height: 1.9;"><strong>Problem</strong> – Serverless workflow runs often generate “mystery” bills because teams can’t forecast the cost of each execution.</p>
<p style="margin-bottom: 2em; line-height: 1.9;"><strong>Solution in a nutshell</strong> –</p>
<ul style="margin-bottom: 1.8em; line-height: 1.9;">
<li>List every billable piece of a workflow run.</li>
<li>Pull real‑time usage metrics.</li>
<li>Apply the provider’s current pricing tiers (via API).</li>
<li>Compare the model to the monthly invoice.</li>
</ul>
<blockquote style="margin: 2em 0; padding-left: 1em; border-left: 4px solid #e0e0e0;">
<p style="margin-bottom: 0; line-height: 1.9;">In production this usually shows up when a sudden spike in the invoice appears. You can’t tell which workflow caused it.</p>
</blockquote>
<hr style="margin: 55px 0;" />
<h2 style="margin-bottom: 45px; line-height: 1.3;">1. Billable Components of a Workflow Execution</h2>
<table style="border-collapse: collapse; width: 100%; margin-bottom: 2em;">
<thead>
<tr>
<th style="border: 1px solid #e0e0e0; padding: 13px; text-align: left;">Component</th>
<th style="border: 1px solid #e0e0e0; padding: 13px; text-align: left;">What It Covers</th>
<th style="border: 1px solid #e0e0e0; padding: 13px; text-align: left;">Typical Pricing Unit</th>
</tr>
</thead>
<tbody>
<tr>
<td style="border: 1px solid #e0e0e0; padding: 13px;"><strong>State Transitions</strong></td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Each step the engine moves through (Task, Choice, Parallel, …)</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">$ per 1,000 transitions</td>
</tr>
<tr>
<td style="border: 1px solid #e0e0e0; padding: 13px;"><strong>Compute Time</strong></td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">CPU‑seconds or GB‑seconds used by a task (Lambda, Cloud Run, …)</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">$ per GB‑second</td>
</tr>
<tr>
<td style="border: 1px solid #e0e0e0; padding: 13px;"><strong>Data Transfer</strong></td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">In/ out bytes between services in the same region</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">$ per GB (first 1 GB often free)</td>
</tr>
<tr>
<td style="border: 1px solid #e0e0e0; padding: 13px;"><strong>Durable Storage</strong></td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Execution history logs, payload snapshots</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">$ per GB‑month</td>
</tr>
<tr>
<td style="border: 1px solid #e0e0e0; padding: 13px;"><strong>Additional Services</strong></td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">API Gateway calls, managed secrets, etc.</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Varies</td>
</tr>
</tbody>
</table>
<p style="margin-bottom: 2em; line-height: 1.9;"><em>Provider‑specific examples (2024)</em></p>
<table style="border-collapse: collapse; width: 100%; margin-bottom: 2em;">
<thead>
<tr>
<th style="border: 1px solid #e0e0e0; padding: 13px; text-align: left;">Provider</th>
<th style="border: 1px solid #e0e0e0; padding: 13px; text-align: left;">State‑Transition Rate</th>
<th style="border: 1px solid #e0e0e0; padding: 13px; text-align: left;">Compute Rate</th>
<th style="border: 1px solid #e0e0e0; padding: 13px; text-align: left;">Data‑Transfer Rate</th>
</tr>
</thead>
<tbody>
<tr>
<td style="border: 1px solid #e0e0e0; padding: 13px;">AWS Step Functions – Standard</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">$0.025 / 1 k transitions</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">$0.00001667 / GB‑s (Lambda)</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">$0.09 / GB (cross‑region)</td>
</tr>
<tr>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Azure Logic Apps</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">$0.000025 / action</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">$0.000016 / GB‑s (Functions)</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Free intra‑region</td>
</tr>
<tr>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Google Cloud Workflows</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">$0.000004 / step</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">$0.0000025 / GB‑s (Cloud Run)</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">$0.12 / GB</td>
</tr>
</tbody>
</table>
<blockquote style="margin: 2em 0; padding-left: 1em; border-left: 4px solid #e0e0e0;">
<p style="margin-bottom: 0; line-height: 1.9;"><strong>EEFA note</strong> – Prices vary by region and tier (e.g., first 1 M transitions free). Pull the latest list from the provider’s pricing API rather than hard‑coding numbers.</p>
</blockquote>
<h3 style="margin-bottom: 45px; line-height: 1.3;">How to Identify Which Components Apply</h3>
<ol style="margin-bottom: 1.8em; line-height: 1.9;">
<li><strong>Inventory task types</strong> – Lambda, Fargate, Cloud Run, etc.</li>
<li><strong>Map each task</strong> to its billing model (compute, memory, request).</li>
<li><strong>Add engine counters</strong> – transitions, payload size, storage usage.</li>
</ol>
<blockquote style="margin: 2em 0; padding-left: 1em; border-left: 4px solid #e0e0e0;">
<p style="margin-bottom: 0; line-height: 1.9;">Missing a storage charge is easy if you only look at compute metrics.</p>
</blockquote>
<hr style="margin: 55px 0;" />
<h2 style="margin-bottom: 45px; line-height: 1.3;">2. Capture Real‑Time Usage Metrics</h2>
<table style="border-collapse: collapse; width: 100%; margin-bottom: 2em;">
<thead>
<tr>
<th style="border: 1px solid #e0e0e0; padding: 13px; text-align: left;">Metric</th>
<th style="border: 1px solid #e0e0e0; padding: 13px; text-align: left;">Source</th>
<th style="border: 1px solid #e0e0e0; padding: 13px; text-align: left;">Retrieval Method</th>
</tr>
</thead>
<tbody>
<tr>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Total Transitions</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">CloudWatch / Azure Monitor / Cloud Logging</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Metric API (e.g., <code>GetMetricStatistics</code>)</td>
</tr>
<tr>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Task Duration & Memory</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Lambda / Function logs</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Log parsing or CloudWatch Insights</td>
</tr>
<tr>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Payload Size</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Execution history JSON</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;"><code>describe‑execution</code> (AWS)</td>
</tr>
<tr>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Data Transfer</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">VPC Flow Logs / Cloud Logging</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Export to Athena / BigQuery</td>
</tr>
</tbody>
</table>
<p style="margin-bottom: 2em; line-height: 1.9;"><strong>Sample AWS CLI call – fetch transition count for the last hour</strong></p>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto; line-height: 1.9;">aws cloudwatch get-metric-statistics \
--namespace "AWS/States" \
--metric-name "ExecutionsStarted" \
--dimensions Name=StateMachineArn,Value=arn:aws:states:us-east-1:123456789012:stateMachine:MySM \
--statistics Sum \
--period 300 \
--start-time $(date -u -d '1 hour ago' +%Y-%m-%dT%H:%M:%SZ) \
--end-time $(date -u +%Y-%m-%dT%H:%M:%SZ)
</pre>
<blockquote style="margin: 2em 0; padding-left: 1em; border-left: 4px solid #e0e0e0;">
<p style="margin-bottom: 0; line-height: 1.9;"><strong>EEFA warning</strong> – Metrics may lag up to 5 minutes. For real‑time throttling prefer EventBridge rules over polling.</p>
</blockquote>
<hr style="margin: 55px 0;" />
<h2 style="margin-bottom: 45px; line-height: 1.3;">3. Build a Reproducible Cost Model</h2>
<h3 style="margin-bottom: 45px; line-height: 1.3;">Core Formula</h3>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto; line-height: 1.9;">Cost_per_execution =
(Transitions / 1,000) × Transition_Rate
+ Σ(Task_i) (Duration_i × Memory_i × Compute_Rate_i)
+ (Payload_In + Payload_Out) × Data_Transfer_Rate
+ Storage_Overhead
+ Service_Extras
</pre>
<p style="margin-bottom: 2em; line-height: 1.9;">At this point tweaking the Lambda memory is usually quicker than redesigning the whole workflow.</p>
<h3 style="margin-bottom: 45px; line-height: 1.3;">Example Calculation (AWS Step Functions + Lambda)</h3>
<table style="border-collapse: collapse; width: 100%; margin-bottom: 2em;">
<thead>
<tr>
<th style="border: 1px solid #e0e0e0; padding: 13px; text-align: left;">Variable</th>
<th style="border: 1px solid #e0e0e0; padding: 13px; text-align: left;">Value</th>
<th style="border: 1px solid #e0e0e0; padding: 13px; text-align: left;">Source</th>
</tr>
</thead>
<tbody>
<tr>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Transitions</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">12</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Execution history</td>
</tr>
<tr>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Transition_Rate</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">$0.025 / 1 k</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">AWS pricing API</td>
</tr>
<tr>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Lambda Duration</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">250 ms</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">CloudWatch logs</td>
</tr>
<tr>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Lambda Memory</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">256 MB</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Lambda config</td>
</tr>
<tr>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Compute_Rate (Lambda)</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">$0.000016667 per GB‑s</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">AWS pricing API</td>
</tr>
<tr>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Payload In/Out</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">45 KB</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Execution JSON</td>
</tr>
<tr>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Data‑Transfer Rate</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">$0.00 (same‑region free)</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Provider docs</td>
</tr>
<tr>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Storage Overhead</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">$0.000001 per KB</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Azure Blob pricing</td>
</tr>
<tr>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Service Extras</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">$0.00</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">None</td>
</tr>
</tbody>
</table>
<p style="margin-bottom: 2em; line-height: 1.9;"><strong>Step‑by‑step</strong></p>
<ol style="margin-bottom: 1.8em; line-height: 1.9;">
<li>Transitions – <code>(12 / 1,000) × $0.025 = $0.00030</code></li>
<li>Lambda compute –<br />
GB‑seconds = <code>0.25 s × 0.256 GB = 0.064 GB‑s</code><br />
Cost = <code>0.064 GB‑s × $0.000016667 = $0.00000107</code></li>
<li>Payload transfer – Free (same region).</li>
<li>Storage – <code>45 KB × $0.000001 = $0.000045</code></li>
</ol>
<p style="margin-bottom: 2em; line-height: 1.9;">Total ≈ <code>$0.000346</code> per execution.</p>
<blockquote style="margin: 2em 0; padding-left: 1em; border-left: 4px solid #e0e0e0;">
<p style="margin-bottom: 0; line-height: 1.9;"><strong>EEFA tip</strong> – Round up to the nearest $0.0001 when budgeting. Providers bill in whole‑cent increments per month, so many cheap executions can push you into the next cent bucket.</p>
</blockquote>
<hr style="margin: 55px 0;" />
<h2 style="margin-bottom: 45px; line-height: 1.3;">4. Automate the Estimation (Provider‑Agnostic Python Script)</h2>
<p style="margin-bottom: 2em; line-height: 1.9;">Below is a compact, production‑ready utility that:</p>
<ul style="margin-bottom: 1.8em; line-height: 1.9;">
<li>pulls the latest transition price from the AWS Pricing API,</li>
<li>enumerates successful executions,</li>
<li>extracts transition count and payload size, and</li>
<li>writes a CSV with per‑run cost estimates.</li>
</ul>
<h3 style="margin-bottom: 45px; line-height: 1.3;">4.1 Imports & Configuration (≈ 5 lines)</h3>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto; line-height: 1.9;">#!/usr/bin/env python3
import boto3, csv, json, datetime
from decimal import Decimal
STATE_MACHINE_ARN = "arn:aws:states:us-east-1:123456789012:stateMachine:MySM"
REGION = "us-east-1"
OUTPUT_FILE = "cost_estimates.csv"
</pre>
<h3 style="margin-bottom: 45px; line-height: 1.3;">4.2 Fetch the Current Transition Rate (≈ 5 lines)</h3>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto; line-height: 1.9;">def get_transition_rate():
pricing = boto3.client('pricing', region_name='us-east-1')
resp = pricing.get_products(
ServiceCode='AmazonStates',
Filters=[
{'Type': 'TERM_MATCH', 'Field': 'regionCode', 'Value': REGION},
{'Type': 'TERM_MATCH', 'Field': 'group', 'Value': 'StateTransition'}
],
MaxResults=1
)
price_json = json.loads(resp['PriceList'][0])
price = Decimal(price_json['terms']['OnDemand']
.popitem()[1]['priceDimensions']
.popitem()[1]['pricePerUnit']['USD'])
return price # $ per 1,000 transitions
</pre>
<h3 style="margin-bottom: 45px; line-height: 1.3;">4.3 List All Successful Executions (≈ 4 lines)</h3>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto; line-height: 1.9;">def list_executions():
sf = boto3.client('stepfunctions', region_name=REGION)
paginator = sf.get_paginator('list_executions')
for page in paginator.paginate(stateMachineArn=STATE_MACHINE_ARN,
statusFilter='SUCCEEDED'):
for exe in page['executions']:
yield exe['executionArn']
</pre>
<h3 style="margin-bottom: 45px; line-height: 1.3;">4.4 Pull Execution Details (≈ 5 lines)</h3>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto; line-height: 1.9;">def get_execution_details(arn):
sf = boto3.client('stepfunctions', region_name=REGION)
details = sf.describe_execution(executionArn=arn)
history = sf.get_execution_history(executionArn=arn, maxResults=1000)
transitions = len([e for e in history['events'] if e['type'] == 'TaskStateEntered'])
payload_kb = len(details['input'].encode('utf-8')) / 1024
return transitions, payload_kb
</pre>
<h3 style="margin-bottom: 45px; line-height: 1.3;">4.5 Compute the Cost for One Run (≈ 5 lines)</h3>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto; line-height: 1.9;">def estimate_cost(transitions, payload_kb, trans_rate, lambda_rate):
trans_cost = (Decimal(transitions) / 1000) * trans_rate
# Demo assumes a single Lambda of 250 ms & 256 MB
duration = Decimal('0.250')
memory = Decimal('0.256')
lambda_cost = duration * memory * lambda_rate
storage_cost = Decimal(payload_kb) * Decimal('0.001') / 1000 # $0.001 per KB placeholder
return trans_cost + lambda_cost + storage_cost
</pre>
<h3 style="margin-bottom: 45px; line-height: 1.3;">4.6 Main Routine – Write CSV (≈ 5 lines)</h3>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto; line-height: 1.9;">def main():
trans_rate = get_transition_rate()
lambda_rate = Decimal('0.000016667') # $ per GB‑s
with open(OUTPUT_FILE, 'w', newline='') as f:
w = csv.writer(f)
w.writerow(['ExecutionArn', 'Transitions', 'PayloadKB', 'EstimatedCostUSD'])
for arn in list_executions():
t, p = get_execution_details(arn)
cost = estimate_cost(t, p, trans_rate, lambda_rate)
w.writerow([arn, t, f"{p:.2f}", f"{cost:.6f}"])
print(f"✅ Estimates written to {OUTPUT_FILE}")
if __name__ == "__main__":
main()
</pre>
<p style="margin-bottom: 2em; line-height: 1.9;"><strong>Run the script</strong></p>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto; line-height: 1.9;">pip install boto3
python estimate_cost.py
</pre>
<blockquote style="margin: 2em 0; padding-left: 1em; border-left: 4px solid #e0e0e0;">
<p style="margin-bottom: 0; line-height: 1.9;"><strong>EEFA reminder</strong> – Replace the hard‑coded Lambda duration and memory with the actual values you retrieve from CloudWatch for production‑grade accuracy.</p>
</blockquote>
<hr style="margin: 55px 0;" />
<h2 style="margin-bottom: 45px; line-height: 1.3;">5. Validate Estimates Against Your Monthly Bill</h2>
<table style="border-collapse: collapse; width: 100%; margin-bottom: 2em;">
<thead>
<tr>
<th style="border: 1px solid #e0e0e0; padding: 13px; text-align: left;">Validation Step</th>
<th style="border: 1px solid #e0e0e0; padding: 13px; text-align: left;">Action</th>
<th style="border: 1px solid #e0e0e0; padding: 13px; text-align: left;">Tool</th>
</tr>
</thead>
<tbody>
<tr>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Aggregate Estimated Cost</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Sum the CSV column (`awk ‘{sum+=$4} END{print sum}’ cost_estimates.csv`)</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Bash / PowerShell</td>
</tr>
<tr>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Pull Official Invoice</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Export the “Bills” CSV from the provider console</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">AWS Billing, Azure Cost Management, GCP Billing Export</td>
</tr>
<tr>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Reconcile</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Compare the two totals; investigate any > 10 % variance</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Excel pivot, pandas diff</td>
</tr>
<tr>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Root‑Cause Analysis</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Look for missing items (e.g., API Gateway, cross‑region traffic)</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Execution logs, service‑usage reports</td>
</tr>
</tbody>
</table>
<blockquote style="margin: 2em 0; padding-left: 1em; border-left: 4px solid #e0e0e0;">
<p style="margin-bottom: 0; line-height: 1.9;"><strong>EEFA insight</strong> – Free‑tier quotas (e.g., first 1 M transitions) must be subtracted before applying rates; otherwise you’ll over‑estimate.</p>
</blockquote>
<hr style="margin: 55px 0;" />
<h2 style="margin-bottom: 45px; line-height: 1.3;">6. Advanced Cost‑Optimization Checklist</h2>
<ul style="margin-bottom: 1.8em; line-height: 1.9;">
<li>☐ Disable long‑term <strong>execution history</strong> storage unless debugging.</li>
<li>☐ Merge tiny Lambda tasks to cut down transition count.</li>
<li>☐ Switch high‑frequency flows to the <strong>Express</strong> tier if latency permits.</li>
<li>☐ Compress payloads (gzip) before cross‑region calls.</li>
<li>☐ Schedule batch jobs during off‑peak discount windows (if offered).</li>
<li>☐ Set CloudWatch alarms on sudden spikes in transition count.</li>
</ul>
<hr style="margin: 55px 0;" />
<h3 style="margin-bottom: 45px; line-height: 1.3;">Bottom Line</h3>
<p style="margin-bottom: 2em; line-height: 1.9;">By cataloguing every billable element, harvesting precise runtime metrics, applying up‑to‑date pricing via the provider API, and continuously reconciling against the actual invoice, you can predict the cost of a single workflow execution with sub‑cent accuracy. This keeps your serverless spend under control.</p>
Step by Step Guide to solve estimating cost per workflow execution
Who this is for: Developers, SREs, and product teams that run serverless workflows (Step Functions, Logic Apps, Cloud Workflows, etc.) and need predictable, per‑run cost estimates. We cover this in detail in the n8n Cost, Scaling & Infrastructure Economics Guide.
Quick Diagnosis
Problem – Serverless workflow runs often generate “mystery” bills because teams can’t forecast the cost of each execution.
Solution in a nutshell –
List every billable piece of a workflow run.
Pull real‑time usage metrics.
Apply the provider’s current pricing tiers (via API).
Compare the model to the monthly invoice.
In production this usually shows up when a sudden spike in the invoice appears. You can’t tell which workflow caused it.
1. Billable Components of a Workflow Execution
Component
What It Covers
Typical Pricing Unit
State Transitions
Each step the engine moves through (Task, Choice, Parallel, …)
$ per 1,000 transitions
Compute Time
CPU‑seconds or GB‑seconds used by a task (Lambda, Cloud Run, …)
$ per GB‑second
Data Transfer
In/ out bytes between services in the same region
$ per GB (first 1 GB often free)
Durable Storage
Execution history logs, payload snapshots
$ per GB‑month
Additional Services
API Gateway calls, managed secrets, etc.
Varies
Provider‑specific examples (2024)
Provider
State‑Transition Rate
Compute Rate
Data‑Transfer Rate
AWS Step Functions – Standard
$0.025 / 1 k transitions
$0.00001667 / GB‑s (Lambda)
$0.09 / GB (cross‑region)
Azure Logic Apps
$0.000025 / action
$0.000016 / GB‑s (Functions)
Free intra‑region
Google Cloud Workflows
$0.000004 / step
$0.0000025 / GB‑s (Cloud Run)
$0.12 / GB
EEFA note – Prices vary by region and tier (e.g., first 1 M transitions free). Pull the latest list from the provider’s pricing API rather than hard‑coding numbers.
How to Identify Which Components Apply
Inventory task types – Lambda, Fargate, Cloud Run, etc.
Map each task to its billing model (compute, memory, request).
EEFA tip – Round up to the nearest $0.0001 when budgeting. Providers bill in whole‑cent increments per month, so many cheap executions can push you into the next cent bucket.
4. Automate the Estimation (Provider‑Agnostic Python Script)
Below is a compact, production‑ready utility that:
pulls the latest transition price from the AWS Pricing API,
enumerates successful executions,
extracts transition count and payload size, and
writes a CSV with per‑run cost estimates.
4.1 Imports & Configuration (≈ 5 lines)
#!/usr/bin/env python3
import boto3, csv, json, datetime
from decimal import Decimal
STATE_MACHINE_ARN = "arn:aws:states:us-east-1:123456789012:stateMachine:MySM"
REGION = "us-east-1"
OUTPUT_FILE = "cost_estimates.csv"
def list_executions():
sf = boto3.client('stepfunctions', region_name=REGION)
paginator = sf.get_paginator('list_executions')
for page in paginator.paginate(stateMachineArn=STATE_MACHINE_ARN,
statusFilter='SUCCEEDED'):
for exe in page['executions']:
yield exe['executionArn']
4.4 Pull Execution Details (≈ 5 lines)
def get_execution_details(arn):
sf = boto3.client('stepfunctions', region_name=REGION)
details = sf.describe_execution(executionArn=arn)
history = sf.get_execution_history(executionArn=arn, maxResults=1000)
transitions = len([e for e in history['events'] if e['type'] == 'TaskStateEntered'])
payload_kb = len(details['input'].encode('utf-8')) / 1024
return transitions, payload_kb
Compare the two totals; investigate any > 10 % variance
Excel pivot, pandas diff
Root‑Cause Analysis
Look for missing items (e.g., API Gateway, cross‑region traffic)
Execution logs, service‑usage reports
EEFA insight – Free‑tier quotas (e.g., first 1 M transitions) must be subtracted before applying rates; otherwise you’ll over‑estimate.
6. Advanced Cost‑Optimization Checklist
☐ Disable long‑term execution history storage unless debugging.
☐ Merge tiny Lambda tasks to cut down transition count.
☐ Switch high‑frequency flows to the Express tier if latency permits.
☐ Compress payloads (gzip) before cross‑region calls.
☐ Schedule batch jobs during off‑peak discount windows (if offered).
☐ Set CloudWatch alarms on sudden spikes in transition count.
Bottom Line
By cataloguing every billable element, harvesting precise runtime metrics, applying up‑to‑date pricing via the provider API, and continuously reconciling against the actual invoice, you can predict the cost of a single workflow execution with sub‑cent accuracy. This keeps your serverless spend under control.