Skip to main content

Azure AI Foundry / Azure OpenAI pricing, including Data Zone deployments.

Project description

azure-genai-prices

Accurate cost calculation for Azure AI Foundry / Azure OpenAI deployments.

Every general-purpose price library ships OpenAI Global list prices. Azure meters each model separately per deployment tier, and a Data Zone deployment costs 10–20% more than Global. If you cost an Azure Data Zone deployment against OpenAI list prices, you under-report your spend — and the error is not a flat percentage you can multiply out afterwards.

This package reads Azure's own public retail price list and bills against the meter your deployment actually uses.

Install

pip install azure-genai-prices

Optional Redis-backed price sync:

pip install azure-genai-prices[redis]

The package ships a bundled price snapshot, so it works offline with no configuration.

Quick start

from azure_genai_prices import Usage, calc_price, DeploymentType, BillingMode

usage = Usage(input_tokens=100_000, output_tokens=2_000, cache_read_tokens=80_000)

price = calc_price(
    usage,
    model="gpt-5.6-luna",
    deployment=DeploymentType.DATA_ZONE,  # or .GLOBAL / .REGIONAL; default GLOBAL
)

price.input_cost  # Decimal
price.output_cost  # Decimal
price.total_cost  # Decimal
price.meters_used  # list[str] — the Azure meter names actually applied

Batch and Priority Processing deployments use different meters:

price = calc_price(usage, model="gpt-5.4", mode=BillingMode.BATCH)

BillingMode.STANDARD is the default; BATCH bills at roughly 50% and PRIORITY at roughly 2x, per Azure's published meters — the library uses the real published rate, not a multiplier.

Not every model is offered on every mode: gpt-5.6-luna has Standard and Priority meters but no Batch one. Asking for a mode Azure does not publish raises PriceNotFound rather than quietly falling back to a rate you are not billed at.

Other public names: ContextTier, Price, Meter, ModelNotFound, PriceNotFound, list_models(), get_meters(model).

Why not genai-prices?

genai-prices and similar libraries are excellent for OpenAI-direct, Anthropic and the rest. They publish OpenAI's Global list price for a model. Azure prices the same model differently depending on where it is deployed, and the Data Zone premium varies per meter:

Model Meter Global Data Zone (EU/US) Data Zone (APAC)
gpt-5.6-luna input $1.00 / M $1.10 / M not offered
gpt-5.4-mini input $0.75 / M $0.825 / M $0.90 / M
gpt-5.1 input $1.25 / M $1.375 / M $1.50 / M

Two things that table should make obvious. The premium is not a fixed percentage — it is 10% on most meters and 20% on others. And "the Data Zone price" is not one number: Azure runs several data zones and charges up to 9% more in APAC than in the EU/US for the same meter on the same date. There is no multiplier that converts an OpenAI list price into what you are billed, which is the reason this package exists.

Regions

Where a meter is priced differently between data zones, calc_price refuses to guess:

from azure_genai_prices import AmbiguousRegionPrice

calc_price(usage, model="gpt-5.4-mini", deployment=DeploymentType.DATA_ZONE)
# AmbiguousRegionPrice: '5.4 mini Inp Dz 1M Tokens' is priced differently per
# region (8.25E-7 in centralus, eastus, eastus2 +11 more; 9E-7 in australiaeast,
# …). Pass region= to pick one.

calc_price(
    usage, model="gpt-5.4-mini", deployment=DeploymentType.DATA_ZONE, region="northeurope"
)  # $0.825 / M

Pass region as the ARM region id of the deployment (northeurope, swedencentral, japaneast, …). It disambiguates rather than restricts: for the ~85% of meters priced identically everywhere, an unrecognised region still returns the price, because Azure's published region list lags real availability. list_regions(model) shows what a model is sold in, and Meter.is_region_dependent flags the ones where it matters.

Deployment tiers

DeploymentType maps to how the deployment was created in Azure AI Foundry:

Value Azure deployment
GLOBAL Global Standard / Global Provisioned — cheapest, no residency guarantee
DATA_ZONE Data Zone Standard — EU, US or APAC data zone residency, 10–20% premium over Global and not priced alike between zones (see Regions)
REGIONAL Regional (single-region) Standard

Pick the one that matches the deployment your requests actually go to. Using the wrong tier is the single most common source of cost drift.

Long-context billing

Azure bills long-context requests on separate "LongCo" meters. A request whose prompt exceeds roughly 272k tokens bills wholly on the higher long-context meters — both input and output — rather than splitting per token at the boundary. calc_price detects the tier from the usage you pass and selects the correct meters; price.meters_used shows which ones were applied, and ContextTier is exposed if you need to reason about the boundary yourself.

