The goal
You have an Azure OpenAI resource, and you want to call a model from Python. Most first-time failures come from two things: confusing the model name with the deployment name, and assuming that being an Owner lets you call the API.
Three names to get straight
| Name | Example | Used for |
|---|---|---|
| Resource name | aoai-ops-demo | Part of the endpoint URL |
| Model name | The model you chose, such as gpt-4o-mini | Only tells you what's deployed |
| Deployment name | chat-prod | This is what you pass as model in code |
You choose the deployment name when you deploy a model. It doesn't have to match the model's name.
Step 1: find the endpoint and deployment
az login
az cognitiveservices account show \
--name aoai-ops-demo --resource-group rg-ai \
--query "{endpoint:properties.endpoint, disableLocalAuth:properties.disableLocalAuth}"
az cognitiveservices account deployment list \
--name aoai-ops-demo --resource-group rg-ai \
--query "[].{deployment:name, model:properties.model.name, version:properties.model.version, capacity:sku.capacity}" \
-o table
No deployments listed? You can't call a model until you create one. Do that in the Foundry portal, or read the CLI cheat sheet.
Step 2: give yourself the right role
Calling the model is a data-plane action, so it needs its own role. Owner and Contributor alone don't include it.
RESOURCE_ID=$(az cognitiveservices account show \
--name aoai-ops-demo --resource-group rg-ai --query id -o tsv)
az role assignment create \
--role "Cognitive Services OpenAI User" \
--assignee "you@contoso.com" \
--scope "$RESOURCE_ID"
Allow a few minutes for this to apply. For an app in Azure, assign the same role to its managed identity.
Step 3: test with curl
This proves that your access works, separately from any Python problems:
TOKEN=$(az account get-access-token --resource https://cognitiveservices.azure.com --query accessToken -o tsv)
curl -s "https://aoai-ops-demo.openai.azure.com/openai/deployments/chat-prod/chat/completions?api-version=2024-10-21" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"messages":[{"role":"user","content":"Say hello in five words."}],"max_tokens":30}'
Step 4: call it from Python
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install openai azure-identity
import os
from azure.identity import DefaultAzureCredential, get_bearer_token_provider
from openai import AzureOpenAI
token_provider = get_bearer_token_provider(
DefaultAzureCredential(),
"https://cognitiveservices.azure.com/.default",
)
client = AzureOpenAI(
azure_endpoint=os.environ["AZURE_OPENAI_ENDPOINT"],
azure_ad_token_provider=token_provider,
api_version="2024-10-21",
)
response = client.chat.completions.create(
model=os.environ["AZURE_OPENAI_DEPLOYMENT"], # deployment name, not model name
messages=[
{"role": "system", "content": "You answer IT questions in one short sentence."},
{"role": "user", "content": "What does DNS do?"},
],
max_tokens=100,
)
print(response.choices[0].message.content)
print(response.usage)
Run it:
export AZURE_OPENAI_ENDPOINT="https://aoai-ops-demo.openai.azure.com/"
export AZURE_OPENAI_DEPLOYMENT="chat-prod"
python first_call.py
DefaultAzureCredential tries several sign-in methods in turn. On a laptop it finds your az login session. In Azure it finds the managed identity. So your code doesn't need to change, and there's no key to leak.
API versions change over time, and some newer models and features need a newer one. Check Microsoft's API version list, and consider the newer v1 API path if your setup supports it.
Some newer models reject max_tokens and require max_completion_tokens. If you see that error, change the parameter name.
Step 5: fix errors
| Status | Message | Cause | Fix |
|---|---|---|---|
| 404 | DeploymentNotFound | The name's wrong, it's in another resource, or it was just created | Use the deployment name from Step 1; wait a few minutes |
| 401 | Access token is missing or invalid / wrong audience | Bad token scope or tenant | Use cognitiveservices.azure.com, and check az account show |
| 403 | PermissionDenied / principal lacks the action | The data-plane role is missing | Add Cognitive Services OpenAI User |
| 403 | Traffic is not from an approved network | The resource firewall or a private endpoint is in the way | Connect from an allowed network; see private endpoints |
| 400 | Unsupported parameter or api-version errors | The model or API version doesn't support it | Check the version and the parameter names |
| 400 | Content filter | The prompt or answer was blocked | See content filter errors |
| 429 | Too Many Requests | Deployment quota reached | Retry with backoff; see throttling |
When DefaultAzureCredential itself fails, it prints a list of each method it tried. Read through it. On a laptop, the fix is usually az login, perhaps with --tenant, because you're signed in to the wrong tenant.
Tip
Copy the endpoint exactly as the portal shows it. Foundry may also show a different URL style. Use the endpoint of the resource holding your deployment, because deployments aren't shared between resources.
Why not use an API key?
A key is quicker to try, but anyone who has it can use your quota, and it doesn't show who made a call.
Warning
If a team must use a key for now, keep it in Key Vault, never in code or Git. Once everyone uses Entra ID, turn off keys with
disableLocalAuth. Check first that nothing still depends on them, because they stop working immediately. See key rotation and moving to Entra ID.
Add a simple retry
Quota errors are normal on shared deployments, so don't fail on the first one. The SDK retries some errors on its own, and you can adjust it:
client = AzureOpenAI(
azure_endpoint=os.environ["AZURE_OPENAI_ENDPOINT"],
azure_ad_token_provider=token_provider,
api_version="2024-10-21",
max_retries=4,
timeout=60,
)
Always set a timeout, so that a stuck call can't hang your app.
Prevent it next time
- Use the same deployment name in each environment, and keep it in configuration rather than code.
- Plan for model retirements, because models and versions do get retired.
- Log
response.usageto track spending. See estimating token costs. - Learn more about prompts and parameters in LLM API basics.