Why Cost Explorer and your CUR don't match (2026)

Akal Cloud Updated 10 min read

Quick answer

They read the same dataset and present it differently. Across two AWS pages, six causes are documented: cash versus amortized basis, service regrouping such as EC2 - Instances against EC2 - Other, different rounding granularity, refresh cadences that differ, refunds which the Bills page excludes and Cost Explorer includes, and the CUR setting that refreshes closed bills. The invoice is the number you owe, and only closed months are worth reconciling.

Two AWS tools, one AWS bill, three different totals. It is the most common question in a FinOps onboarding and the answer is not "one of them is wrong". Between two AWS pages, the Billing versus Cost Explorer differences page and the CUR troubleshooting page, six distinct causes are documented. Once you can name which one you are looking at, the discrepancy usually stops being a discrepancy.

CauseDocumented on
Cash basis versus amortized basisDifferences page
Service regrouping, including data transferBoth pages
Different rounding granularityBoth pages
Refresh cadences that differDifferences page
Refunds included in one view, excluded in anotherDifferences page
The CUR automatic-refresh setting for closed billsCUR troubleshooting

Are Cost Explorer and the CUR even the same data?

Underneath, yes. The Cost Explorer documentation states it directly: "Cost Explorer uses the same dataset that is used to generate the AWS Cost and Usage Reports and the detailed billing reports."

So this is never a data-lineage problem. It is a presentation problem: the same underlying line items, grouped differently, rounded differently, refreshed on different clocks, and with different rules about what is included. Note also that the third participant, the Bills page, is a distinct thing again. AWS separates "billing data" from "Cost Explorer data" and lists the ways they diverge on a dedicated page: Knowing the differences between Billing and Cost Explorer data.

Which number is the one you owe?

The invoice. AWS is unambiguous: "when these data sources are different, the amount on your invoice is the final amount that you owe to AWS." Billing data "represents the amount that you owe to AWS."

That is the tiebreak for any argument about which tool to trust for accounts payable. It is not the tiebreak for engineering decisions, where the CUR's resource-level detail is the only thing that will tell you which of four hundred instances caused the change.

Why does an upfront purchase land in one month in one place and twelve in another?

Because billing is cash and Cost Explorer can be amortized. AWS: "billing data is always presented on a cash basis. It represents the amount that AWS charges you each month. For example, if you purchase a one-year, all-upfront Savings Plan in September, AWS will charge you the full cost for that Savings Plan in the September billing period."

Cost Explorer can show the same purchase spread out: "a one-year, all-upfront Savings Plan purchase will be spread evenly across the 12 months of the commitment term."

This is the single largest source of "our September bill tripled" panics, and it resolves entirely to which view someone had open. In the CUR the same choice is a column choice, and the CUR troubleshooting guide spells out the All Upfront case: "reservation/AmortizedUpfrontCostForUsage and reservation/EffectiveCost are the same rate for All Upfront Reserved Instances," both being "an equal division of the upfront payment for the Reserved Instance over the total hours of its term."

Those two names are the legacy CUR spellings used on that page. CUR 2.0 uses snake_case for the same fields, so the equivalents are reservation_amortized_upfront_cost_for_usage and reservation_effective_cost. Pointing a query at the wrong spelling is its own source of a zero that looks like a real number.

You wantCost ExplorerCUR 2.0 column
What you were charged this monthUnblendedline_item_unblended_cost
What the usage effectively costAmortizedreservation_effective_cost, savings_plan_savings_plan_effective_cost
Either, after discountsNet unblended / net amortizedline_item_net_unblended_cost, reservation_net_effective_cost, savings_plan_net_savings_plan_effective_cost

Pick one basis per report and state it on the report. Most reconciliation arguments are two people comparing an unblended number to an amortized one.

The net columns come with a condition attached that is easy to miss: AWS documents each of them as "included in your report only when your account has a discount in the applicable billing period". A query written against a net column on an account with no negotiated discount is querying a column that is not there.

What is the refunds gap?

A real and documented asymmetry: "the billing data on the Bills page (for example, in the Charges by service tab) excludes refunds, while Cost Explorer data includes refunds."

AWS gives the knock-on: "let's say that a portion of your taxes was refunded. On the Bills page, the Taxes by service tab will continue to show the full tax amount. The Cost Explorer data will show the post-refund tax amount." So a refund makes the two disagree in a category that has nothing obvious to do with the refund.

