
Cost Estimator
- 34 installs
- 41 repo stars
- Updated July 6, 2026
- aws-samples/sample-agent-skills-for-builders
cost-estimator is a Claude skill that estimates AWS costs for CDK projects using real-time AWS Price List data and generates Excel and Markdown reports.
About
This skill estimates AWS infrastructure costs for CDK projects using real-time pricing from the AWS Price List Bulk API. A developer uses it before deployment to scan CDK resources, fetch current prices, and generate Excel and Markdown cost reports. It also covers comparing costs across regions and calculating Bedrock model costs.
- Estimates AWS costs for CDK projects using real-time Price List Bulk API data
- Scans CDK resources and generates Excel and Markdown reports
- Supports all standard regions plus China regions with CNY pricing
Cost Estimator by the numbers
- 34 all-time installs (skills.sh)
- Ranked #777 of 1,039 Cloud & Infrastructure skills by installs in the Skillselion catalog
- Data as of Jul 30, 2026 (Skillselion catalog sync)
cost-estimator capabilities & compatibility
- Capabilities
- security scan · create install scripts
- Works with
- aws · excel
- Use cases
- data analysis · devops
- Pricing
- Free
What cost-estimator says it does
Estimate AWS costs for CDK projects using real-time pricing data.
AWS cost estimation tool for CDK projects with real-time pricing from AWS Price List Bulk API.
China regions (cn-north-1, cn-northwest-1) with CNY pricing
npx skills add https://github.com/aws-samples/sample-agent-skills-for-builders --skill cost-estimatorAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 34 |
|---|---|
| repo stars | ★ 41 |
| Last updated | July 6, 2026 |
| Repository | aws-samples/sample-agent-skills-for-builders ↗ |
What it does
Estimate AWS infrastructure costs for a CDK project before deployment and produce Excel and Markdown pricing reports.
Who is it for?
Developers estimating AWS CDK infrastructure costs before deployment.
Skip if: Non-CDK infrastructure or projects without synthesizable stacks.
When should I use this skill?
When calculating infrastructure costs, generating pricing reports, or analyzing CDK resource costs before deployment.
What you get
A per-region cost estimate is produced as an Excel and Markdown report saved to cost-estimates/.
- cost-estimate-{region}.xlsx
- cost-estimate-{region}.md
By the numbers
- 4-step flow (confirm, scan, fetch, generate)
- 2 report formats (Excel and Markdown)
Files
Cost Estimator
AWS cost estimation tool for CDK projects with real-time pricing from AWS Price List Bulk API.
When to Apply
Reference this skill when:
- Estimating AWS infrastructure costs
- Analyzing CDK project resource costs
- Generating pricing reports (Excel/Markdown)
- Comparing costs across AWS regions
- Calculating Bedrock model costs
How It Works
1. Confirm Region - Select target AWS region(s) 2. Scan Resources - Analyze CDK code for AWS resources 3. Fetch Pricing - Get real-time prices from AWS Bulk API 4. Generate Reports - Create Excel and Markdown reports
Prerequisites
- AWS CDK project with synthesizable stacks
- Python 3.8+ with pandas, openpyxl
- AWS credentials (for pricing API access)
Usage
# Fetch AWS pricing data
python scripts/fetch-aws-pricing.py --region us-east-1
# Generate Excel report
python scripts/generate-pricing-excel.py --input resources.json --output costs.xlsxOutput Files
Reports are saved to cost-estimates/:
cost-estimate-{region}.xlsx- Detailed Excel reportcost-estimate-{region}.md- Markdown summary
Supported Regions
- All standard AWS regions
- China regions (cn-north-1, cn-northwest-1) with CNY pricing
References
- CDK Analysis - Resource scanning guide
- Pricing API - AWS Bulk API usage
- Report Generation - Report creation
- Bedrock Pricing - AI model pricing
Cost Estimator Skill
Analyze and estimate AWS infrastructure costs for CDK projects using real-time pricing data from AWS.
Quick Start
Installation
npx skills add https://github.com/aws-samples/sample-agent-skills-for-builders --skill cost-estimatorBasic Usage
# Estimate costs for your CDK project in a specific region
python scripts/fetch-aws-pricing.py --region us-east-1
# Generate an Excel report with detailed cost breakdown
python scripts/generate-pricing-excel.py --input resources.json --output costs.xlsxPrerequisites
Before using this skill, ensure you have:
- Python 3.8+ installed
- AWS CDK project with synthesizable stacks
- Python packages:
pandas,openpyxl - AWS credentials configured (for AWS Pricing API access)
- Valid AWS regions (standard regions or China regions: cn-north-1, cn-northwest-1)
File Structure
cost-estimator/
├── README.md # This file
├── SKILL.md # Skill definition and metadata
├── scripts/
│ ├── fetch-aws-pricing.py # Fetch real-time pricing from AWS
│ ├── generate-pricing-excel.py # Create detailed Excel reports
│ └── bedrock-fallback-prices.json # AI model pricing fallback data
├── references/
│ ├── cdk-analysis.md # Guide to scanning CDK resources
│ ├── pricing-api.md # AWS Bulk API usage details
│ ├── report-generation.md # Report creation methodology
│ └── bedrock-pricing.md # Bedrock model pricing guideCommon Tasks
Estimate costs for a single region
python scripts/fetch-aws-pricing.py --region us-west-2Output files will be saved to cost-estimates/ directory:
cost-estimate-us-west-2.xlsx- Detailed spreadsheet with formulascost-estimate-us-west-2.md- Markdown summary for documentation
Generate costs for multiple regions
Run the pricing script for each region:
for region in us-east-1 us-west-2 eu-west-1; do
python scripts/fetch-aws-pricing.py --region $region
doneCalculate Bedrock model costs
The skill includes Bedrock model pricing data. Reference references/bedrock-pricing.md for supported models and pricing structures.
Output Files
Generated reports are saved to the cost-estimates/ directory:
| File | Purpose |
|---|---|
cost-estimate-{region}.xlsx | Comprehensive cost breakdown with per-resource costs |
cost-estimate-{region}.md | Summary report for sharing and documentation |
Supported AWS Regions
- Standard Regions: us-east-1, us-west-1, us-west-2, eu-west-1, eu-central-1, ap-southeast-1, ap-northeast-1, and all other public AWS regions
- China Regions: cn-north-1, cn-northwest-1 (with pricing in CNY)
References
For detailed documentation:
- [CDK Analysis](./references/cdk-analysis.md) - Learn how the skill scans and identifies AWS resources in your CDK code
- [Pricing API](./references/pricing-api.md) - Understand AWS Bulk API integration and pricing data sources
- [Report Generation](./references/report-generation.md) - Details on Excel and Markdown report creation
- [Bedrock Pricing](./references/bedrock-pricing.md) - AI model pricing including Claude, Titan, and Llama
Troubleshooting
AWS Credentials Not Found
Ensure your AWS credentials are configured:
aws configure
# or set AWS_PROFILE, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEYPython Dependencies Missing
Install required packages:
pip install pandas openpyxlNo Resources Found
Verify your CDK project can be synthesized:
cdk synthLicense
MIT
Amazon Bedrock Model Pricing Reference
Last updated: 2026-03-03. Source: https://aws.amazon.com/bedrock/pricing/
Bedrock models are NOT available in the Bulk Price List API. Use the hardcoded prices below when estimating Bedrock costs.
Anthropic Claude Models (On-Demand)
| Model | Input ($/1M tokens) | Output ($/1M tokens) | Batch Input | Batch Output | Cache Write (5m) | Cache Write (1h) | Cache Read |
|---|---|---|---|---|---|---|---|
| Claude Sonnet 4.6 | $3.00 | $15.00 | $1.50 | $7.50 | $3.75 | $6.00 | $0.30 |
| Claude Sonnet 4.6 — Long Context | $6.00 | $22.50 | $3.00 | $11.25 | $7.50 | $12.00 | $0.60 |
| Claude Opus 4.6 | $5.00 | $25.00 | $2.50 | $12.50 | $6.25 | $10.00 | $0.50 |
| Claude Opus 4.6 — Long Context | $10.00 | $37.50 | $5.00 | $18.75 | $12.50 | $20.00 | $1.00 |
| Claude Opus 4.5 | $5.00 | $25.00 | $2.50 | $12.50 | $6.25 | $10.00 | $0.50 |
| Claude Haiku 4.5 | $1.00 | $5.00 | $0.50 | $2.50 | $1.25 | $2.00 | $0.10 |
| Claude Sonnet 4.5 | $3.00 | $15.00 | $1.50 | $7.50 | $3.75 | $6.00 | $0.30 |
| Claude Sonnet 4.5 — Long Context | $6.00 | $22.50 | $3.00 | $11.25 | $7.50 | $12.00 | $0.60 |
| Claude Sonnet 4 | $3.00 | $15.00 | $1.50 | $7.50 | $3.75 | N/A | $0.30 |
| Claude Sonnet 4 — Long Context | $6.00 | $22.50 | $3.00 | $11.25 | $7.50 | N/A | $0.60 |
Long Context pricing applies when input exceeds 200K tokens (1M context window beta).
Amazon Embedding Models
| Model | Input ($/1M tokens) |
|---|---|
| Titan Text Embeddings V2 | $0.02 |
| Titan Multimodal Embeddings G1 | $0.80 (image), $0.02 (text) |
Amazon Nova Models
| Model | Input ($/1M tokens) | Output ($/1M tokens) |
|---|---|---|
| Nova Micro | $0.035 | $0.14 |
| Nova Lite | $0.06 | $0.24 |
| Nova Pro | $0.80 | $3.20 |
OpenSearch Serverless (AOSS)
Not in Bulk API. Published pricing:
| Component | Price | Unit |
|---|---|---|
| Indexing OCU | $0.24 | OCU-hour |
| Search & Query OCU | $0.24 | OCU-hour |
Minimum: 2 OCU for indexing + 2 OCU for search = 4 OCU total.
Neptune Serverless
Available via Bulk API (AmazonNeptune, filter usagetype=Serverless), but listed here for reference:
| Component | Price | Unit |
|---|---|---|
| NCU | $0.1608 | NCU-hour |
Usage Notes
- Default to on-demand pricing unless the user specifies batch or caching.
- For GraphRAG / RAG workloads, remember to account for document parsing token usage (entity extraction, relationship extraction, summarization) — typically 5-10x amplification of raw document token count.
- For agent workloads, estimate token usage per interaction and multiply by expected daily/monthly volume.
Step 2: Scan Resources and Confirm
Purpose
Automatically scan CDK code and architecture documentation in the project, discover all AWS resource definitions, present them to the user for confirmation, and generate a resource list JSON.
2.1 Discover CDK Directories
First, locate the CDK code directories in the project without assuming fixed paths:
Search patterns (by priority):
1. glob `**/cdk.json` → find CDK app entry point, infer lib directory from the app field
2. glob `**/lib/**/*.ts` files that import `aws-cdk-lib` or `@aws-cdk`
3. glob `**/infra/**/*.ts` with the same imports
4. glob `**/stack*.ts`, `**/construct*.ts`Record all discovered CDK source file paths for subsequent grep operations.
2.2 Resource Scan Checklist
For the discovered CDK files, search for AWS resources using the following patterns. Extract key configuration parameters for each resource type.
Compute
| Resource | grep Pattern | Fields to Extract |
|---|---|---|
| ECS Fargate | FargateTaskDefinition, FargateService | cpu, memoryLimitMiB, desiredCount |
| EC2 Instance | Instance(, ec2.Instance | instanceType |
| Lambda | Function(, lambda.Function | memorySize, timeout, runtime |
| ECS on EC2 | Ec2TaskDefinition, Ec2Service | instanceType, desiredCount |
Database & Storage
| Resource | grep Pattern | Fields to Extract |
|---|---|---|
| RDS | DatabaseInstance, DatabaseCluster | instanceType, engine, multiAz |
| Aurora | ServerlessCluster, DatabaseCluster | instanceType, minCapacity, maxCapacity |
| DynamoDB | Table(, dynamodb.Table | billingMode, GSI count |
| OpenSearch | opensearch.Domain, Domain( | instanceType, dataNodes, volumeSize |
| ElastiCache | CfnCacheCluster, elasticache | nodeType, numCacheNodes |
| S3 | Bucket( | bucket count |
Networking
| Resource | grep Pattern | Fields to Extract |
|---|---|---|
| VPC + NAT | Vpc(, natGateways | natGateways count, maxAzs |
| ALB | ApplicationLoadBalancer | internetFacing, associated services |
| NLB | NetworkLoadBalancer | same as above |
| API Gateway | RestApi, HttpApi, WebSocketApi | API type |
| CloudFront | Distribution | count |
Messaging & Integration
| Resource | grep Pattern | Fields to Extract |
|---|---|---|
| SQS | Queue( | queue count |
| SNS | Topic( | topic count |
| EventBridge | Rule(, EventBus | rule count |
| Step Functions | StateMachine | count |
Security & Management
| Resource | grep Pattern | Fields to Extract |
|---|---|---|
| Secrets Manager | Secret(, secretsmanager | secret count |
| KMS | Key(, kms.Key | key count |
| WAF | WebAcl, CfnWebACL | count |
2.3 Architecture Documentation Search
Search project documentation to supplement information that CDK code may miss:
Search paths:
- **/doc/**, **/docs/**
- README.md, ARCHITECTURE.md
- **/pricing*, **/cost*Look for in the documentation:
- Existing pricing/cost estimates (as ground truth reference)
- Services managed outside CDK (e.g., manually created EC2 GPU instances, Bedrock usage, SageMaker endpoints, etc.)
- Services mentioned in architecture descriptions but not found in CDK code
2.4 Module Assignment
Automatically infer module names from the directory structure, with no fixed mapping:
- Use the directory name as the default module name (e.g.,
modules/auth/→ "Auth") - If all CDK code is in a single directory, group by Stack name
- If unable to infer, label as "Main"
2.5 Confirm with User
Format the discovery results as a table and present to the user:
=== Discovered AWS Resources (N total) ===
| # | Module | AWS Service | Configuration | Pricing Basis | Source |
|---|--------|-------------|---------------|---------------|--------|
| 1 | ... | ECS Fargate | 1 vCPU, 2 GB, 2 tasks | hourly | lib/xxx.ts:42 |
| 2 | ... | RDS PostgreSQL | db.t4g.medium, Single-AZ | hourly | lib/xxx.ts:18 |
| ...
Minimal-cost services (not individually priced): S3(3), DynamoDB(2), SQS(4), SNS(1), Lambda(5)
Please confirm:
1. Are there any missing services to add manually? (e.g., Bedrock, SageMaker, manually created EC2, etc.)
2. Are there any services to exclude?
3. Are the resource configurations correct?Minimal-cost classification rules: The following services typically cost < $5/month under normal usage and are grouped as "minimal cost" without individual pricing:
- S3 (storage < 100 GB)
- DynamoDB PAY_PER_REQUEST (low request volume)
- SQS / SNS (low message volume)
- CloudFront (low traffic)
- Lambda (low invocation count)
- Secrets Manager (small number of secrets)
- API Gateway (marked as ~$1 at low request volume)
Users can promote any "minimal cost" service to individual pricing.
2.6 Output JSON Schema
After user confirmation, save to cost-estimates/.current-analysis.json:
{
"projectName": "<from package.json or directory name>",
"analyzedAt": "2026-03-03T10:00:00Z",
"cdkPaths": [
"<discovered CDK directories>"
],
"resources": [
{
"module": "<inferred module name>",
"service": "Amazon ECS Fargate",
"resourceId": "<unique-id>",
"required": true,
"configuration": {
"cpu": 1024,
"memoryMiB": 2048,
"desiredCount": 2,
"display": "1 vCPU, 2 GB memory, 2 tasks"
},
"pricingDimensions": {
"vcpuHours": 1460,
"gbHours": 2920
},
"sourceFile": "lib/ecs-service.ts",
"note": ""
}
],
"minimalCostServices": [
{ "service": "Amazon S3", "count": 3, "module": "App" },
{ "service": "Amazon DynamoDB", "count": 2, "module": "App" }
]
}pricingDimensions Calculation Rules
- ECS Fargate:
vcpuHours = (cpu/1024) * desiredCount * 730,gbHours = (memoryMiB/1024) * desiredCount * 730 - RDS / Aurora:
instanceHours = instanceCount * 730 - OpenSearch:
instanceHours = dataNodes * 730 - ALB / NLB:
lbHours = 730,lcuHours = 730(estimated at 1 LCU) - NAT:
natHours = natGateways * 730 - EC2:
instanceHours = instanceCount * 730 - Minimal-cost services: No pricingDimensions calculated; placed in
minimalCostServices
AWS Price List Bulk API Reference
Overview
The AWS Price List Bulk API is a public endpoint that requires no authentication. It is used to query On-Demand unit prices for any AWS service.
Script Usage
# Basic usage: query pricing for a given offerCode in a given region
python3 .claude/skills/cost-estimator/scripts/fetch-aws-pricing.py <region> \
--offer-code <CODE> [--filter KEY=VALUE ...] \
[--cache-dir DIR] [--output FILE]Examples
# ECS Fargate (returns both vCPU and Memory usagetype results)
python3 fetch-aws-pricing.py us-east-1 --offer-code AmazonECS --filter usagetype=Fargate
# RDS specific instance type
python3 fetch-aws-pricing.py us-east-1 --offer-code AmazonRDS \
--filter instanceType=db.t4g.medium --filter databaseEngine=PostgreSQL
# ALB
python3 fetch-aws-pricing.py us-east-1 --offer-code AWSELB --filter usagetype=LoadBalancer
# NAT Gateway (under the AmazonEC2 offerCode)
python3 fetch-aws-pricing.py us-east-1 --offer-code AmazonEC2 --filter usagetype=NatGateway
# OpenSearch
python3 fetch-aws-pricing.py us-east-1 --offer-code AmazonES --filter instanceType=r7g.large
# EC2 GPU instance
python3 fetch-aws-pricing.py cn-northwest-1 --offer-code AmazonEC2 \
--filter instanceType=g5.12xlarge --filter operatingSystem=LinuxOutput
Returns JSON where each result contains price, currency, unit, description, and key attributes.
Parameter Reference
--filter KEY=VALUE— Performs substring matching on product attributes. Can be repeated. Common keys:usagetype,instanceType,databaseEngine,operatingSystem,deploymentOption--cache-dir— Enables 24-hour file caching to avoid re-downloading large files--output— Writes to a file; if not specified, outputs to stdout
Common offerCodes
| offerCode | Included Services |
|---|---|
| AmazonECS | ECS Fargate, ECS on EC2 |
| AmazonRDS | RDS (all engines) |
| AmazonES | OpenSearch Service |
| AWSELB | ALB, NLB, CLB |
| AmazonEC2 | EC2 instances, NAT Gateway, EBS |
| AmazonElastiCache | ElastiCache |
| AmazonRedshift | Redshift |
| AmazonSageMaker | SageMaker |
| AmazonDynamoDB | DynamoDB |
Endpoints
| Region Type | Base URL |
|---|---|
| Global regions | https://pricing.us-east-1.amazonaws.com/offers/v1.0/aws/ |
| China regions | https://pricing.cn-northwest-1.amazonaws.com.cn/offers/v1.0/cn/ |
The script automatically selects the endpoint based on the region prefix (cn-).
Bedrock Pricing
Bedrock uses a different service code (AmazonBedrockService) and many newer models are not yet in the API. The script handles this automatically:
# Query Bedrock — auto-uses Query API + fallback for missing models
python3 fetch-aws-pricing.py us-east-1 --offer-code Bedrock \
--filter "model=Claude Sonnet 4.6"
# Filter by provider
python3 fetch-aws-pricing.py us-east-1 --offer-code Bedrock \
--filter "provider=Anthropic"How it works: 1. Queries AmazonBedrockService via boto3 Query API (paginated) 2. For models not found in API, loads fallback prices from scripts/bedrock-fallback-prices.json 3. API results take priority — fallback only fills gaps
Fallback file maintenance: Update scripts/bedrock-fallback-prices.json when new models are released. Once AWS adds them to the Pricing API, the fallback entries are automatically ignored.
Monthly Cost Calculation Reference
- Average hours per month: 730
- ECS Fargate:
(vCPU count × vCPU unit price + GB count × GB unit price) × task count × 730 - RDS / OpenSearch / EC2:
instance unit price × instance count × 730 - ALB:
ALB fixed fee × 730(LCU estimated at 1) - NAT:
NAT unit price × gateway count × 730
Report Style Guide
Excel Generation
Use generate-pricing-excel.py to convert the prepared JSON into a styled Excel file.
Script Usage
python3 .claude/skills/cost-estimator/scripts/generate-pricing-excel.py \
--input cost-estimates/.report.json \
--output cost-estimates/pricing-model-YYYY-MM-DD.xlsxInput JSON Format
{
"sheets": [
{
"name": "us-east-1",
"columns": ["Module", "AWS Service", "Required", "Configuration", "Est. Price/Month (USD $)", "Note"],
"columnWidths": [20, 35, 10, 40, 18, 40],
"rows": [
{"values": ["Console", "Amazon ECS Fargate", "Y", "1 vCPU, 2 GB, 2 tasks", 72.10, "API Service"], "module": "Console"},
{"values": ["", "Amazon RDS", "Y", "db.t4g.medium, Single-AZ", 58.95, ""], "module": "Console"},
{"values": ["Core", "Amazon ECS Fargate", "Y", "1 vCPU, 2 GB", 36.05, ""], "module": "Core"}
],
"totalRow": {"label": "Total", "value": 639.29},
"assumptions": [
"Deployed in us-east-1 region",
"ECS utilization: 10%, do not assume high usage to avoid auto-scaling cost"
]
},
{
"name": "Unit Pricing Reference",
"columns": ["AWS Service", "Configuration", "Unit Price", "Unit", "Monthly Cost"],
"columnWidths": [30, 40, 15, 15, 18],
"rows": [
{"values": ["Amazon ECS", "1 vCPU", 0.04048, "vCPU-hour", 29.55], "module": ""},
{"values": ["", "2 GiB memory", 0.00445, "GiB-hour", 6.50], "module": ""}
]
}
]
}Required Content
1. Pricing Sheet (one per region): Resource detail table + totalRow + assumptions 2. Unit Pricing Reference Sheet: A separate sheet listing all unit prices used and monthly cost calculations
Auto-Applied Styles
| Element | Style |
|---|---|
| Header row | Gray background (#607D8B) + white text + bold |
| Module coloring | Light blue/green/orange/purple/red/cyan/yellow/lime cycling per module |
| Total row | Dark gray background (#37474F) + white text + bold |
| Price column | Right-aligned, #,##0.00 format |
| Key Assumptions | Below the Total row, bold title + bulleted list |
| Borders | Thin gray lines |
---
Markdown Style
The agent directly generates the Markdown file, following this style:
Structure Template
# Pricing Model
## <Region> Pricing
| Module | AWS Service (BOM) | Required | Configuration | Est. Price/Month (<currency>) | Note |
|---|---|---|---|---:|---|
| Console | Amazon ECS Fargate | Y | 1 vCPU, 2 GB, 2 tasks | 72.10 | API Service |
| | Amazon RDS (PostgreSQL) | Y | db.t4g.medium, Single-AZ | 58.95 | |
| Core | Amazon ECS Fargate | Y | 1 vCPU, 2 GB | 36.05 | |
| **Total** | | | | **639.29** | |
**Key Assumptions:**
- Deployed in us-east-1 region
- ECS utilization: 10%
- ...
---
## Unit Pricing Reference
> Avg. hours in a month: **730**
| AWS Service | Configuration | Unit Price | Unit | Monthly Cost |
|---|---|---:|---|---:|
| Amazon ECS | 1 vCPU | 0.04048 | vCPU-hour | 29.55 |
| | 2 GiB memory | 0.00445 | GiB-hour | 6.50 |
| Amazon RDS | db.t4g.medium, Single-AZ | 0.065 | hour | 47.45 |
| ...Rules
- One
##section per region - Price column right-aligned (
---:) - Total row in bold
- Key Assumptions must follow each pricing table, explaining usage assumptions
- Unit Pricing Reference must be at the end, listing all fetched unit prices
- Minimal-cost services represented with
— - Module name displayed only on the first row of each module
{
"_comment": "Hardcoded Bedrock prices for models not yet in AWS Pricing API. Updated 2026-03-03. Source: https://aws.amazon.com/bedrock/pricing/",
"us-east-1": [
{"model": "Claude Sonnet 4.6", "provider": "Anthropic", "inferenceType": "Input tokens", "price": 0.003, "unit": "1K tokens"},
{"model": "Claude Sonnet 4.6", "provider": "Anthropic", "inferenceType": "Output tokens", "price": 0.015, "unit": "1K tokens"},
{"model": "Claude Sonnet 4.6", "provider": "Anthropic", "inferenceType": "Input tokens long context", "price": 0.006, "unit": "1K tokens"},
{"model": "Claude Sonnet 4.6", "provider": "Anthropic", "inferenceType": "Output tokens long context", "price": 0.0225, "unit": "1K tokens"},
{"model": "Claude Sonnet 4.6", "provider": "Anthropic", "inferenceType": "Input tokens batch", "price": 0.0015, "unit": "1K tokens"},
{"model": "Claude Sonnet 4.6", "provider": "Anthropic", "inferenceType": "Output tokens batch", "price": 0.0075, "unit": "1K tokens"},
{"model": "Claude Sonnet 4.6", "provider": "Anthropic", "inferenceType": "Cache write (5m)", "price": 0.00375, "unit": "1K tokens"},
{"model": "Claude Sonnet 4.6", "provider": "Anthropic", "inferenceType": "Cache write (1h)", "price": 0.006, "unit": "1K tokens"},
{"model": "Claude Sonnet 4.6", "provider": "Anthropic", "inferenceType": "Cache read", "price": 0.0003, "unit": "1K tokens"},
{"model": "Claude Opus 4.6", "provider": "Anthropic", "inferenceType": "Input tokens", "price": 0.005, "unit": "1K tokens"},
{"model": "Claude Opus 4.6", "provider": "Anthropic", "inferenceType": "Output tokens", "price": 0.025, "unit": "1K tokens"},
{"model": "Claude Opus 4.6", "provider": "Anthropic", "inferenceType": "Input tokens long context", "price": 0.01, "unit": "1K tokens"},
{"model": "Claude Opus 4.6", "provider": "Anthropic", "inferenceType": "Output tokens long context", "price": 0.0375, "unit": "1K tokens"},
{"model": "Claude Opus 4.6", "provider": "Anthropic", "inferenceType": "Input tokens batch", "price": 0.0025, "unit": "1K tokens"},
{"model": "Claude Opus 4.6", "provider": "Anthropic", "inferenceType": "Output tokens batch", "price": 0.0125, "unit": "1K tokens"},
{"model": "Claude Opus 4.6", "provider": "Anthropic", "inferenceType": "Cache write (5m)", "price": 0.00625, "unit": "1K tokens"},
{"model": "Claude Opus 4.6", "provider": "Anthropic", "inferenceType": "Cache write (1h)", "price": 0.01, "unit": "1K tokens"},
{"model": "Claude Opus 4.6", "provider": "Anthropic", "inferenceType": "Cache read", "price": 0.0005, "unit": "1K tokens"},
{"model": "Claude Opus 4.5", "provider": "Anthropic", "inferenceType": "Input tokens", "price": 0.005, "unit": "1K tokens"},
{"model": "Claude Opus 4.5", "provider": "Anthropic", "inferenceType": "Output tokens", "price": 0.025, "unit": "1K tokens"},
{"model": "Claude Opus 4.5", "provider": "Anthropic", "inferenceType": "Input tokens batch", "price": 0.0025, "unit": "1K tokens"},
{"model": "Claude Opus 4.5", "provider": "Anthropic", "inferenceType": "Output tokens batch", "price": 0.0125, "unit": "1K tokens"},
{"model": "Claude Opus 4.5", "provider": "Anthropic", "inferenceType": "Cache write (5m)", "price": 0.00625, "unit": "1K tokens"},
{"model": "Claude Opus 4.5", "provider": "Anthropic", "inferenceType": "Cache write (1h)", "price": 0.01, "unit": "1K tokens"},
{"model": "Claude Opus 4.5", "provider": "Anthropic", "inferenceType": "Cache read", "price": 0.0005, "unit": "1K tokens"},
{"model": "Claude Haiku 4.5", "provider": "Anthropic", "inferenceType": "Input tokens", "price": 0.001, "unit": "1K tokens"},
{"model": "Claude Haiku 4.5", "provider": "Anthropic", "inferenceType": "Output tokens", "price": 0.005, "unit": "1K tokens"},
{"model": "Claude Haiku 4.5", "provider": "Anthropic", "inferenceType": "Input tokens batch", "price": 0.0005, "unit": "1K tokens"},
{"model": "Claude Haiku 4.5", "provider": "Anthropic", "inferenceType": "Output tokens batch", "price": 0.0025, "unit": "1K tokens"},
{"model": "Claude Haiku 4.5", "provider": "Anthropic", "inferenceType": "Cache write (5m)", "price": 0.00125, "unit": "1K tokens"},
{"model": "Claude Haiku 4.5", "provider": "Anthropic", "inferenceType": "Cache write (1h)", "price": 0.002, "unit": "1K tokens"},
{"model": "Claude Haiku 4.5", "provider": "Anthropic", "inferenceType": "Cache read", "price": 0.0001, "unit": "1K tokens"},
{"model": "Claude Sonnet 4.5", "provider": "Anthropic", "inferenceType": "Input tokens", "price": 0.003, "unit": "1K tokens"},
{"model": "Claude Sonnet 4.5", "provider": "Anthropic", "inferenceType": "Output tokens", "price": 0.015, "unit": "1K tokens"},
{"model": "Claude Sonnet 4.5", "provider": "Anthropic", "inferenceType": "Input tokens long context", "price": 0.006, "unit": "1K tokens"},
{"model": "Claude Sonnet 4.5", "provider": "Anthropic", "inferenceType": "Output tokens long context", "price": 0.0225, "unit": "1K tokens"},
{"model": "Claude Sonnet 4.5", "provider": "Anthropic", "inferenceType": "Input tokens batch", "price": 0.0015, "unit": "1K tokens"},
{"model": "Claude Sonnet 4.5", "provider": "Anthropic", "inferenceType": "Output tokens batch", "price": 0.0075, "unit": "1K tokens"},
{"model": "Claude Sonnet 4.5", "provider": "Anthropic", "inferenceType": "Cache write (5m)", "price": 0.00375, "unit": "1K tokens"},
{"model": "Claude Sonnet 4.5", "provider": "Anthropic", "inferenceType": "Cache write (1h)", "price": 0.006, "unit": "1K tokens"},
{"model": "Claude Sonnet 4.5", "provider": "Anthropic", "inferenceType": "Cache read", "price": 0.0003, "unit": "1K tokens"},
{"model": "Titan Text Embeddings V2", "provider": "Amazon", "inferenceType": "Input tokens", "price": 0.00002, "unit": "1K tokens"},
{"model": "Titan Multimodal Embeddings G1", "provider": "Amazon", "inferenceType": "Input tokens (text)", "price": 0.00002, "unit": "1K tokens"},
{"model": "Titan Multimodal Embeddings G1", "provider": "Amazon", "inferenceType": "Input (image)", "price": 0.0008, "unit": "per image"},
{"model": "Nova Micro", "provider": "Amazon", "inferenceType": "Input tokens", "price": 0.000035, "unit": "1K tokens"},
{"model": "Nova Micro", "provider": "Amazon", "inferenceType": "Output tokens", "price": 0.00014, "unit": "1K tokens"},
{"model": "Nova Lite", "provider": "Amazon", "inferenceType": "Input tokens", "price": 0.00006, "unit": "1K tokens"},
{"model": "Nova Lite", "provider": "Amazon", "inferenceType": "Output tokens", "price": 0.00024, "unit": "1K tokens"},
{"model": "Nova Pro", "provider": "Amazon", "inferenceType": "Input tokens", "price": 0.0008, "unit": "1K tokens"},
{"model": "Nova Pro", "provider": "Amazon", "inferenceType": "Output tokens", "price": 0.0032, "unit": "1K tokens"}
]
}
#!/usr/bin/env python3
"""
AWS Price List Bulk API query tool.
All queries use the public Bulk API — no AWS credentials needed.
For Bedrock models not yet in the API, falls back to bedrock-fallback-prices.json.
Usage:
python3 fetch-aws-pricing.py <region> --offer-code CODE [--filter KEY=VALUE ...]
[--cache-dir DIR] [--output FILE]
Examples:
# Fargate prices
python3 fetch-aws-pricing.py us-east-1 --offer-code AmazonECS \
--filter usagetype=Fargate
# RDS db.t4g.medium
python3 fetch-aws-pricing.py cn-northwest-1 --offer-code AmazonRDS \
--filter instanceType=db.t4g.medium --filter databaseEngine=PostgreSQL
# NAT Gateway (under AmazonEC2)
python3 fetch-aws-pricing.py us-east-1 --offer-code AmazonEC2 \
--filter usagetype=NatGateway
# Bedrock — tries AmazonBedrockService then AmazonBedrock, plus fallback
python3 fetch-aws-pricing.py us-east-1 --offer-code Bedrock \
--filter "model=Claude Sonnet 4.6"
Uses only Python stdlib (urllib). No boto3 or AWS CLI dependency.
"""
import argparse
import hashlib
import json
import os
import sys
import time
import urllib.request
import urllib.error
from datetime import datetime, timezone
GLOBAL_BASE = "https://pricing.us-east-1.amazonaws.com"
CN_BASE = "https://pricing.cn-northwest-1.amazonaws.com.cn"
CACHE_TTL = 86400 # 24 hours
# Bedrock offer codes to try via Bulk API (in order of preference)
BEDROCK_SERVICE_CODES = ["AmazonBedrockService", "AmazonBedrock"]
# Path to fallback prices JSON (relative to this script)
BEDROCK_FALLBACK_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), "bedrock-fallback-prices.json")
# Attributes to keep in output
KEEP_ATTRS = (
# infra
"servicecode", "usagetype", "operation", "regionCode",
"instanceType", "instanceFamily",
"databaseEngine", "deploymentOption",
"operatingSystem", "cpuArchitecture",
"tenancy", "storage", "networkPerformance",
"vcpu", "memory",
# bedrock
"model", "provider", "inferenceType", "featuretype",
)
def is_cn(region: str) -> bool:
return region.startswith("cn-")
def base_url(region: str) -> tuple[str, str]:
host = CN_BASE if is_cn(region) else GLOBAL_BASE
path = "/offers/v1.0/cn" if is_cn(region) else "/offers/v1.0/aws"
return host, host + path
def cache_key(cache_dir: str, url: str) -> str:
h = hashlib.sha256(url.encode()).hexdigest()[:16]
return os.path.join(cache_dir, f"{h}.json")
def fetch_json(url: str, cache_dir: str | None = None) -> dict:
# Security: Only allow HTTPS URLs to prevent file:// scheme attacks
if not url.startswith("https://"):
print(f" Error: Only HTTPS URLs are allowed: {url}", file=sys.stderr)
return {}
if cache_dir:
cp = cache_key(cache_dir, url)
if os.path.exists(cp) and (time.time() - os.path.getmtime(cp)) < CACHE_TTL:
with open(cp) as f:
return json.load(f)
print(f" Fetching {url[:120]}...", file=sys.stderr)
req = urllib.request.Request(url, headers={"Accept": "application/json"})
try:
with urllib.request.urlopen(req, timeout=180) as resp: # nosec B310 - URL scheme validated above
data = json.loads(resp.read().decode())
except (urllib.error.HTTPError, urllib.error.URLError) as e:
print(f" Error: {e}", file=sys.stderr)
return {}
if cache_dir:
os.makedirs(cache_dir, exist_ok=True)
with open(cache_key(cache_dir, url), "w") as f:
json.dump(data, f)
return data
def resolve_region_url(host, api_base, offer_code, region, cache_dir):
index_url = f"{api_base}/{offer_code}/current/region_index.json"
index = fetch_json(index_url, cache_dir)
if not index:
return None
region_info = index.get("regions", {}).get(region)
if not region_info:
print(f" Region {region} not in {offer_code}", file=sys.stderr)
return None
ver_url = region_info.get("currentVersionUrl", "")
return (host + ver_url) if ver_url.startswith("/") else ver_url
def extract_on_demand_price(terms: dict, sku: str) -> dict:
for _k, offer in terms.get(sku, {}).items():
for _dk, dim in offer.get("priceDimensions", {}).items():
per_unit = dim.get("pricePerUnit", {})
for currency, val in per_unit.items():
try:
p = float(val)
if p > 0:
return {"price": p, "currency": currency, "unit": dim.get("unit", ""),
"description": dim.get("description", "")}
except ValueError:
pass
return {}
def match_filters(attrs: dict, filters: list[tuple[str, str]]) -> bool:
for key, val in filters:
attr_val = attrs.get(key, "")
if val.lower() not in attr_val.lower():
return False
return True
# ---------- Bulk API query ----------
def query_bulk(region, offer_code, filters, cache_dir):
host, api_base = base_url(region)
region_url = resolve_region_url(host, api_base, offer_code, region, cache_dir)
if not region_url:
return []
data = fetch_json(region_url, cache_dir)
if not data:
return []
products = data.get("products", {})
terms = data.get("terms", {}).get("OnDemand", {})
results = []
for sku, product in products.items():
attrs = product.get("attributes", {})
if not match_filters(attrs, filters):
continue
price_info = extract_on_demand_price(terms, sku)
if not price_info:
continue
results.append({
"sku": sku,
"productFamily": product.get("productFamily", attrs.get("productFamily", "")),
"attributes": {k: v for k, v in attrs.items() if k in KEEP_ATTRS},
**price_info,
})
return results
# ---------- Bedrock fallback from local JSON ----------
def query_bedrock_fallback(region, filters):
"""Load hardcoded Bedrock prices from bedrock-fallback-prices.json."""
if not os.path.exists(BEDROCK_FALLBACK_PATH):
print(f" Fallback file not found: {BEDROCK_FALLBACK_PATH}", file=sys.stderr)
return []
with open(BEDROCK_FALLBACK_PATH) as f:
data = json.load(f)
entries = data.get(region, data.get("us-east-1", []))
results = []
for entry in entries:
attrs = {
"model": entry.get("model", ""),
"provider": entry.get("provider", ""),
"inferenceType": entry.get("inferenceType", ""),
}
if not match_filters(attrs, filters):
continue
results.append({
"sku": "fallback",
"productFamily": "Amazon Bedrock (fallback)",
"attributes": attrs,
"price": entry["price"],
"currency": "USD",
"unit": entry.get("unit", "1K tokens"),
"description": f"Fallback: {entry['model']} {entry['inferenceType']} @ ${entry['price']}/{entry.get('unit','1K tokens')}",
})
return results
def main():
parser = argparse.ArgumentParser(
description="Query AWS Price List Bulk API",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog=__doc__,
)
parser.add_argument("region", help="AWS region (e.g. us-east-1, cn-northwest-1)")
parser.add_argument("--offer-code", required=True,
help="Service offer code. Use 'Bedrock' as shortcut for AmazonBedrockService + fallback.")
parser.add_argument("--filter", action="append", default=[],
help="Filter as KEY=VALUE (substring match). Repeatable.")
parser.add_argument("--cache-dir", default=None, help="Cache dir (24h TTL)")
parser.add_argument("--output", default=None, help="Output JSON file (default: stdout)")
args = parser.parse_args()
filters = []
for f in args.filter:
if "=" not in f:
print(f"Invalid filter (need KEY=VALUE): {f}", file=sys.stderr)
sys.exit(1)
k, v = f.split("=", 1)
filters.append((k, v))
offer_code = args.offer_code
# Bedrock shortcut: Bulk API + fallback for missing models
if offer_code.lower() in ("bedrock", "amazonbedrock", "amazonbedrockservice"):
api_results = []
for svc in BEDROCK_SERVICE_CODES:
api_results = query_bulk(args.region, svc, filters, args.cache_dir)
if api_results:
offer_code = svc
break
if not api_results:
offer_code = "AmazonBedrockService"
# Fill gaps from fallback for models not in API
api_models = {r["attributes"].get("model", "") for r in api_results}
fallback_results = query_bedrock_fallback(args.region, filters)
merged = list(api_results)
fallback_count = 0
for fb in fallback_results:
if fb["attributes"].get("model", "") not in api_models:
merged.append(fb)
fallback_count += 1
if fallback_count:
print(f" Added {fallback_count} entries from fallback (models not in API)", file=sys.stderr)
results = merged
else:
results = query_bulk(args.region, offer_code, filters, args.cache_dir)
output = {
"region": args.region,
"offerCode": offer_code,
"filters": {k: v for k, v in filters},
"resultCount": len(results),
"fetchedAt": datetime.now(timezone.utc).isoformat(),
"results": results,
}
text = json.dumps(output, indent=2, ensure_ascii=False)
if args.output:
os.makedirs(os.path.dirname(os.path.abspath(args.output)), exist_ok=True)
with open(args.output, "w") as f:
f.write(text)
print(f"Wrote {len(results)} results to {args.output}", file=sys.stderr)
else:
print(text)
if __name__ == "__main__":
main()
#!/usr/bin/env python3
"""
Generic JSON-to-styled-Excel converter for pricing reports.
Reads a JSON file describing the report structure and produces a formatted .xlsx.
Usage:
python3 generate-pricing-excel.py --input report.json --output pricing.xlsx
Input JSON schema:
{
"sheets": [
{
"name": "us-east-1", // Sheet tab name
"columns": ["Module", "Service", ...], // Header row
"columnWidths": [20, 35, ...], // Column widths
"rows": [ // Data rows
{"values": ["Console", "ECS Fargate", ...], "module": "Console"},
...
],
"totalRow": {"label": "Total", "value": 639.29}, // Optional total
"assumptions": ["Deployed in us-east-1", ...] // Optional Key Assumptions
}
]
}
- "module" on each row → color-banding (same module = same color, auto-assigned).
- Numeric values → right-aligned with #,##0.00 format.
- "—" → centered dash (minimal cost).
- "totalRow" → bold dark summary row.
- "assumptions" → printed below the table as "Key Assumptions:" bullet list.
Requires: openpyxl
"""
import argparse
import json
import os
import sys
try:
from openpyxl import Workbook
from openpyxl.styles import Alignment, Border, Font, PatternFill, Side
except ImportError:
print("Error: openpyxl required. Install: pip3 install openpyxl", file=sys.stderr)
sys.exit(1)
# --- Style constants ---
COLOR_PALETTE = [
"E3F2FD", "E8F5E9", "FFF3E0", "F3E5F5",
"FFEBEE", "E0F7FA", "FFF9C4", "F1F8E9",
]
HEADER_FILL = PatternFill(start_color="607D8B", end_color="607D8B", fill_type="solid")
HEADER_FONT = Font(bold=True, color="FFFFFF", size=11)
TOTAL_FILL = PatternFill(start_color="37474F", end_color="37474F", fill_type="solid")
TOTAL_FONT = Font(bold=True, color="FFFFFF", size=11)
BORDER = Border(
left=Side(style="thin", color="BDBDBD"),
right=Side(style="thin", color="BDBDBD"),
top=Side(style="thin", color="BDBDBD"),
bottom=Side(style="thin", color="BDBDBD"),
)
NUM_FMT = '#,##0.00'
def build_sheet(wb: Workbook, sheet_def: dict):
name = sheet_def.get("name", "Sheet")[:31]
ws = wb.create_sheet(title=name)
columns = sheet_def.get("columns", [])
col_widths = sheet_def.get("columnWidths", [])
rows = sheet_def.get("rows", [])
# Column widths
for i, w in enumerate(col_widths):
ws.column_dimensions[chr(65 + i)].width = w
# Header row
for ci, header in enumerate(columns, 1):
c = ws.cell(row=1, column=ci, value=header)
c.fill = HEADER_FILL
c.font = HEADER_FONT
c.alignment = Alignment(horizontal="center", vertical="center")
c.border = BORDER
# Module color assignment
color_map = {}
def module_fill(mod: str) -> PatternFill:
if not mod:
return PatternFill(fill_type=None)
if mod not in color_map:
color_map[mod] = COLOR_PALETTE[len(color_map) % len(COLOR_PALETTE)]
c = color_map[mod]
return PatternFill(start_color=c, end_color=c, fill_type="solid")
# Data rows
for ri, row_def in enumerate(rows, 2):
values = row_def.get("values", [])
mod = row_def.get("module", "")
fill = module_fill(mod)
for ci, val in enumerate(values, 1):
cell = ws.cell(row=ri, column=ci, value=val)
cell.border = BORDER
cell.fill = fill
# Numeric formatting for price columns
if isinstance(val, (int, float)):
cell.number_format = NUM_FMT
cell.alignment = Alignment(horizontal="right")
elif val == "—" or val == "—":
cell.alignment = Alignment(horizontal="center")
# Bold first column if it has a module name (first in group)
if values and values[0]:
ws.cell(row=ri, column=1).font = Font(bold=True)
# Total row
next_row = len(rows) + 2
total_def = sheet_def.get("totalRow")
if total_def:
tr = next_row
total_label = total_def.get("label", "Total")
total_value = total_def.get("value", 0)
for ci in range(1, len(columns) + 1):
cell = ws.cell(row=tr, column=ci, value="")
cell.fill = TOTAL_FILL
cell.font = TOTAL_FONT
cell.border = BORDER
ws.cell(row=tr, column=1, value=total_label).fill = TOTAL_FILL
ws.cell(row=tr, column=1).font = TOTAL_FONT
# Put total value in the price column (find "Price" or "Cost" in header)
price_col = None
for ci, header in enumerate(columns, 1):
if "price" in header.lower() or "cost" in header.lower():
price_col = ci
break
if price_col is None and len(columns) >= 5:
price_col = 5
if price_col:
# Use SUM formula instead of hardcoded value for accuracy
col_letter = chr(64 + price_col)
data_start = 2
data_end = len(rows) + 1
sum_formula = f"=SUM({col_letter}{data_start}:{col_letter}{data_end})"
tc = ws.cell(row=tr, column=price_col, value=sum_formula)
tc.fill = TOTAL_FILL
tc.font = TOTAL_FONT
tc.number_format = NUM_FMT
tc.alignment = Alignment(horizontal="right")
next_row = tr + 2
# Key Assumptions
assumptions = sheet_def.get("assumptions", [])
if assumptions:
ar = next_row
cell = ws.cell(row=ar, column=1, value="Key Assumptions:")
cell.font = Font(bold=True, size=11)
for i, text in enumerate(assumptions):
ws.cell(row=ar + 1 + i, column=1, value=f"• {text}").font = Font(size=10)
ws.merge_cells(start_row=ar + 1 + i, start_column=1,
end_row=ar + 1 + i, end_column=len(columns))
def main():
parser = argparse.ArgumentParser(description="JSON → styled Excel pricing report")
parser.add_argument("--input", required=True, help="Input JSON file describing the report")
parser.add_argument("--output", required=True, help="Output .xlsx file path")
args = parser.parse_args()
with open(args.input) as f:
report = json.load(f)
wb = Workbook()
wb.remove(wb.active)
for sheet_def in report.get("sheets", []):
build_sheet(wb, sheet_def)
if not wb.sheetnames:
print("Warning: no sheets defined in input JSON", file=sys.stderr)
wb.create_sheet("Empty")
os.makedirs(os.path.dirname(os.path.abspath(args.output)), exist_ok=True)
wb.save(args.output)
print(f"Written: {args.output} ({len(wb.sheetnames)} sheets)")
if __name__ == "__main__":
main()
Related skills
FAQ
Where does the pricing data come from?
Real-time prices are fetched from the AWS Price List Bulk API.
What output formats are produced?
A detailed Excel report and a Markdown summary, saved to the cost-estimates/ folder per region.