Understanding Azure REST APIs and the ARM API
Every action in Azure — whether you click it in the portal, run it with the CLI, or call it from an SDK — eventually becomes an HTTPS request to the Azure Resource Manager REST API. Understanding how that API works gives you a complete picture of how Azure operates, and it opens a path for building integrations that go beyond what the CLI offers. This page explains the ARM API structure, authentication, and shows a working curl example against a real Azure endpoint.
What Azure Resource Manager Is
Azure Resource Manager (ARM) is the deployment and management layer that sits between you and every Azure service. When you create a virtual machine, ARM validates your request, checks permissions against Azure RBAC, records the operation in the Activity Log, and dispatches the creation request to the compute service. When you query an existing resource, ARM routes the request, applies access controls, and returns a consistent response format.
ARM exposes all of this through a single REST API base URL:
https://management.azure.comEvery resource in Azure has a unique URL path under this base. A resource group, for example, looks like:
https://management.azure.com/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}?api-version=2021-04-01A virtual machine in that resource group looks like:
https://management.azure.com/subscriptions/{subscriptionId}/resourceGroups/{rg}/providers/Microsoft.Compute/virtualMachines/{vmName}?api-version=2023-03-01The providers/Microsoft.Compute/virtualMachines segment is the resource provider path. Different Azure services have different provider namespaces — Microsoft.Storage, Microsoft.Network, Microsoft.Web, and so on. See Registering Resource Providers for how provider namespaces work.
The api-version Parameter
Every ARM API call requires an api-version query parameter. This is not optional. Azure uses it to determine which version of the request schema and response format to apply. Microsoft updates API versions frequently as services add features. Old versions remain available for backward compatibility, so existing code does not break.
API versions are date strings in the format YYYY-MM-DD, sometimes with a suffix like -preview for features still in preview:
?api-version=2021-04-01 # stable
?api-version=2023-03-01-preview # previewTo find the correct API version for a resource type, check the Azure REST API documentation for that resource provider, or look at what the CLI uses. Running az storage account list —debug 2>&1 | grep “api-version” shows the API version the CLI chose for that operation.
When writing code that calls the ARM API, always use the explicit, latest stable api-version string rather than relying on defaults. This makes your code predictable and easier to audit when you need to update it.
Authentication: Bearer Tokens
The ARM REST API uses OAuth 2.0 bearer tokens issued by Microsoft Entra ID. Every HTTP request to management.azure.com must include an Authorization header with a valid token:
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhb...A bearer token is a short-lived credential that proves you (or your application) have authenticated with Entra ID and have been granted specific permissions. The token encodes your identity and the scopes you are authorized for. ARM validates the token on every request.
Getting a Token with az account get-access-token
The Azure CLI can issue a token for your currently authenticated session. This is the easiest way to get a token for testing REST API calls manually:
az account get-access-token --query accessToken --output tsvThis prints the raw token string. You can assign it to a shell variable for use in subsequent curl commands:
TOKEN=$(az account get-access-token --query accessToken --output tsv)The token is valid for roughly 60-90 minutes. For production code, use a client SDK that manages token acquisition and refresh automatically rather than hard-coding tokens obtained this way.
A Real curl Example: Listing Resource Groups
Here is a complete working example that lists all resource groups in a subscription using curl. This demonstrates every element of an ARM API call — endpoint construction, the api-version parameter, and the Authorization header.
First, get your subscription ID and a token:
# Get the subscription ID of the currently active subscription
SUBSCRIPTION_ID=$(az account show --query id --output tsv)
# Get a bearer token for the current session
TOKEN=$(az account get-access-token --query accessToken --output tsv)Now make the REST API call:
curl -s \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
"https://management.azure.com/subscriptions/$SUBSCRIPTION_ID/resourceGroups?api-version=2021-04-01" \
| python3 -m json.toolThe -s flag suppresses curl’s progress output. The python3 -m json.tool at the end formats the JSON response for readability. The response looks like:
{
"value": [
{
"id": "/subscriptions/abc123.../resourceGroups/myapp-dev-rg",
"name": "myapp-dev-rg",
"location": "eastus",
"properties": {
"provisioningState": "Succeeded"
},
"tags": {
"environment": "dev",
"team": "platform"
}
}
]
}The response body is a JSON object with a value array containing one object per resource group. Each object has the resource’s full ID path, name, location, provisioning state, and tags. This is the same data the portal shows in the Resource Groups list — because the portal makes the same API call behind the scenes.
The full ARM resource ID — the long string starting with /subscriptions/ — uniquely identifies a resource globally across all of Azure. You will see this ID format throughout the Azure CLI, portal, and SDKs. It encodes the subscription, resource group, provider namespace, and resource name in one string.
HTTP Methods and What They Do
The ARM API uses standard HTTP verbs. Understanding what each one does helps you interpret API documentation and understand what operations are safe to repeat:
- GET — reads a resource or lists resources. Safe to call repeatedly with no side effects.
- PUT — creates or fully replaces a resource. The request body must include all required properties. If the resource exists, PUT replaces it entirely.
- PATCH — partially updates a resource. The request body only needs to include the fields you want to change. Most Azure resources support PATCH for updates.
- DELETE — deletes a resource. Irreversible.
- POST — triggers an action on a resource. Starting a VM, stopping a VM, and rotating a key are POST operations on existing resources.
Long-running operations (creating a VM, deploying an ARM template) return HTTP 202 Accepted immediately, with a URL in the response headers that you poll to check completion status. SDKs handle this polling automatically; raw REST calls need to implement the polling loop manually.
When to Use SDKs vs Raw REST
For most application code that interacts with Azure, use an official Azure SDK rather than calling the REST API directly. Microsoft provides SDKs for Python, JavaScript/TypeScript, .NET, Java, and Go. The SDKs offer:
- Automatic token acquisition and refresh — no need to manage token expiry.
- Automatic polling for long-running operations — you await a result, not a polling loop.
- Typed request and response objects — compile-time errors for mistakes instead of runtime HTTP 400 errors.
- Pagination handling — the SDKs automatically page through paginated results.
- Retry logic — the SDKs retry on transient errors (429 rate limit, 503 service unavailable) with appropriate backoff.
When to call raw REST instead of using a SDK:
- Your language does not have an official Azure SDK.
- A new feature is available in the API before the SDK has been updated to include it.
- You are building a minimal integration (a single HTTP call in a shell script) where importing a full SDK is not justified.
- You are debugging what the CLI or portal is actually sending, and you want to replicate the exact call for investigation.
The Azure CLI itself is built on the Azure Python SDK, so when you run az group list, it makes the same ARM REST call under the hood, with the SDK handling auth and pagination.
Pagination
API responses that return lists of resources are paginated. A response that includes more items than fit in a single page has a nextLink field in the JSON body:
{
"value": ["..."],
"nextLink": "https://management.azure.com/subscriptions/.../resourceGroups?api-version=2021-04-01&$skipToken=..."
}To get all results, you must follow the nextLink URL to retrieve the next page, and keep following until no nextLink appears. Azure SDKs handle this automatically. In a raw REST integration, build a loop that checks for nextLink and fetches subsequent pages.
When testing with curl and you only need the first page of results (for example, to check a single resource group), pagination is not a concern. It matters when you need all items and there are more than the default page size.
Common Mistakes When Using the ARM REST API
- Omitting the api-version parameter. Without api-version, the API returns an error. Every request needs it, even for seemingly simple GET requests. There is no default.
- Hard-coding bearer tokens in code. Tokens expire in 60-90 minutes. Code that stores a token obtained at deploy time will fail when the token expires. Use an SDK or a credential object that fetches fresh tokens automatically.
- Treating PUT like PATCH. PUT replaces the entire resource. If you PUT a resource with a body that is missing some optional settings, those settings are removed. Use PATCH to update specific fields without affecting others.
- Not handling 202 responses. Many create and delete operations return 202 immediately. If your code checks for 200 OK and treats 202 as a failure, it will incorrectly report errors for operations that actually succeeded.
- Ignoring the x-ms-request-id header. Every ARM response includes a unique request ID in the
x-ms-request-idheader. Log this value. When you need to file a support ticket or investigate a failed operation, this ID is what Microsoft support uses to find your specific request in their logs.
Summary
- All Azure management operations — portal, CLI, SDKs — go through the ARM REST API at management.azure.com.
- Every API URL includes the subscription ID, resource group, provider namespace, and resource name, plus a required api-version query parameter.
- Authentication uses OAuth 2.0 bearer tokens issued by Microsoft Entra ID, passed in the Authorization header.
- Use
az account get-access-token —query accessToken —output tsvto get a token for testing with curl. - For application code, use an official Azure SDK rather than raw REST — it handles token refresh, pagination, retries, and long-running operation polling automatically.
- Long-running operations return 202 Accepted; you must poll the provided URL to check completion.
Frequently asked questions
Do I need to use the REST API directly or can I just use the CLI?
For most tasks, the CLI or an SDK is the right tool. The REST API matters when you need to integrate Azure management into code that cannot use the CLI, when a feature is available in the API before it reaches the CLI, or when you are building a custom integration or tool on top of Azure.
How long do the bearer tokens from az account get-access-token last?
Tokens issued by Microsoft Entra ID typically expire after 60-90 minutes. For short scripts this is fine. For long-running processes, use an SDK that handles token refresh automatically rather than a hard-coded token from az account get-access-token.
What is the api-version parameter and is it required?
Yes, api-version is required on every Azure REST API call. It specifies which version of the resource provider API to use. Azure keeps old versions available for backward compatibility, so your calls will not break when Microsoft updates the API. Use the most recent stable version for new code.