There is a second-order catch inside Cost Explorer itself. The dashboard's headline tiles are not on the same footing as the reports: "the Month-to-date costs and the Forecasted month end costs don't include refunds," and "your daily unblended costs don't include refunds." The Cost Explorer landing page can therefore disagree with a Cost Explorer report you built yourself.

Why does EC2 appear twice in Cost Explorer?

Deliberate regrouping. "With billing data, your AWS charges are grouped into AWS services on your invoice. To help with deep-dive analysis, Cost Explorer will group some costs differently… Instead of a single group for Amazon EC2 costs, Cost Explorer will group costs into EC2 - Instances and EC2 - Other."

The two examples AWS gives for what lands in EC2 - Other are "Amazon Elastic Block Store volumes or NAT gateways". It is routinely a team's second-largest line and routinely unexplained, because the name says nothing. Rather than guessing at the contents, resolve it in the CUR: the grouping is a Cost Explorer presentation choice, while line_item_usage_type survives it intact and names the actual meter. If the answer turns out to be NAT Gateway hourly and processing charges, those separate cleanly by usage type.

Where did the "Data Transfer" service go?

It was split up. "To help analyze data transfer costs, Cost Explorer groups your transfer costs by service. In billing data, data transfer costs are grouped into a single service named Data Transfer."

The CUR troubleshooting page describes the same split from the other side: "the Bills page in the Billing and Cost Management console shows data transfer charges as a separate Data Transfer grouping within your AWS Service Charges. Meanwhile, Cost and Usage Reports and Cost Explorer show data transfer charges as a usage type for each service."

So a network cost review that starts from the Bills page and one that starts from Cost Explorer will not even agree on which services are expensive. On this one, CUR and Cost Explorer agree with each other and the invoice is the odd one out.

What does the automatic refresh setting do?

It is the difference that is genuinely your fault, and the easiest to fix. Per the CUR troubleshooting page: "you can choose whether or not your Cost and Usage Report automatically refreshes a previously closed bill with any refunds, credits, or Support fees applied after the bill is finalized. Cost Explorer automatically reflects the same items. In this scenario, if you don't activate the automatic refresh on your Cost and Usage Report, then the Cost and Usage Report data won't match the Cost Explorer data."

Cost Explorer refreshes closed months whether you like it or not. The CUR only does if you asked. If your March CUR total has been stable for five months and Cost Explorer's March moved, this setting is the first thing to check.

Why does rounding matter at all?

Because the two are aggregated at different grains before you ever see them. "Your billing data and Cost Explorer data are processed at different granularities. For example, Cost Explorer data is available with hourly and resource-level granularity. Billing data is monthly and doesn't offer resource-level details. As a result, your billing data and Cost Explorer data might vary due to rounding."

The CUR page lists the same cause first among its reasons: "the billing features round cost data in different ways." Rounding differences scale with row count, so an account with millions of hourly resource-level rows shows a larger absolute gap than a small one, without anything being wrong.

Why is timing a difference on its own?

Both refresh daily, on separate clocks: "your billing data and Cost Explorer data are refreshed at least once per day. The cadence when they're refreshed might differ. This can result in differences for your month-to-date estimated charges."

Cost Explorer also lags by design: "all costs reflect your usage up to the previous day. For example, if today is December 2, the data includes your usage through December 1," with the caveat that in the current billing period "some data might be updated later than 24 hours."

Practical consequence: month-to-date comparisons between tools are close to meaningless. Reconcile on closed months.

Why do some line items show a blended cost of zero?

This one only bites organizations. Under consolidated billing, "unblended rates are the rates associated with an individual account's service usage… Blended rates are the rates associated with total usage in an organization averaged across accounts."

And then: "Amazon EC2 line items with a Reserved Instance discount have a blended rate of zero. For these line items, the LineItemType is Discounted Usage." Summing blended_cost across an RI-covered fleet therefore quietly under-reports. Use unblended for what an account was charged, amortized for what usage cost, and blended essentially never unless you specifically want the cross-account average.

A related surprise from the same page: "it's expected that your report has RIFee line items populated for All Upfront Reserved Instances, even though the RIFee is $0.00."

What does Cost Explorer cost?

The console is free. "You can view your costs and usage using the Cost Explorer user interface free of charge. You can also access your data programmatically using the Cost Explorer API. Each paginated API request incurs a charge of $0.01."

