Skip to main content

Reference: Cost & Commitments

Cost Explorer data, cost anomalies, and reservation and savings plan data. This is the data behind the Cost Explorer, Cost Anomaly Detection and Commitment Coverage pages. Every route here needs View Costs. Date range rules are in Conventions; Defender spend is on the Security page.

Cost Explorer

GET /api/cost-explorer-summary

Permission: View Costs

Cost per subscription, resource group and resource.

Parameters

cost-explorer-summary parameters
NameRequiredNotes
startDate, endDatenoYYYY-MM-DD; applied only when both are sent; one alone is ignored. Max 366 days
tagFiltersnoURL-encoded JSON array of { "id": "<any>", "key": "<tag>", "value": "<value>", "operator": "equals" | "exists" | "missing" }. For exists/missing send "value": "". Invalid elements are dropped silently
tagModenoand (default) or or
locationsnoCSV of region names, exact and case-sensitive (e.g. eastus)
includeLocationsno1 adds location to resource rows

Errors

  • 400Cost date errors
  • 400Invalid tagFilters format. Must be a JSON-encoded array.
  • 400Invalid tagMode. Must be "and" or "or".
  • 500Failed to retrieve cost explorer hierarchy summary

Response: { compactSummaries[], availableFilters: { resourceTypes[] }, totalCount }. Each summary:

cost-explorer-summary summary fields
FieldMeaning
entityId, displayName, entityTypesubscription, resourceGroup, resource (cost rows at that level)
subscriptionId, subscriptionName, resourceGroupName?Location
monthToDateCostTotalActual cost: the current month without dates, or the exact window with dates
monthToDateAmortizedCostTotal?Amortized; reservation-covered resources show non-zero here
currency?Currency of the row
resourceTypeCountsPer type
historicalCounts{ historical, current }: cost for resources no longer in the inventory
totalCountMonthly cost documents for the row. Without dates the route loads the current and the previous month, so one resource can show totalCount: 2 with historicalCounts: { historical: 1, current: 1 } while monthToDateCostTotal still covers the current month only; with dates every row has totalCount: 1. The response-level totalCount is the sum of the row counts
location?With includeLocations=1

Notes

  • Tag and location filters keep resource rows only.
  • Don't sum monthToDateCostTotal across rows with different currency.

Example

GET /api/cost-explorer-summary?startDate=2026-09-01&endDate=2026-09-30&tagFilters=%5B%7B%22id%22%3A%221%22%2C%22key%22%3A%22env%22%2C%22value%22%3A%22prod%22%2C%22operator%22%3A%22equals%22%7D%5D

GET /api/cost-explorer-details

Permission: View Costs

Daily cost records for one entity. An out-of-scope subscription returns an empty result.

Parameters

cost-explorer-details parameters
NameRequiredNotes
entityTypeyessubscription, resourceGroup, resource
subscriptionIdyesExact
resourceGroupName, resourceNameper entityType
includeChildResourcesnotrue/false; subscription and resource group only
startDate, endDatenoWithout dates, you get the current month plus the two before

Errors

  • 400entityType and subscriptionId are required
  • 400entityType must be one of subscription, resourceGroup, resource
  • 400resourceGroupName is required for resourceGroup entityType
  • 400resourceName and resourceGroupName are required for resource entityType
  • 400includeChildResources must be true or false
  • 400Cost date errors
  • 500Failed to retrieve cost explorer details

Response: { entityType, entityId, costRecords[], totalCount }. Each record is one resource:

cost-explorer-details record fields
FieldMeaning
resourceId, originalResourceId, resourceName, resourceType, entityTypeIdentity
subscriptionId, subscriptionName, resourceGroupNameLocation
isHistorical, monthWhether the resource has left the inventory, and the month
recentDailyCosts[]Oldest first: { date, cost, amortizedCost?, currency, meterBreakdown?[] }. Each meter entry: { meterCategory, meterSubCategory?, meter?, cost, amortizedCost?, pricingModel?: OnDemand | Reservation | SavingsPlan | Spot, benefitName?, chargeType?, publisherType? }. A negative cost is a refund
costSnapshot{ monthToDateCost, monthTotalCost, averageDailyCost }
computedMetrics.previousMonthCostThe calendar month before today, not the month before the requested range. 0 when that month is not among the loaded months
costCollectionMetadata{ month, monthStartDate, monthEndDate, daysWithCostData, isFinalized }

Dates pick months, not days

startDate/endDate choose which months are loaded; recentDailyCosts is not trimmed to them. Filter by date yourself.

Example

GET /api/cost-explorer-details?entityType=resourceGroup&subscriptionId=<guid>&resourceGroupName=rg-prod&includeChildResources=true&startDate=2026-08-01&endDate=2026-10-15

Cost Anomalies

GET /api/anomalies-summary

Permission: View Costs

Resources with unusual cost changes. No parameters. The API returns everything detected with wide thresholds; the UI narrows further on screen.

Response: { recommendations[], totalCount, totalResourcesAnalyzed, lastUpdatedScanId, lastUpdatedTimestamp }. totalResourcesAnalyzed is installation-wide. Each recommendation is one resource:

anomalies-summary recommendation fields
FieldMeaning
Identity fields, currency?The resource
anomalies[]{ anomalyType: spike | pattern-shift, anomalyDate, severity: low | medium | high, baselineCost, anomalyCost, changePercent, changeDollar (signed), description }
totalAnomalies, highestSeverity, mostRecentDateSummary
totalCostImpact, mostRecentChangeDollarImpact
mostRecentIncrease, mostRecentDecrease{ date, changeDollar } or null
anomalyTypeCounts, severityCountsDictionaries
combinedCountsKey type|severity
directionCounts{ increase, decrease }

Errors

  • 500Failed to retrieve cost anomaly recommendations

Example

GET /api/anomalies-summary

Reservations and Savings Plans

Check the data tier before trusting utilization or expiry

StratoLens reads commitments with whatever billing access it has, and metadata.dataTier tells you how much detail that gave (see Permissions & Data Tier):

  • base: only spend from cost data. No utilization, expiry, SKU, term or scope. overallUtilizationPercent is 0 and every commitment counts as unknown utilization. Scripts must report "unknown", not "fully utilized" or "nothing expiring".
  • partial: one of reservations or savings plans has full detail; partialTierMissingSide says which one is missing.
  • enhanced: everything.

GET /api/commitment-inventory

Permission: View Costs

Reservations and savings plans, with spend, utilization and expiry. No parameters.

Response: { inventory[], metadata, totalCount, lastUpdatedScanId, lastUpdatedTimestamp }.

inventory[] item

commitment-inventory item fields
FieldMeaning
commitmentId, commitmentType, displayNamecommitmentType is reservation or savingsPlan
meterCategory, currencyBilling
subscriptionIds[], subscriptionNamesWhere it applies
orderId, sku, reservedResourceType, quantity, location, instanceFlexibilityPurchase details
commitmentAmountPerHour, commitmentCurrencySavings plan commitment
termP1Y/P3Y/P5Y
billingPlanMonthly or Upfront
scope, appliedScopes[]Single | Shared | ManagementGroup
benefitStartDate, expiryDate?, expiryDateTime?, provisioningStateLifecycle. Reservations carry both expiry fields (expiryDate is an ISO date, expiryDateTime an ISO timestamp); savings plans carry only expiryDateTime. Use expiryDateTime when you need one field for both
utilizationPercent1Day, utilizationPercent7Day, utilizationPercent30Day, utilizationTrend0-100, and UP | DOWN | STEADY
monthlyCost, monthlyWastageSpend

metadata (or null before the first collection)

commitment-inventory metadata fields
FieldMeaning
dataTierbase | partial | enhanced
totalReservations, totalSavingsPlansCounts
overallUtilizationPercent0 at base tier
totalMonthlyCommitmentSpend, totalEstimatedWastage, currencyTotals
commitmentsWithUnknownUtilization, commitmentsWithUnknownCostHow much is unknown
partialTierMissingSide?Which side lacks detail at partial tier
nearestExpiryDate?, nearestExpiryName?Next expiry

Notes

  • For scoped users, totals are recalculated over what they can see.
  • At base tier a commitment is visible to a scoped user only when one of its subscriptionIds is in that user's scope.

Errors

  • 500The error text, or Failed to fetch commitment inventory

Example

GET /api/commitment-inventory

GET /api/commitment-coverage-recommendations

Permission: View Costs

What to buy, plus underutilized and expiring commitment alerts. No parameters. Hidden items are included.

Response: { recommendations[], metadata, totalCount, lastUpdatedScanId, lastUpdatedTimestamp }. Each item has a recommendationSubType of purchase, underutilized or expiring, and:

commitment-coverage-recommendations item fields
Sub-typeFields
Commonid, subscriptionId?, subscriptionName?, subscriptionIds? (copied from the parent commitment), currency?, scope?, appliedScopes?
purchasearmSkuName, benefitType (ReservedInstances | SavingsPlan), recommendedQuantity, recommendedCommitmentAmountPerHour, term (P1Y or P3Y), lookBackPeriodDays (7, 30 or 60), location, reservedResourceType, estimatedOnDemandCost, estimatedSavingsPerMonth, savingsPercentage, coveragePercentage
underutilizedcommitmentId, commitmentName, commitmentType (reservation or savingsPlan), currentMonthlyCost, estimatedWastagePerMonth, currentUtilizationPercent, utilizationThresholdPercent, evaluationWindowDays (1, 7 or 30; absent on older documents, treat as 30)
expiringcommitmentId, commitmentName, commitmentType (reservation or savingsPlan), currentMonthlyCost, expiryDate, daysUntilExpiry (as of the scan), evaluationWindowDays (1, 7 or 30; absent on older documents, treat as 30)
metadata{ dataTier, totalPurchaseRecommendations, totalUnderutilizedAlerts, totalExpiringAlerts, totalPotentialSavingsPerMonth, totalWastagePerMonth, currency, lastEvaluationWindowDays? }

Notes

  • Base tier produces no expiring alerts.
  • Tenant-wide shared purchase recommendations are visible to every scoped user.

Errors

  • 500The error text, or Failed to fetch commitment coverage recommendations

Example

GET /api/commitment-coverage-recommendations

Was this page helpful?