Microsoft Azureintermediate

How to Enable the Cost Management Data Export API in Azure

Verify subscription type and permissions for Cost Management API access

8 min readUpdated January 2025

The Azure Cost Management Data Export API allows programmatic access to your billing and usage data, enabling automated cost analysis, custom reporting, and integration with third-party financial systems. This guide shows you how to verify your subscription eligibility, configure API access, and authenticate for programmatic cost data retrieval.

Overview

Unlike the Azure Portal's Cost Management interface, the Cost Management Data Export API provides:

  • Automated cost data retrieval for scheduled reporting and analysis
  • Integration capabilities with external systems (ERPs, FinOps platforms, custom dashboards)
  • Programmatic export management for creating, updating, and triggering cost exports
  • Granular data access with filtering, grouping, and aggregation options
  • Real-time API queries without waiting for scheduled export files

The API is available through Azure REST API endpoints and can be accessed using:

  • Azure CLI (az costmanagement commands)
  • PowerShell Az module (Az.CostManagement cmdlets)
  • Direct REST API calls with OAuth2 authentication
  • Azure SDKs (Python, .NET, Java, JavaScript)

This guide covers eligibility requirements, authentication setup, and common API operations.

Prerequisites

Before you begin, ensure you have:

  • An Enterprise Agreement (EA), Microsoft Customer Agreement (MCA), or Pay-As-You-Go subscription
    • Note: Not all API features are available for all subscription types (detailed below)
  • Cost Management Contributor or Cost Management Reader role at the appropriate scope
  • Azure CLI 2.30.0+ or PowerShell 7+ with Az modules installed
  • For direct API access: Ability to create Azure AD App Registrations (Service Principal)
  • Basic understanding of OAuth2 authentication and REST APIs
  • (Optional) Familiarity with Azure Resource Manager scopes

Subscription Type Support Matrix

FeatureEAMCAPay-As-You-GoCSP
Usage Details API
Cost Management Exports APILimited
Budgets API
Cost Allocation Rules API
Pricing Sheet API

Verify Your Subscription Type

# Check subscription type
az account show --query "{Name:name, Type:tenantId, SubscriptionId:id}" -o table

# Check billing account type (for EA/MCA)
az billing account list --query "[].{Name:name, Type:agreementType}" -o table

# Expected output for EA:
# Name        Type
# 12345678    EnterpriseAgreement

# Expected output for MCA:
# Name                     Type
# abcd-1234-efgh-5678     MicrosoftCustomerAgreement

Method 1: Enable API Access Using Azure CLI

The simplest way to access the Cost Management API is through Azure CLI.

Step 1: Verify Azure CLI Installation

# Check Azure CLI version (requires 2.30.0+)
az --version

# Upgrade if needed
# macOS: brew upgrade azure-cli
# Windows: Download from https://aka.ms/installazurecliwindows
# Linux: curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash

# Login to Azure
az login

# Set default subscription
az account set --subscription "YOUR-SUBSCRIPTION-ID"

Step 2: Verify API Permissions

# Check your Cost Management role assignments
az role assignment list \
  --assignee $(az account show --query user.name -o tsv) \
  --scope "/subscriptions/$(az account show --query id -o tsv)" \
  --query "[?contains(roleDefinitionName, 'Cost')].{Role:roleDefinitionName, Scope:scope}" -o table

# Expected roles:
# - Cost Management Contributor (read/write access)
# - Cost Management Reader (read-only access)

Step 3: Test API Access with Usage Details Query

# Query usage details for current month
az costmanagement query \
  --type "Usage" \
  --scope "/subscriptions/$(az account show --query id -o tsv)" \
  --timeframe "MonthToDate" \
  --dataset-aggregation '{
    "totalCost": {
      "name": "PreTaxCost",
      "function": "Sum"
    }
  }' \
  --dataset-grouping '{
    "type": "Dimension",
    "name": "ResourceGroup"
  }'

