AWS Builder Center

Why Is My AWS Bill So High? Easily finding NAT Gateway, Data Transfer, Networking and other Costs with CUR and Athena and Kiro

A practical guide for engineering and finance teams who want to understand their AWS spend at the resource level — without spending hours in the console.

Understanding AWS Billing Granularity

AWS billing is designed to be precise. With over 200 services, thousands of instance types, and pricing dimensions that vary by region, availability zone, usage type, and operation, the billing system captures an extraordinary level of detail. That granularity exists for good reason: it enables accurate cost attribution, supports chargeback models across teams, and gives you the data you need to optimise at every layer of your architecture.
That same granularity, however, creates a real challenge for day-to-day cost management. A single workload — say, a containerised application running on EKS — can generate charges across a dozen different billing dimensions: cluster hours, EC2 compute, EBS volumes, NAT Gateway hours, NAT Gateway data processing, cross-AZ data transfer, VPC endpoint hours, Elastic IP addresses, load balancer LCUs, CloudWatch logs, and ECR storage. Each of these appears as a separate line item, often under different service categories.
This is not a flaw in the billing system. It reflects the actual structure of what you're consuming. But it does mean that understanding the true cost of a service requires correlating charges across multiple categories — and that correlation is not something the AWS billing console does automatically.
The result is a common pattern: engineering teams spend significant time each month manually reconciling charges, finance teams receive bills they cannot interpret without engineering support, and cost anomalies — like unexpected data transfer charges — can go unnoticed for months before someone traces them to their source.



The Architecture: CUR + Athena + AWS Glue

The foundation of any serious AWS cost visibility solution is the AWS Cost and Usage Report (CUR). CUR is the most detailed billing dataset AWS provides. It captures every charge at the resource level, including:
  •  line_item_resource_id  — the ARN of the specific resource being charged
  •  line_item_usage_type  — the precise usage dimension (e.g.  USE1-NatGateway-Hours )
  •  line_item_operation  — what the resource was doing (e.g.  InterZone-Out ,  NatGateway ,  VpcEndpoint )
  •  line_item_availability_zone  — the AZ where the charge originated
  •  product_region  — the AWS region, already decoded
  •  line_item_line_item_description  — AWS's own description of the charge
