Microsoft Azureintermediate

15 min read

title: Azure Pricing Data Export: Retrieve SKU Prices with Retail Prices API description: Export Azure pricing data for SKUs and services using the Azure Retail Prices API. Step-by-step guide to query VM prices, storage costs, and service rates with PowerShell and REST API. difficulty: intermediate estimatedReadTime: 12 lastUpdated: January 2025 featured: false faqItems:

  • question: >- How can I handle pagination when exporting large pricing datasets using the Azure Retail Prices API? answer: >- To manage pagination, continuously check for the 'NextPageLink' in the API response. Start by making your initial request, then append the 'NextPageLink' to your base URL for subsequent calls. For example, in Python, you can use a loop to keep fetching data until 'NextPageLink' is absent. This ensures that all pricing records are retrieved without missing any data. Be mindful of rate limits, and consider implementing delays between requests to avoid throttling.
  • question: >- What filters should I apply to narrow down Azure service pricing queries effectively? answer: >- To optimize pricing queries, use specific filters that target your requirements. For instance, include 'serviceName', 'armRegionName', and 'meterName' in your OData filter string. For example, use: $filter=serviceName eq 'Virtual Machines' and armRegionName eq 'eastus' and meterName eq 'D4s v3'. This precision reduces the result set, leading to faster responses and lower chances of hitting API rate limits. Always start with broader filters and refine based on results.
  • question: What should I do if the Azure Retail Prices API returns empty results? answer: >- If you receive an empty 'Items' array from the API, first verify the spelling of your 'serviceName' and ensure it's available in the specified region. Simplify your filter to broaden the results, e.g., just query by 'serviceName'. Additionally, check the API version being used; outdated versions may not reflect current services. If issues persist, validate your network connection and retry the request after a short delay to account for transient errors. heroImage: "https://images.unsplash.com/photo-1550645612-83f5d594b673?w=1200&h=630&fit=crop"

The Azure Retail Prices API provides programmatic access to official list prices for all Azure services, enabling accurate cost estimation, pricing comparisons, and rate card exports without Azure Portal access. This guide shows you how to retrieve, filter, and export pricing data for SKUs, regions, and meters using REST API calls.

Overview

Understanding Azure pricing is essential for:

  • Cost estimation before deploying resources
  • Budget planning with accurate list prices
  • Multi-region pricing comparison to optimize deployment location
  • Reserved Instance vs pay-as-you-go analysis for savings calculations
  • Vendor RFP responses requiring official pricing documentation
  • FinOps tooling that needs current rate cards for cost modeling

The Azure Retail Prices API provides:

  • Pay-as-you-go list prices for all Azure services
  • Regional pricing variations across 60+ Azure regions
  • SKU-level pricing details with unit of measure and meter information
  • Real-time updates as Microsoft adjusts pricing
  • No authentication required for public pricing data

Unlike the Azure Pricing Calculator (web-based), the API enables automated pricing lookups, bulk exports, and integration with custom cost estimation tools.

Prerequisites

Before you begin, ensure you have:

  • No Azure subscription required - The API is publicly accessible
  • A tool for making HTTP requests:
    • curl or wget (command-line)
    • Postman or Insomnia (GUI)
    • Python with requests library
    • PowerShell with Invoke-RestMethod
  • (Optional) jq for JSON parsing in command-line examples
  • (Optional) Excel or CSV processing tool for large exports
  • Basic understanding of Azure service names and SKUs

Verify Tools Installation

# Check curl installation
curl --version

# Check jq installation (for JSON parsing)
jq --version

# If jq not installed:
# macOS: brew install jq
# Ubuntu/Debian: sudo apt-get install jq
# Windows: Download from https://stedolan.github.io/jq/download/

Understanding the Retail Prices API

API Endpoint

https://prices.azure.com/api/retail/prices

Key Query Parameters

ParameterDescriptionExample
$filterOData filter expressionserviceName eq 'Virtual Machines'
$skipNumber of results to skip (pagination)$skip=100
currencyCodeCurrency for pricingcurrencyCode='USD'
api-versionAPI versionapi-version=2023-01-01-preview