Expected output:

{
  "id": "subscriptions/.../providers/Microsoft.CostManagement/query/...",
  "name": "...",
  "type": "Microsoft.CostManagement/query",
  "properties": {
    "nextLink": null,
    "columns": [
      {"name": "PreTaxCost", "type": "Number"},
      {"name": "ResourceGroup", "type": "String"},
      {"name": "UsageDate", "type": "Number"}
    ],
    "rows": [
      [123.45, "production-rg", 20250115],
      [67.89, "dev-rg", 20250115]
    ]
  }
}

Step 4: List Existing Exports

# List all cost exports at subscription scope
az costmanagement export list \
  --scope "/subscriptions/$(az account show --query id -o tsv)"

# List exports at billing account scope (EA/MCA only)
BILLING_ACCOUNT_ID=$(az billing account list --query "[0].id" -o tsv)
az costmanagement export list --scope "$BILLING_ACCOUNT_ID"

Method 2: Enable API Access Using PowerShell

PowerShell provides cmdlets for Cost Management API operations.

Step 1: Install Required Modules

# Install Az modules
Install-Module -Name Az.CostManagement -Repository PSGallery -Force
Install-Module -Name Az.Accounts -Repository PSGallery -Force
Install-Module -Name Az.Billing -Repository PSGallery -Force

# Connect to Azure
Connect-AzAccount

# Set subscription context
Set-AzContext -SubscriptionId "YOUR-SUBSCRIPTION-ID"

Step 2: Verify API Access

# Get current context
$context = Get-AzContext
Write-Host "Connected as: $($context.Account.Id)"
Write-Host "Subscription: $($context.Subscription.Name)"

# Check Cost Management permissions
$scope = "/subscriptions/$($context.Subscription.Id)"
$assignments = Get-AzRoleAssignment -Scope $scope -SignInName $context.Account.Id
$costRoles = $assignments | Where-Object { $_.RoleDefinitionName -like "*Cost*" }
$costRoles | Format-Table RoleDefinitionName, Scope

Step 3: Query Cost Data

# Define query parameters
$scope = "/subscriptions/$((Get-AzContext).Subscription.Id)"
$timePeriod = New-AzCostManagementQueryComparisonExpressionObject -Name 'UsageDate' -Operator 'In' -Value @('20250101', '20250131')

# Create aggregation
$aggregation = @{
    totalCost = @{
        name = 'PreTaxCost'
        function = 'Sum'
    }
}

# Create grouping
$grouping = @(
    @{
        type = 'Dimension'
        name = 'ResourceType'
    }
)