Note "paginated": each page is a request. A daily job that walks 40 pages across 12 months is 480 requests, not one. This is the main reason a data pipeline should read the CUR from S3 rather than poll the Cost Explorer API, quite apart from the CUR being more detailed. If you go that way, partition projection removes the Glue crawler from that pipeline as well.

Hourly granularity is separately metered. Per Estimating cost for Cost Explorer hourly granularity: "Cost Explorer offers hourly granularity data at a daily charge of $0.00000033 per usage record, which translates to $0.01 per 1,000 usage records monthly. A usage record corresponds to a line item with a specific resource and usage type." Billing is on a rolling window: "Cost Explorer bills you daily based on the total hourly usage records hosted in Cost Explorer for the past 14 days."

AWS's own worked example, which is worth reading before extrapolating:

"If you run one EC2 instance all day every day for the past month, and you have hourly granularity enabled, Cost Explorer will host 336 records per day (24 hours x 14 days) and charge you $0.0001 daily ($0.00000033 per record x 336 records), resulting in a monthly bill of $0.003."

Three tenths of a cent for one instance. The figure scales with distinct resource-and-usage-type combinations, not with spend, so a large fleet of small resources costs more to observe than a small fleet of large ones. Also worth knowing about the retention envelope: Cost Explorer "can display up to 13 months of historical data (if you have that much), the current month, and the forecasted costs for the next 18 months," and "you can't disable Cost Explorer after you enable it."

What can neither of them recover?

Be realistic about the floor before promising a reconciliation:

  • CUR backfill is a support ticket, not a button. "Open a support case to request a backfill of your cost data." And it has hard limits: no backfill "from before the date that you created the account," and none across a change of AWS Organizations membership or management account.
  • Resource IDs can be unrecoverable. "If you create your report with Report versioning set to Overwrite existing report, then you can't modify your Include resource ID selection after you create your report. To include resource IDs, you must create a new report."
  • Some resources are metered in aggregate. "Amazon EC2 Elastic IP addresses are metered in aggregate. This means that each line item in your report doesn't correspond with an individual Elastic IP address." Dedicated Hosts likewise report against the host ID, not the instance ID.
  • Legacy CUR columns move. "The columns that AWS includes in your report depend on your AWS usage," which is exactly the instability CUR 2.0's fixed schema was introduced to remove.

What to check, in order

  1. Are you comparing a closed month? If not, stop; the clocks differ.
  2. Same cost basis on both sides, unblended or amortized, not one of each?
  3. Refunds or credits in the period? The Bills page excludes them, Cost Explorer includes them.
  4. Is CUR automatic refresh of closed bills switched on?
  5. Are you summing a blended column across RI-discounted usage?
  6. Are you comparing an invoice service grouping against Cost Explorer's, particularly for EC2 and data transfer?
  7. If a gap survives all six, AWS's instruction is to "open a support case to request a review of your cost data," quoting the report name and billing period.

Steps 1 to 6 account for nearly every gap we have chased in production. Step 7 is rare, and worth reaching for only once the first six are genuinely ruled out.

See this on your own bill

Akal Cloud connects in about two minutes and shows the same numbers against your real AWS accounts.

Get started on AWS Marketplace

Related reading

  • How to see Amazon Bedrock cost per team (2026)

    Bedrock inference is not a resource you tag, so it has no owner column. The two features that give it one: IAM principal cost allocation in CUR 2.0, and application inference profiles. What each one answers, and why CloudWatch cannot answer it at all.

  • AWS Cost Categories: what split charge rules can and can't do (2026)

    Cost categories reorganise spend retroactively to the start of the month, which tags cannot. Split charge rules then divide shared cost across teams, but the result never leaves the console: it is absent from the CUR, Cost Explorer and Budgets.

  • ECS split cost allocation data, and how it differs from EKS (2026)

    Split cost allocation data covers ECS tasks as well as Kubernetes pods, but ECS gets no service-generated tags, no GPU splitting, and the same row multiplier. What you get free on each side, and the row count formula AWS publishes.

  • How to query your CUR in Athena without a Glue crawler (2026)

    Partition projection computes partitions from table properties instead of calling GetPartitions, so a CUR table needs no crawler and no MSCK REPAIR TABLE. The configuration, and the six ways it returns zero rows instead of an error.