Available Filters

  • armRegionName - Azure region (e.g., 'eastus', 'westeurope')
  • serviceName - Service name (e.g., 'Virtual Machines', 'Storage')
  • productName - Product/SKU name (e.g., 'Standard_D4s_v3')
  • skuName - SKU tier (e.g., 'Standard', 'Premium')
  • meterName - Billing meter (e.g., 'D4s v3', 'LRS Data Stored')
  • type - Price type ('Consumption', 'Reservation', 'DevTestConsumption')
  • isPrimaryMeterRegion - Boolean, true for primary pricing region

Method 1: Query Pricing with curl

The simplest way to retrieve pricing data is using curl for ad-hoc queries.

Example 1: Get Virtual Machine Pricing

# Get pricing for all VMs in East US
curl -G "https://prices.azure.com/api/retail/prices" \
  --data-urlencode "api-version=2023-01-01-preview" \
  --data-urlencode "\$filter=serviceName eq 'Virtual Machines' and armRegionName eq 'eastus' and priceType eq 'Consumption'" \
  | jq '.Items[] | {productName: .productName, meterName: .meterName, unitPrice: .unitPrice, currencyCode: .currencyCode}'

Example output:

{
  "productName": "Virtual Machines Dv3 Series",
  "meterName": "D2 v3",
  "unitPrice": 0.096,
  "currencyCode": "USD"
}
{
  "productName": "Virtual Machines Dv3 Series",
  "meterName": "D4 v3",
  "unitPrice": 0.192,
  "currencyCode": "USD"
}

Example 2: Get Storage Pricing

# Get Standard LRS storage pricing
curl -G "https://prices.azure.com/api/retail/prices" \
  --data-urlencode "api-version=2023-01-01-preview" \
  --data-urlencode "\$filter=serviceName eq 'Storage' and productName eq 'Standard LRS' and armRegionName eq 'eastus'" \
  | jq '.Items[] | {meterName: .meterName, unitPrice: .unitPrice, unitOfMeasure: .unitOfMeasure}'

Example 3: Get SQL Database Pricing

# Get Azure SQL Database pricing for a specific SKU
curl -G "https://prices.azure.com/api/retail/prices" \
  --data-urlencode "api-version=2023-01-01-preview" \
  --data-urlencode "\$filter=serviceName eq 'SQL Database' and armRegionName eq 'eastus' and skuName eq 'Standard' and priceType eq 'Consumption'" \
  | jq '.Items[0:10] | .[] | {meterName: .meterName, unitPrice: .unitPrice}'

Example 4: Compare Pricing Across Regions

# Compare D4s_v3 VM pricing across multiple regions
for region in eastus westus westeurope southeastasia; do
  echo "Region: $region"
  curl -s -G "https://prices.azure.com/api/retail/prices" \
    --data-urlencode "api-version=2023-01-01-preview" \
    --data-urlencode "\$filter=serviceName eq 'Virtual Machines' and armRegionName eq '$region' and meterName eq 'D4s v3'" \
    | jq -r '.Items[0] | "\(.armRegionName): \(.unitPrice) \(.currencyCode)"'
  echo ""
done

Method 2: Export Pricing Data with Python

For larger datasets and automated workflows, use Python with the requests library.

Example 1: Basic Pricing Query

import requests
import json
from urllib.parse import urlencode

def get_azure_pricing(service_name, region=None):
    """Fetch Azure pricing for a specific service"""
    base_url = "https://prices.azure.com/api/retail/prices"

    # Build filter
    filters = [f"serviceName eq '{service_name}'"]
    if region:
        filters.append(f"armRegionName eq '{region}'")
    filters.append("priceType eq 'Consumption'")

    params = {
        'api-version': '2023-01-01-preview',
        '$filter': ' and '.join(filters)
    }

    response = requests.get(base_url, params=params)
    response.raise_for_status()

    return response.json()

# Get VM pricing
pricing_data = get_azure_pricing('Virtual Machines', 'eastus')

# Display first 5 results
for item in pricing_data['Items'][:5]:
    print(f"{item['meterName']}: ${item['unitPrice']}/{item['unitOfMeasure']}")

Example 2: Export to CSV

import requests
import csv
from urllib.parse import urlencode