# Execute query
$result = Invoke-AzCostManagementQuery `
    -Scope $scope `
    -Timeframe 'MonthToDate' `
    -Type 'Usage' `
    -DatasetAggregation $aggregation `
    -DatasetGrouping $grouping

# Display results
$result.Row | ForEach-Object {
    [PSCustomObject]@{
        Cost = $_[0]
        ResourceType = $_[1]
        Date = $_[2]
    }
} | Format-Table

Step 4: Manage Exports via PowerShell

# List all exports
$exports = Get-AzCostManagementExport -Scope $scope
$exports | Format-Table Name, @{Label="Type";Expression={$_.DefinitionType}}, @{Label="Status";Expression={$_.ScheduleStatus}}

# Get specific export
$exportName = "daily-costs"
$export = Get-AzCostManagementExport -Scope $scope -Name $exportName
$export | Format-List

# Trigger manual export run
Invoke-AzCostManagementExecuteExport -ExportName $exportName -Scope $scope

Method 3: Enable API Access Using Service Principal

For automated systems and CI/CD pipelines, use Service Principal authentication.

Step 1: Create Service Principal

# Create service principal with Cost Management Reader role
SUBSCRIPTION_ID=$(az account show --query id -o tsv)
APP_NAME="cost-management-api-sp"

# Create the service principal and assign role
az ad sp create-for-rbac \
  --name "$APP_NAME" \
  --role "Cost Management Reader" \
  --scopes "/subscriptions/$SUBSCRIPTION_ID" \
  --query "{AppId:appId, Password:password, Tenant:tenant}" \
  -o json

# Save output (appId, password, tenant) securely
# Example output:
# {
#   "AppId": "12345678-1234-1234-1234-123456789012",
#   "Password": "abcdefgh-1234-5678-90ab-cdefghijklmn",
#   "Tenant": "87654321-4321-4321-4321-210987654321"
# }

Step 2: Grant Additional Permissions (if needed)

# Grant export management permissions
az role assignment create \
  --assignee "APP-ID-FROM-STEP-1" \
  --role "Cost Management Contributor" \
  --scope "/subscriptions/$SUBSCRIPTION_ID"

# Grant billing account access (for EA/MCA)
BILLING_ACCOUNT_ID=$(az billing account list --query "[0].id" -o tsv)
az role assignment create \
  --assignee "APP-ID-FROM-STEP-1" \
  --role "Cost Management Reader" \
  --scope "$BILLING_ACCOUNT_ID"

Step 3: Test Service Principal Authentication

# Login using service principal
az login \
  --service-principal \
  --username "APP-ID" \
  --password "PASSWORD" \
  --tenant "TENANT-ID"

# Verify access
az account show

# Test Cost Management query
az costmanagement query \
  --type "Usage" \
  --scope "/subscriptions/$SUBSCRIPTION_ID" \
  --timeframe "MonthToDate"

Step 4: Use Service Principal in Applications

Python Example:

from azure.identity import ClientSecretCredential
from azure.mgmt.costmanagement import CostManagementClient
from azure.mgmt.costmanagement.models import (
    QueryDefinition,
    QueryTimePeriod,
    QueryDataset,
    QueryAggregation,
    QueryGrouping
)
import os
from datetime import datetime, timedelta

# Service Principal credentials
tenant_id = os.getenv("AZURE_TENANT_ID")
client_id = os.getenv("AZURE_CLIENT_ID")
client_secret = os.getenv("AZURE_CLIENT_SECRET")
subscription_id = os.getenv("AZURE_SUBSCRIPTION_ID")

# Authenticate
credential = ClientSecretCredential(
    tenant_id=tenant_id,
    client_id=client_id,
    client_secret=client_secret
)

# Create Cost Management client
client = CostManagementClient(credential)

# Define query
scope = f"/subscriptions/{subscription_id}"

# Calculate date range (last 7 days)
end_date = datetime.now()
start_date = end_date - timedelta(days=7)

query = QueryDefinition(
    type="Usage",
    timeframe="Custom",
    time_period=QueryTimePeriod(
        from_property=start_date.strftime("%Y-%m-%dT00:00:00Z"),
        to=end_date.strftime("%Y-%m-%dT23:59:59Z")
    ),
    dataset=QueryDataset(
        granularity="Daily",
        aggregation={
            "totalCost": QueryAggregation(
                name="PreTaxCost",
                function="Sum"
            )
        },
        grouping=[
            QueryGrouping(
                type="Dimension",
                name="ResourceGroup"
            )
        ]
    )
)

# Execute query
result = client.query.usage(scope, query)

# Process results
for row in result.rows:
    cost = row[0]
    resource_group = row[1]
    date = row[2]
    print(f"Date: {date}, Resource Group: {resource_group}, Cost: ${cost:.2f}")

PowerShell Example:

# Service Principal credentials (from environment variables or Key Vault)
$tenantId = $env:AZURE_TENANT_ID
$clientId = $env:AZURE_CLIENT_ID
$clientSecret = $env:AZURE_CLIENT_SECRET | ConvertTo-SecureString -AsPlainText -Force

# Create credential
$credential = New-Object System.Management.Automation.PSCredential($clientId, $clientSecret)

# Connect using service principal
Connect-AzAccount -ServicePrincipal -TenantId $tenantId -Credential $credential

# Query cost data
$scope = "/subscriptions/$env:AZURE_SUBSCRIPTION_ID"
$result = Invoke-AzCostManagementQuery `
    -Scope $scope `
    -Timeframe 'MonthToDate' `
    -Type 'Usage'