When configured with Parquet format and Athena integration, CUR delivers data to S3 and AWS Glue automatically creates and maintains a queryable table. The architecture looks like this:
┌─────────────────────────────────────────────────────────────────┐
│ AWS Account │
│ │
│ ┌──────────────┐ hourly ┌─────────────────────────┐ │
│ │ AWS Billing │ ─────────────▶│ S3 Bucket (Parquet) │ │
│ │ (CUR) │ │ costreport-emb1/ │ │
│ └──────────────┘ └────────────┬────────────┘ │
│ │ │
│ ┌─────────▼──────────┐ │
│ │ AWS Glue Crawler │ │
│ │ (auto-updates │ │
│ │ schema/partitions│ │
│ └─────────┬──────────┘ │
│ │ │
│ ┌─────────▼──────────┐ │
│ │ Glue Data Catalog │ │
│ │ cur_database. │ │
│ │ cost_and_usage_ │ │
│ │ report │ │
│ └─────────┬──────────┘ │
│ │ │
│ ┌──────────────┐ ┌─────────▼──────────┐ │
│ │ Your laptop │◀─────────────────│ Amazon Athena │ │
│ │ (Python + │ SQL results │ (serverless SQL) │ │
│ │ boto3) │ └────────────────────┘ │
│ └──────────────┘ │
└─────────────────────────────────────────────────────────────────┘

Enabling CUR with Athena Integration

In the AWS Billing console, navigate to Cost and Usage Reports → Create report and configure:
Setting
Value
Report name
 costreport-<your-account-alias> 
Include resource IDs
✅ Enabled
Time granularity
Hourly
Report versioning
Overwrite existing report
Format
Parquet
Compression
Parquet
Additional schema elements
RESOURCES
Additional artifacts
Amazon Athena
Once enabled, AWS delivers the first report within 24 hours and creates a CloudFormation template in your S3 bucket that sets up the Glue crawler and catalog table automatically. Run the crawler once to initialise the table, then set it on a daily schedule to pick up new partitions.
The resulting Athena table is partitioned by  year  and  month , which keeps query costs low — a typical monthly analysis query scans only the relevant partition.



The Challenge of Hard to find Networking Costs

One category of charges deserves special attention because it consistently surprises teams: networking costs that appear under non-networking service categories.
AWS networking charges are distributed across the billing system in ways that reflect the underlying service ownership rather than a logical cost grouping:
Charge
Appears under
Why
NAT Gateway hours
EC2 - Other
NAT Gateways are an EC2 networking primitive
NAT Gateway data processing
EC2 - Other
Same resource, different dimension
Cross-AZ data transfer
EC2 - Other
Charged at the EC2 layer
VPC Endpoint hours
Amazon VPC
VPC service resource
Transit Gateway attachment hours
Amazon VPC
VPC service resource
Public IPv4 address charges
Amazon VPC
Introduced Feb 2024, $0.005/hr per IP
Inter-region data transfer
Varies by source service
Charged at the originating service
This distribution is architecturally correct — each charge belongs to the service that owns the resource. But it means that a team asking "what does our networking cost?" cannot get the answer from a single line item. They need to aggregate across multiple services and usage type patterns.



Building the Solution with Agentic Coding Assistant

Rather than writing these scripts from scratch, we used an agentic coding tool (Kiro) to generate the implementation. The key is giving the agent enough context about the CUR schema and the specific problem to produce accurate, working code.

Prompt 1: Hidden Networking Cost Analyzer (analyze-cost-transfer.py)

I have AWS Cost and Usage Reports (CUR) stored in S3 and queryable via Amazon Athena. The Glue catalog database is  cur_database  and the table is  cost_and_usage_report . The S3 output location for Athena results is  s3://my-bucket/athena-results/ . The table is partitioned by  year  and  month  (stored without zero-padding, e.g. month='2' not '02').Write a Python script using boto3 that queries this CUR table via Athena to surface all hidden networking costs — specifically: NAT Gateway hours and bytes (under AmazonEC2), cross-AZ data transfer (DataTransfer-Regional usage types under AmazonEC2), Transit Gateway attachment hours (under AmazonVPC), VPC Endpoint hours and bytes (under AmazonVPC), and Public IPv4 address charges (PublicIPv4 usage types under AmazonVPC).For each line item, include: usage type, service (product_code), region (product_region), cost, quantity, and the resource ARN (line_item_resource_id). Group the summary by category (NAT Gateway, Cross-AZ Transfer, etc.) and show individual resource ARNs with their costs under each category. Include a direct AWS Console URL for each category so the user can navigate straight to the relevant resource list. Output a formatted text report and a JSON file. Accept optional start/end date arguments, defaulting to the last full calendar month.

Prompt 2: Finance-Friendly Bill Explainer (generate-bill-explainer.py)

Using the same CUR Athena setup (database:  cur_database , table:  cost_and_usage_report , Athena output:  s3://my-bucket/athena-results/ ), write a Python script that generates a self-contained HTML report suitable for non-technical finance stakeholders.The report should:* Show total monthly spend prominently* List every AWS service with its cost and percentage of total bill* For each service, provide a plain-English explanation of what the service does and why it costs money (e.g. "Amazon EKS charges $0.10/hour per cluster regardless of workload size")* Include saving tips where applicable (e.g. "Upgrade past end-of-life Kubernetes versions to remove the extended support surcharge")* Allow clicking a service row to expand and show the underlying line items with usage type, operation, region, quantity, and cost
  • Be a single HTML file with no external dependencies — embeddable in email or shareable via a file linkCover at minimum these services with plain-English explanations: Amazon Bedrock (token-based pricing), Amazon SageMaker (GPU instances, Studio notebooks, storage), Amazon EKS (per-cluster fee, extended support surcharge), EC2 (instance hours, NAT Gateway, cross-AZ transfer), Amazon OpenSearch (OCU pricing), VPC (endpoints, Transit Gateway, public IPs), Amazon Comprehend, Amazon GuardDuty, Amazon Kinesis, RDS, ElastiCache, CloudWatch, KMS, ELB, and AWS Directory Service.



What the Scripts Produce

Hidden Networking Cost Report

Running  analyze-cost-transfer.py  against a real account produces output like this:
════════════════════════════════════════════════════════════════════════
HIDDEN NETWORKING COST REPORT | 2026-02-01 → 2026-03-01
Source: CUR Athena (cur_database.cost_and_usage_report)
════════════════════════════════════════════════════════════════════════

NAT Gateway — $342.36
NOTE: Billed under EC2, not under Networking or Data Transfer

$146.57 NatGateway-Hours | region: us-east-1
resources (9 found):
$30.24 arn:aws:ec2:us-east-1:123456789012:natgateway/nat-01f14ff135e1v7we1
$30.24 arn:aws:ec2:us-east-1:123456789012:natgateway/nat-0dda7b685bd864577
$23.90 arn:aws:ec2:us-east-1:123456789012:natgateway/nat-0e9c905a4da5934fe
...
console: https://console.aws.amazon.com/vpc/home?region=us-east-1#NatGateways:

VPC Endpoints / PrivateLink — $221.77
$161.28 USW2-VpcEndpoint-Hours | region: us-west-2
resources (8 found):
$20.16 arn:aws:ec2:us-west-2:123456789012:vpc-endpoint/vpce-0a280ebe2f7fa1854
$20.16 arn:aws:ec2:us-west-2:123456789012:vpc-endpoint/vpce-0fa7a77cdb13f4511
...

SUMMARY
$342 NAT Gateway
$221 VPC Endpoints / PrivateLink
$100 Transit Gateway
$80 Public IPv4 Addresses
$5 Cross-AZ Transfer
─────────────────────────────────────────────────────
$750 TOTAL (11.5% of $6,532 monthly spend)
Every resource ARN is actionable. You can take the NAT Gateway ID directly to the VPC console, identify which VPC it belongs to, and determine whether it can be consolidated or replaced with VPC endpoints for AWS service traffic.

Finance Bill Explainer

The HTML report opens in any browser. The top of the page shows the total spend for the month. Below it, each service appears as a row with cost, percentage of bill, and a one-line description. Clicking a row expands it to show:
  • A plain-English explanation of what the service does
  • Why the specific charges are incurred
  • A saving tip where one exists
  • The underlying line items with usage type, region, quantity, and cost
For example, the EKS row expands to:
Kubernetes cluster management fee AWS charges $0.10/hour (~$73/month) per Kubernetes cluster to manage the control plane, regardless of how many workloads are running on it. 💡 One cluster in us-west-2 is running a Kubernetes version past its standard support end date. AWS applies an extended support surcharge of $0.60/hour on top of the normal fee. Upgrading to a supported version removes this charge.
No AWS console access required. No technical knowledge needed. The file can be emailed or shared via a link.



Findings From a Real Account

Running these scripts against a production account for February 2026 surfaced several items worth acting on:
Idle Elastic IP addresses — Three public IPs allocated but not attached to any running resource. At $0.005/hour each, the cost is small individually, but they signal a cleanup opportunity and are trivially easy to release.
EKS Extended Support surcharge — One cluster running a Kubernetes version past its end-of-support date. The surcharge adds $0.60/hour on top of the standard $0.10/hour cluster fee — a 600% increase for that cluster. Upgrading the Kubernetes version removes the charge entirely.
13 VPC Interface Endpoints — Eight in us-west-2 at $20.16/month each, five in us-east-1. Interface endpoints are valuable for keeping traffic off the public internet, but unused endpoints still bill. Auditing which endpoints are receiving traffic (via CloudWatch metrics) and removing unused ones is a straightforward optimisation.
NAT Gateway data asymmetry — One NAT Gateway processed significantly more data than the others in the same region. This is a signal worth investigating: it may indicate that a service is routing traffic through NAT that could instead use a VPC endpoint, eliminating the per-GB processing charge.



Running the Scripts

Both scripts require (Scripts are generated by Kiro and for brevity have not been provided):
  • AWS credentials with  athena:StartQueryExecution ,  athena:GetQueryResults ,  s3:GetObject , and  s3:PutObject  permissions on the CUR bucket
  • CUR enabled with Athena integration and the Glue crawler having run at least once
# Install dependency
pip install boto3

# Hidden networking cost report — last full month
python3 cost_management/analyze-cost-transfer.py

# Specific date range
python3 cost_management/analyze-cost-transfer.py 2026-01-01 2026-02-01

# Finance HTML report
python3 cost_management/generate-bill-explainer.py
# Output: cost_management/aws-bill-explainer-2026-02.html



Summary

AWS Cost and Usage Reports, combined with Amazon Athena and the AWS Glue Data Catalog, provide a complete and queryable record of every charge in your account at the resource level. The complexity of AWS billing reflects the genuine complexity of what you are consuming — but that complexity does not have to be opaque.
With the right queries and a small amount of Python, you can:
  1. Surface networking costs that are distributed across multiple service categories and attribute them to specific resource ARNs
  1. Generate finance-ready reports that translate technical billing dimensions into plain English, without requiring AWS console access
Agentic coding tools accelerate this significantly. With a well-structured prompt that describes the CUR schema, the Athena configuration, and the specific cost categories you want to surface, a tool like Kiro can generate working, production-quality scripts in a single session — including the HTML report with per-service explanations.
Any opinions in this article are those of the individual author and may not reflect the opinions of AWS.
Enjoyed reading this content? Let the author know!

Your likes, comments, shares, and saves help creators reach more builders.

Loading recommendations

Loading article