def export_pricing_to_csv(service_name, region, output_file):
    """Export Azure pricing to CSV file"""
    base_url = "https://prices.azure.com/api/retail/prices"

    params = {
        'api-version': '2023-01-01-preview',
        '$filter': f"serviceName eq '{service_name}' and armRegionName eq '{region}' and priceType eq 'Consumption'"
    }

    all_items = []

    # Handle pagination
    while True:
        response = requests.get(base_url, params=params)
        response.raise_for_status()
        data = response.json()

        all_items.extend(data['Items'])

        # Check for next page
        if 'NextPageLink' in data and data['NextPageLink']:
            base_url = data['NextPageLink']
            params = {}  # NextPageLink includes all params
        else:
            break

    # Write to CSV
    if all_items:
        with open(output_file, 'w', newline='', encoding='utf-8') as csvfile:
            fieldnames = [
                'productName', 'skuName', 'meterName',
                'unitPrice', 'unitOfMeasure', 'currencyCode',
                'armRegionName', 'type', 'effectiveStartDate'
            ]
            writer = csv.DictWriter(csvfile, fieldnames=fieldnames, extrasaction='ignore')

            writer.writeheader()
            for item in all_items:
                writer.writerow(item)

        print(f"Exported {len(all_items)} pricing records to {output_file}")
    else:
        print("No pricing data found")

# Export VM pricing for East US
export_pricing_to_csv('Virtual Machines', 'eastus', 'vm_pricing_eastus.csv')

Example 3: Multi-Region Comparison

import requests
import pandas as pd

def compare_pricing_across_regions(service_name, meter_name, regions):
    """Compare pricing for a specific SKU across regions"""
    base_url = "https://prices.azure.com/api/retail/prices"

    results = []

    for region in regions:
        params = {
            'api-version': '2023-01-01-preview',
            '$filter': f"serviceName eq '{service_name}' and meterName eq '{meter_name}' and armRegionName eq '{region}' and priceType eq 'Consumption'"
        }

        response = requests.get(base_url, params=params)
        data = response.json()

        if data['Items']:
            item = data['Items'][0]
            results.append({
                'Region': region,
                'Price': item['unitPrice'],
                'Currency': item['currencyCode'],
                'Unit': item['unitOfMeasure']
            })

    # Create DataFrame for easy comparison
    df = pd.DataFrame(results)
    df = df.sort_values('Price')

    return df

# Compare D4s v3 pricing across regions
regions = ['eastus', 'westus', 'westeurope', 'southeastasia', 'australiaeast']
comparison = compare_pricing_across_regions('Virtual Machines', 'D4s v3', regions)

print(comparison.to_string(index=False))

# Find cheapest region
cheapest = comparison.iloc[0]
print(f"\nCheapest region: {cheapest['Region']} at ${cheapest['Price']}/{cheapest['Unit']}")

Example 4: Reserved Instance Pricing

import requests

def get_reserved_instance_pricing(service_name, meter_name, region, term='1 Year'):
    """Get Reserved Instance pricing for comparison with pay-as-you-go"""
    base_url = "https://prices.azure.com/api/retail/prices"

    # Get consumption (PAYG) pricing
    payg_params = {
        'api-version': '2023-01-01-preview',
        '$filter': f"serviceName eq '{service_name}' and meterName eq '{meter_name}' and armRegionName eq '{region}' and priceType eq 'Consumption'"
    }
    payg_response = requests.get(base_url, params=payg_params)
    payg_data = payg_response.json()

    # Get reservation pricing
    ri_params = {
        'api-version': '2023-01-01-preview',
        '$filter': f"serviceName eq '{service_name}' and meterName eq '{meter_name}' and armRegionName eq '{region}' and priceType eq 'Reservation' and reservationTerm eq '{term}'"
    }
    ri_response = requests.get(base_url, params=ri_params)
    ri_data = ri_response.json()

    if payg_data['Items'] and ri_data['Items']:
        payg_price = payg_data['Items'][0]['unitPrice']
        ri_price = ri_data['Items'][0]['unitPrice']

        # Calculate savings
        hours_per_year = 8760 if term == '1 Year' else 8760 * 3
        payg_annual_cost = payg_price * hours_per_year
        ri_upfront_cost = ri_price  # This is often total upfront cost

        savings_percent = ((payg_annual_cost - ri_upfront_cost) / payg_annual_cost) * 100

        print(f"Service: {service_name}")
        print(f"SKU: {meter_name}")
        print(f"Region: {region}")
        print(f"Pay-as-you-go: ${payg_price}/hour (${payg_annual_cost:.2f}/year)")
        print(f"Reserved ({term}): ${ri_upfront_cost:.2f}")
        print(f"Savings: {savings_percent:.1f}%")
    else:
        print("Pricing data not available")