# Process results
$result.Row | ForEach-Object {
    Write-Host "Cost: $($_[0]), Resource: $($_[1])"
}

Method 4: Direct REST API Access

For maximum flexibility, use the REST API directly with OAuth2 tokens.

Step 1: Obtain Access Token

# Get access token using Azure CLI
ACCESS_TOKEN=$(az account get-access-token --resource https://management.azure.com --query accessToken -o tsv)

# Verify token (should return valid JWT)
echo $ACCESS_TOKEN | cut -d '.' -f 2 | base64 -d 2>/dev/null | jq .

Step 2: Make API Request

# Define variables
SUBSCRIPTION_ID=$(az account show --query id -o tsv)
API_VERSION="2023-03-01"
SCOPE="/subscriptions/$SUBSCRIPTION_ID"

# Query usage details
curl -X POST \
  "https://management.azure.com${SCOPE}/providers/Microsoft.CostManagement/query?api-version=${API_VERSION}" \
  -H "Authorization: Bearer ${ACCESS_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "Usage",
    "timeframe": "MonthToDate",
    "dataset": {
      "granularity": "Daily",
      "aggregation": {
        "totalCost": {
          "name": "PreTaxCost",
          "function": "Sum"
        }
      },
      "grouping": [
        {
          "type": "Dimension",
          "name": "ResourceType"
        }
      ]
    }
  }' | jq .

Step 3: List Exports via REST API

# List all exports
curl -X GET \
  "https://management.azure.com${SCOPE}/providers/Microsoft.CostManagement/exports?api-version=${API_VERSION}" \
  -H "Authorization: Bearer ${ACCESS_TOKEN}" | jq .

# Get specific export
EXPORT_NAME="daily-costs"
curl -X GET \
  "https://management.azure.com${SCOPE}/providers/Microsoft.CostManagement/exports/${EXPORT_NAME}?api-version=${API_VERSION}" \
  -H "Authorization: Bearer ${ACCESS_TOKEN}" | jq .

Step 4: Create Export via REST API

# Create new export
curl -X PUT \
  "https://management.azure.com${SCOPE}/providers/Microsoft.CostManagement/exports/api-created-export?api-version=${API_VERSION}" \
  -H "Authorization: Bearer ${ACCESS_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "properties": {
      "schedule": {
        "status": "Active",
        "recurrence": "Daily",
        "recurrencePeriod": {
          "from": "2025-01-01T00:00:00Z",
          "to": "2025-12-31T23:59:59Z"
        }
      },
      "format": "Csv",
      "deliveryInfo": {
        "destination": {
          "resourceId": "/subscriptions/SUB-ID/resourceGroups/RG/providers/Microsoft.Storage/storageAccounts/STORAGE",
          "container": "cost-exports",
          "rootFolderPath": "api-exports"
        }
      },
      "definition": {
        "type": "ActualCost",
        "timeframe": "MonthToDate",
        "dataSet": {
          "granularity": "Daily"
        }
      }
    }
  }' | jq .

Common API Operations

Query Cost Data with Filters

# Query costs for specific resource group
az costmanagement query \
  --type "Usage" \
  --scope "/subscriptions/$(az account show --query id -o tsv)" \
  --timeframe "MonthToDate" \
  --dataset-filter '{
    "dimensions": {
      "name": "ResourceGroup",
      "operator": "In",
      "values": ["production-rg", "staging-rg"]
    }
  }'