Redis sync

The intended production shape is one scheduled job that fetches prices and stores them, with every worker process loading the shared snapshot at startup instead of calling the Azure API itself.

from azure_genai_prices import (
    fetch_and_store_prices,
    load_prices_from_redis,
    refresh_from_azure,
)

fetch_and_store_prices(redis_url="redis://localhost:6379/2")  # scheduled job
load_prices_from_redis(redis_url="redis://localhost:6379/2")  # process startup
refresh_from_azure()  # no Redis: straight into memory

Keys written: azure_genai_prices:data and azure_genai_prices:updated_at.

Redis is an optional extra. If you call none of these, the bundled snapshot is used.

CLI

azure-genai-prices refresh [--output PATH]
azure-genai-prices price gpt-5.6-luna --input-tokens 100000 --output-tokens 2000 --deployment data-zone
azure-genai-prices models [--filter TEXT] [-v]
azure-genai-prices price gpt-5.4-mini --input-tokens 1000000 --deployment data-zone --region northeurope
azure-genai-prices coverage

coverage reports how many Azure meters parsed cleanly, how many were skipped as out of scope, and how many failed to parse — useful when Azure introduces a new meter naming pattern.

How prices are sourced

Prices come from Azure's public retail price list API, https://prices.azure.com/api/retail/prices — no authentication, no API key, no subscription required.

Azure only ever changes these prices on the first of a month: across 20,390 meter rows, zero carry a non-month-start effective date. A daily (or even six-hourly) refresh is therefore more than sufficient; there is no benefit to polling more aggressively.

Limitations

  • Prices are retail. Enterprise agreements, committed-use discounts, reservations and private pricing are not visible in the public API and will differ from what this library reports.
  • Azure sets its own rates and lags OpenAI. When OpenAI changes a list price, Azure may follow later, or not at all, or at a different number. Do not treat this library's output as a proxy for OpenAI-direct pricing.
  • Meter coverage is limited to generative AI meters that the parser recognises. Run azure-genai-prices coverage to see what was skipped. Fine-tuning, Sora video, realtime audio and the retired flat-rate completion models are out of scope by design — none of them is a per-token inference rate.
  • Currency is USD as published by the retail API.

Releasing

Releases publish to PyPI through GitHub Actions using Trusted Publishing, so no API token is stored in the repo or in CI. .github/workflows/release.yml runs the test suite, builds, and uploads — triggered by publishing a GitHub Release (or manually via workflow_dispatch). Bump version in pyproject.toml and add a CHANGELOG.md entry first.

Contributing

Issues and pull requests are welcome. To work on the package:

uv sync --all-extras
uv run ruff check .
uv run ruff format --check .
uv run pytest -q

CI runs the same commands on Python 3.11, 3.12 and 3.13. New pricing behaviour should come with a test, and meter-parsing changes should keep azure-genai-prices coverage at or above its current numbers.

License

MIT — see LICENSE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

azure_genai_prices-0.1.0.tar.gz (99.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

azure_genai_prices-0.1.0-py3-none-any.whl (98.0 kB view details)

Uploaded Python 3

File details

Details for the file azure_genai_prices-0.1.0.tar.gz.

File metadata

  • Download URL: azure_genai_prices-0.1.0.tar.gz
  • Upload date:
  • Size: 99.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for azure_genai_prices-0.1.0.tar.gz
Algorithm Hash digest
SHA256 814528a8c1dc726ea7a9d75a2d487ff1ca9bc1e96d9f98d94708e1d1914be2b7
MD5 9a01ad724a6e0450a3fb0e22db2b1d43
BLAKE2b-256 7cc8f21ca0d5c9b65ef5750e724b0ad3a67f54659c9e79a88a28de50db8f5e3a

See more details on using hashes here.

Provenance

The following attestation bundles were made for azure_genai_prices-0.1.0.tar.gz:

Publisher: release.yml on nikklavzar/azure-genai-prices

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file azure_genai_prices-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for azure_genai_prices-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 6c28fb480962fa29522072224036d1305137765089b083c3b68d3c8b9d3ab6c4
MD5 e479e30db43c75a8b0818d3459a54431
BLAKE2b-256 72c114a959e9fcfe2044c5b03dc2c5448ac4ff91b8e567ba722aa52a3d4b08fc

See more details on using hashes here.

Provenance

The following attestation bundles were made for azure_genai_prices-0.1.0-py3-none-any.whl:

Publisher: release.yml on nikklavzar/azure-genai-prices

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page