# Compare D4s_v3 PAYG vs 1-year RI pricing
get_reserved_instance_pricing('Virtual Machines', 'D4s v3', 'eastus', '1 Year')

Method 3: Query Pricing with PowerShell

PowerShell provides native REST API support for Windows environments.

Example 1: Basic Query

# Function to get Azure pricing
function Get-AzurePricing {
    param(
        [string]$ServiceName,
        [string]$Region,
        [string]$MeterName
    )

    $baseUrl = "https://prices.azure.com/api/retail/prices"
    $apiVersion = "2023-01-01-preview"

    # Build filter
    $filterParts = @(
        "serviceName eq '$ServiceName'",
        "priceType eq 'Consumption'"
    )

    if ($Region) {
        $filterParts += "armRegionName eq '$Region'"
    }

    if ($MeterName) {
        $filterParts += "meterName eq '$MeterName'"
    }

    $filter = $filterParts -join ' and '

    $params = @{
        Uri = "$baseUrl`?api-version=$apiVersion&`$filter=$filter"
        Method = 'GET'
        ContentType = 'application/json'
    }

    $response = Invoke-RestMethod @params
    return $response.Items
}

# Get VM pricing
$vmPricing = Get-AzurePricing -ServiceName "Virtual Machines" -Region "eastus"
$vmPricing | Select-Object meterName, unitPrice, unitOfMeasure | Format-Table

Example 2: Export to CSV