# Query costs with tag filter
az costmanagement query \
  --type "Usage" \
  --scope "/subscriptions/$(az account show --query id -o tsv)" \
  --timeframe "MonthToDate" \
  --dataset-filter '{
    "tags": {
      "name": "Environment",
      "operator": "In",
      "values": ["Production"]
    }
  }'

Get Pricing Information (EA/MCA only)

# Get price sheet for EA billing account
BILLING_ACCOUNT_ID=$(az billing account list --query "[0].name" -o tsv)
az costmanagement query \
  --type "Usage" \
  --scope "/providers/Microsoft.Billing/billingAccounts/$BILLING_ACCOUNT_ID" \
  --timeframe "MonthToDate"

Trigger Export Execution

# Manually run an export
az costmanagement export execute \
  --export-name "daily-costs" \
  --scope "/subscriptions/$(az account show --query id -o tsv)"

# Check export run history
az rest --method GET \
  --uri "https://management.azure.com/subscriptions/$(az account show --query id -o tsv)/providers/Microsoft.CostManagement/exports/daily-costs/runHistory?api-version=2023-03-01"

Best Practices

1. Use Managed Identities When Possible

For Azure-hosted applications, use Managed Identity instead of Service Principals:

from azure.identity import ManagedIdentityCredential
from azure.mgmt.costmanagement import CostManagementClient

# Use Managed Identity (no credentials needed)
credential = ManagedIdentityCredential()
client = CostManagementClient(credential)

2. Implement Token Caching

Cache access tokens to reduce authentication overhead:

from azure.identity import ClientSecretCredential
from azure.core.credentials import AccessToken
from datetime import datetime, timedelta

class TokenCache:
    def __init__(self, credential):
        self._credential = credential
        self._token = None
        self._expires_on = None

    def get_token(self):
        if self._token is None or datetime.now() >= self._expires_on:
            token = self._credential.get_token("https://management.azure.com/.default")
            self._token = token.token
            self._expires_on = datetime.fromtimestamp(token.expires_on) - timedelta(minutes=5)
        return self._token

3. Handle API Rate Limits

Implement exponential backoff for API throttling:

import time
from azure.core.exceptions import HttpResponseError

def query_with_retry(client, scope, query, max_retries=3):
    for attempt in range(max_retries):
        try:
            return client.query.usage(scope, query)
        except HttpResponseError as e:
            if e.status_code == 429:  # Too Many Requests
                wait_time = (2 ** attempt) * 5  # Exponential backoff
                print(f"Rate limited. Waiting {wait_time} seconds...")
                time.sleep(wait_time)
            else:
                raise
    raise Exception("Max retries exceeded")

4. Use Appropriate Scopes

Choose the right scope for your queries:

# Subscription scope
SCOPE="/subscriptions/SUB-ID"

# Resource group scope
SCOPE="/subscriptions/SUB-ID/resourceGroups/RG-NAME"

# Management group scope
SCOPE="/providers/Microsoft.Management/managementGroups/MG-ID"

# Billing account scope (EA/MCA)
SCOPE="/providers/Microsoft.Billing/billingAccounts/BILLING-ID"

5. Optimize Query Performance

  • Use appropriate date ranges (avoid querying years of data at once)
  • Apply filters to reduce result set size
  • Use pagination for large result sets
  • Cache results when appropriate
# Query with specific date range instead of "MonthToDate"
az costmanagement query \
  --type "Usage" \
  --scope "/subscriptions/$(az account show --query id -o tsv)" \
  --timeframe "Custom" \
  --time-period '{
    "from": "2025-01-01T00:00:00Z",
    "to": "2025-01-07T23:59:59Z"
  }'

6. Secure Credentials

Never hardcode credentials:

# Use environment variables
export AZURE_TENANT_ID="your-tenant-id"
export AZURE_CLIENT_ID="your-client-id"
export AZURE_CLIENT_SECRET="your-client-secret"