function Export-AzurePricing {
    param(
        [string]$ServiceName,
        [string]$Region,
        [string]$OutputFile
    )

    $baseUrl = "https://prices.azure.com/api/retail/prices"
    $apiVersion = "2023-01-01-preview"
    $filter = "serviceName eq '$ServiceName' and armRegionName eq '$Region' and priceType eq 'Consumption'"

    $allItems = @()
    $nextPageLink = "$baseUrl`?api-version=$apiVersion&`$filter=$filter"

    # Handle pagination
    while ($nextPageLink) {
        $response = Invoke-RestMethod -Uri $nextPageLink -Method Get
        $allItems += $response.Items

        $nextPageLink = $response.NextPageLink
    }

    # Export to CSV
    $allItems | Select-Object `
        productName, skuName, meterName, unitPrice, unitOfMeasure, `
        currencyCode, armRegionName, type, effectiveStartDate `
        | Export-Csv -Path $OutputFile -NoTypeInformation

    Write-Host "Exported $($allItems.Count) pricing records to $OutputFile"
}

# Export SQL Database pricing
Export-AzurePricing -ServiceName "SQL Database" -Region "eastus" -OutputFile "sql_pricing.csv"

Example 3: Regional Price Comparison

function Compare-RegionalPricing {
    param(
        [string]$ServiceName,
        [string]$MeterName,
        [string[]]$Regions
    )

    $baseUrl = "https://prices.azure.com/api/retail/prices"
    $apiVersion = "2023-01-01-preview"

    $results = @()

    foreach ($region in $Regions) {
        $filter = "serviceName eq '$ServiceName' and meterName eq '$MeterName' and armRegionName eq '$region' and priceType eq 'Consumption'"
        $uri = "$baseUrl`?api-version=$apiVersion&`$filter=$filter"

        $response = Invoke-RestMethod -Uri $uri -Method Get

        if ($response.Items.Count -gt 0) {
            $item = $response.Items[0]
            $results += [PSCustomObject]@{
                Region = $region
                Price = $item.unitPrice
                Currency = $item.currencyCode
                Unit = $item.unitOfMeasure
            }
        }
    }

    # Sort by price
    $results = $results | Sort-Object Price

    return $results
}

# Compare D4s v3 across regions
$regions = @('eastus', 'westus', 'westeurope', 'southeastasia')
$comparison = Compare-RegionalPricing -ServiceName "Virtual Machines" -MeterName "D4s v3" -Regions $regions

$comparison | Format-Table -AutoSize

# Find cheapest
$cheapest = $comparison | Select-Object -First 1
Write-Host "`nCheapest region: $($cheapest.Region) at $($cheapest.Price) $($cheapest.Currency)/$($cheapest.Unit)"

Method 4: Batch Export All Pricing Data

For complete price catalogs, export all pricing data for a service.

Example: Complete Service Export (Python)

import requests
import csv
from datetime import datetime
import time

def export_complete_service_pricing(service_name, output_file):
    """Export all pricing data for a service with pagination handling"""
    base_url = "https://prices.azure.com/api/retail/prices"

    params = {
        'api-version': '2023-01-01-preview',
        '$filter': f"serviceName eq '{service_name}'"
    }

    all_items = []
    page_count = 0

    print(f"Exporting pricing for {service_name}...")

    while True:
        try:
            response = requests.get(base_url, params=params, timeout=30)
            response.raise_for_status()
            data = response.json()

            items_in_page = len(data['Items'])
            all_items.extend(data['Items'])
            page_count += 1

            print(f"Page {page_count}: {items_in_page} items (total: {len(all_items)})")

            # Check for next page
            if 'NextPageLink' in data and data['NextPageLink']:
                base_url = data['NextPageLink']
                params = {}  # NextPageLink includes all params
                time.sleep(0.5)  # Rate limiting courtesy
            else:
                break

        except requests.RequestException as e:
            print(f"Error fetching data: {e}")
            break

    # Write to CSV
    if all_items:
        timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
        output_path = f"{output_file}_{timestamp}.csv"

        with open(output_path, 'w', newline='', encoding='utf-8') as csvfile:
            fieldnames = list(all_items[0].keys())
            writer = csv.DictWriter(csvfile, fieldnames=fieldnames)

            writer.writeheader()
            writer.writerows(all_items)

        print(f"\nExport complete!")
        print(f"Total items: {len(all_items)}")
        print(f"Output file: {output_path}")
        print(f"File size: {os.path.getsize(output_path) / (1024*1024):.2f} MB")
    else:
        print("No pricing data found")

# Export all VM pricing
export_complete_service_pricing('Virtual Machines', 'azure_vm_pricing')

# Export all storage pricing
export_complete_service_pricing('Storage', 'azure_storage_pricing')

Common Pricing Queries

Get All Azure Services

# List all available services
curl -s -G "https://prices.azure.com/api/retail/prices" \
  --data-urlencode "api-version=2023-01-01-preview" \
  | jq -r '.Items[].serviceName' | sort -u

Get All Regions for a Service

# List all regions where a service is available
curl -s -G "https://prices.azure.com/api/retail/prices" \
  --data-urlencode "api-version=2023-01-01-preview" \
  --data-urlencode "\$filter=serviceName eq 'Virtual Machines'" \
  | jq -r '.Items[].armRegionName' | sort -u

Get Specific SKU Pricing

# Get pricing for a specific VM SKU
curl -s -G "https://prices.azure.com/api/retail/prices" \
  --data-urlencode "api-version=2023-01-01-preview" \
  --data-urlencode "\$filter=serviceName eq 'Virtual Machines' and armSkuName eq 'Standard_D4s_v3' and armRegionName eq 'eastus'" \
  | jq '.Items[] | {skuName: .skuName, unitPrice: .unitPrice}'

Get Spot VM Pricing

# Get Spot VM pricing (usually much cheaper)
curl -s -G "https://prices.azure.com/api/retail/prices" \
  --data-urlencode "api-version=2023-01-01-preview" \
  --data-urlencode "\$filter=serviceName eq 'Virtual Machines' and armRegionName eq 'eastus' and priceType eq 'Consumption' and meterName contains 'Spot'" \
  | jq '.Items[0:5] | .[] | {meterName: .meterName, unitPrice: .unitPrice}'

Best Practices

1. Handle Pagination

The API returns results in pages (default 100 items). Always check for NextPageLink:

all_items = []
next_url = initial_url

while next_url:
    response = requests.get(next_url)
    data = response.json()
    all_items.extend(data['Items'])
    next_url = data.get('NextPageLink')

2. Use Specific Filters

Narrow results with precise filters to reduce API calls and processing:

# Too broad (thousands of results)
$filter=serviceName eq 'Virtual Machines'

# Better (specific SKU and region)
$filter=serviceName eq 'Virtual Machines' and meterName eq 'D4s v3' and armRegionName eq 'eastus'

3. Cache Pricing Data

Pricing doesn't change frequently. Cache results to minimize API calls:

import json
from datetime import datetime, timedelta
import os

def get_cached_pricing(cache_file, service_name, region, cache_hours=24):
    """Get pricing from cache or API"""
    cache_valid = False

    if os.path.exists(cache_file):
        cache_age = datetime.now() - datetime.fromtimestamp(os.path.getmtime(cache_file))
        if cache_age < timedelta(hours=cache_hours):
            cache_valid = True

    if cache_valid:
        with open(cache_file, 'r') as f:
            return json.load(f)
    else:
        # Fetch from API
        pricing_data = get_azure_pricing(service_name, region)

        # Save to cache
        with open(cache_file, 'w') as f:
            json.dump(pricing_data, f)

        return pricing_data

4. Compare Currency Codes

When comparing prices globally, ensure consistent currency:

# Specify currency code
curl -G "https://prices.azure.com/api/retail/prices" \
  --data-urlencode "currencyCode='EUR'" \
  --data-urlencode "\$filter=serviceName eq 'Virtual Machines' and armRegionName eq 'westeurope'"

5. Filter by Effective Date

Get current pricing by filtering out future-dated prices:

from datetime import datetime

current_date = datetime.now().isoformat()
filter_str = f"serviceName eq 'Virtual Machines' and effectiveStartDate le '{current_date}'"

6. Use Primary Meter Region

For consistent comparisons, filter by primary meter region:

curl -G "https://prices.azure.com/api/retail/prices" \
  --data-urlencode "\$filter=serviceName eq 'Virtual Machines' and isPrimaryMeterRegion eq true"

Troubleshooting

Issue: Empty Results

Symptoms: API returns empty Items array

Solution:

  1. Verify service name spelling (case-sensitive)
  2. Check if service is available in specified region
  3. Simplify filter to broaden results
# Test with minimal filter
curl -G "https://prices.azure.com/api/retail/prices" \
  --data-urlencode "api-version=2023-01-01-preview" \
  --data-urlencode "\$filter=serviceName eq 'Virtual Machines'" \
  | jq '.Items | length'

Issue: Timeout Errors

Symptoms: Request times out or takes too long

Solution:

  1. Add more specific filters to reduce result set
  2. Implement pagination handling
  3. Increase timeout value
response = requests.get(url, params=params, timeout=60)  # 60 second timeout

Issue: Rate Limiting

Symptoms: HTTP 429 errors or throttling

Solution:

  1. Add delays between requests
  2. Implement exponential backoff
  3. Cache results to reduce API calls
import time

for page in range(num_pages):
    response = requests.get(url)
    # Process response
    time.sleep(1)  # 1 second delay between requests

Issue: Inconsistent Pricing Data

Symptoms: Prices don't match Azure Portal or calculator

Solution:

  1. Check effectiveStartDate - may be future pricing
  2. Verify priceType (Consumption vs Reservation vs DevTest)
  3. Confirm isPrimaryMeterRegion is true
  4. Check for promotional/discounted pricing in Portal
# Get only current consumption pricing in primary region
curl -G "https://prices.azure.com/api/retail/prices" \
  --data-urlencode "\$filter=serviceName eq 'Virtual Machines' and priceType eq 'Consumption' and isPrimaryMeterRegion eq true" \
  | jq '.Items[0]'

Next Steps

After exporting Azure pricing data:

  1. Build Cost Estimation Tools: Create custom calculators

    • TCO models for migration planning
    • What-if analysis for architecture changes
    • Multi-cloud pricing comparisons
  2. Automate Budget Planning: Use pricing API in financial workflows

    • Quarterly budget updates with current rates
    • Cost projections based on planned deployments
    • Reserve instance vs PAYG ROI calculations
  3. Integrate with FinOps Platforms: Feed pricing data to cost management tools

    • Import to spreadsheet models
    • Populate cost databases
    • Update rate cards in enterprise systems
  4. Monitor Pricing Changes: Track price fluctuations over time

    • Historical pricing analysis
    • Alert on significant price changes
    • Optimize based on regional pricing differences
  5. Combine with Usage Data: Calculate actual costs

Related Resources

Need Professional Help?

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