# Or use Azure Key Vault
az keyvault secret set \
  --vault-name "cost-api-vault" \
  --name "service-principal-secret" \
  --value "YOUR-SECRET"

# Retrieve in application
SECRET=$(az keyvault secret show \
  --vault-name "cost-api-vault" \
  --name "service-principal-secret" \
  --query value -o tsv)

Troubleshooting

Issue: 403 Forbidden Error

Symptoms: API requests fail with "AuthorizationFailed" or "Forbidden"

Solution:

  1. Verify you have correct role assignment
  2. Check scope matches your permission level
  3. Ensure subscription type supports the API feature
# Check role assignments
az role assignment list \
  --assignee $(az account show --query user.name -o tsv) \
  --all

# Test with more permissive role
az role assignment create \
  --assignee "[email protected]" \
  --role "Cost Management Contributor" \
  --scope "/subscriptions/$(az account show --query id -o tsv)"

Issue: 404 Not Found Error

Symptoms: API returns "ResourceNotFound"

Solution:

  1. Verify scope format is correct
  2. Check subscription ID is accurate
  3. Ensure export name matches existing export
# Verify subscription ID
az account show --query id -o tsv

# List available scopes
az account list --query "[].{Name:name, Id:id}" -o table

# List existing exports
az costmanagement export list \
  --scope "/subscriptions/$(az account show --query id -o tsv)" \
  --query "[].name" -o table

Issue: Token Expiration

Symptoms: Authentication works initially but fails after some time

Solution:

  1. Implement token refresh logic
  2. Check token expiration before each request
  3. Use SDK automatic token management
from azure.identity import ClientSecretCredential
from azure.core.credentials import AccessToken

credential = ClientSecretCredential(tenant_id, client_id, client_secret)

# SDK automatically refreshes tokens
# Manual check if needed:
token = credential.get_token("https://management.azure.com/.default")
print(f"Token expires at: {token.expires_on}")

Issue: Empty or Incomplete Data

Symptoms: API returns success but with no data rows

Solution:

  1. Check date range includes billable period
  2. Verify scope contains resources with costs
  3. Review filters aren't excluding all data
  4. Wait 24-48 hours for recent cost data to finalize
# Check if subscription has any costs
az costmanagement query \
  --type "Usage" \
  --scope "/subscriptions/$(az account show --query id -o tsv)" \
  --timeframe "TheLastMonth" \
  --query "properties.rows" -o table

# If empty, try wider date range
az costmanagement query \
  --type "Usage" \
  --scope "/subscriptions/$(az account show --query id -o tsv)" \
  --timeframe "MonthToDate"

Next Steps

After enabling Cost Management API access:

  1. Build Custom Dashboards: Integrate cost data into existing reporting tools

    • Connect to Power BI, Tableau, or Grafana
    • Create real-time cost monitoring dashboards
    • Implement anomaly detection
  2. Automate Cost Governance: Use API for proactive cost management

    • Automated tagging enforcement
    • Budget threshold alerts
    • Resource cleanup automation
  3. Create Cost Analytics Pipelines: Build data pipelines for advanced analysis

    • Azure Data Factory integration
    • Azure Synapse Analytics for large-scale analysis
    • Machine learning for cost prediction
  4. Set Up Scheduled Exports: Automate cost data delivery

  5. Implement Cost Allocation: Programmatically manage cost allocation rules

Related Resources

Frequently Asked Questions

Find answers to common questions

The Cost Management Data Export API is supported on Enterprise Agreement (EA), Microsoft Customer Agreement (MCA), and Pay-As-You-Go subscription types. However, note that not all features are available for all subscription types. For example, the Cost Management Exports API has limited functionality for Cloud Solution Provider (CSP) subscriptions. Always verify the specific features you need against the Subscription Type Support Matrix provided in the documentation to ensure compatibility with your subscription.

Need Professional Help?

Our team of experts can help you implement and configure these solutions for your organization.