Home / AI & GenAI

AI & GenAICheat sheetBeginner

Azure AI Search cheat sheet: az search and REST calls for service, keys, indexes, indexers, metrics and logs

Copy-paste az search and REST commands for checking a search service, scaling it, rotating keys, inspecting indexes, running indexers and reading metrics and logs.

Deepak Bhardwaj7 min read

Applies toAzure AI Search with Azure CLI and data plane REST API 2026-04-01; check your CLI build with az search -h and the current API versions page, as options change

Use this to check a search service quickly. The commands run in Bash and use curl and jq.

Setup

RG=rg-ai
SVC=srch-prod
ENDPOINT="https://$SVC.search.windows.net"
API=2026-04-01

# Option 1: Entra ID token (needs role-based access enabled on the service)
TOKEN=$(az account get-access-token --scope https://search.azure.com/.default \
  --query accessToken -o tsv)
AUTH="Authorization: Bearer $TOKEN"

# Option 2: admin key (full data plane access, so don't paste it into tickets)
ADMIN_KEY=$(az search admin-key show -g $RG --service-name $SVC --query primaryKey -o tsv)
AUTH="api-key: $ADMIN_KEY"

Pick one option. The API version shown was the latest stable release when this was written. Features such as resync and resetdocs need a preview version.

Service

# All services in a resource group (-g is required)
az search service list -g $RG -o table

# State, capacity and access settings; property names can vary by CLI version
az search service show -g $RG -n $SVC --query "{sku:sku.name,status:status,\
state:provisioningState,replicas:replicaCount,partitions:partitionCount,\
public:publicNetworkAccess,auth:authOptions}"

# Scale query capacity
az search service update -g $RG -n $SVC --replica-count 3

# Scale storage (valid values: 1, 2, 3, 4, 6, 12)
az search service update -g $RG -n $SVC --partition-count 2

# Private endpoint connections
az search private-endpoint-connection list -g $RG --service-name $SVC -o table

Warning

Changing --public-access, --ip-rules, --bypass or the auth settings can cut off apps immediately. Check how clients connect first and keep the previous values ready to restore. Also check that tier limits allow the replica and partition count you want, and how the change affects billing.

Keys and access

# Admin keys
az search admin-key show -g $RG --service-name $SVC

# Rotate one admin key at a time. Move apps to the other key first
az search admin-key renew -g $RG --service-name $SVC --key-kind secondary

# Query keys: read-only access to documents
az search query-key list -g $RG --service-name $SVC
az search query-key create -g $RG --service-name $SVC -n chat-app

# Query keys can't be regenerated, so delete and recreate them
az search query-key delete -g $RG --service-name $SVC --key-value "<key value>"

# Allow both API keys and Entra ID
az search service update -g $RG -n $SVC --auth-options aadOrApiKey \
  --aad-auth-failure-mode http401WithBearerChallenge

# Grant a role
SVC_ID=$(az search service show -g $RG -n $SVC --query id -o tsv)
az role assignment create --assignee "<principal id>" \
  --role "Search Index Data Reader" --scope "$SVC_ID"

Caution

Regenerating an admin key breaks every client still using it. Check the indexers, ingestion jobs and apps that use the key before you renew it.

Role Allows
Search Index Data Reader Query and retrieve documents
Search Index Data Contributor Read and write documents
Search Service Contributor Manage objects such as indexes and indexers, and run and reset indexers
Owner, Contributor Control plane management and admin key retrieval. These roles don't include data plane run and reset through RBAC

A role assignment can take time to apply, so wait and retest before you change anything else.

Capacity

curl -s "$ENDPOINT/servicestats?api-version=$API" -H "$AUTH" \
  | jq '.counters | {storageSize, vectorIndexSize, documentCount, indexesCount, indexersCount}'

curl -s "$ENDPOINT/indexes/kb-v12/stats?api-version=$API" -H "$AUTH"

Each counter has a usage and may have a quota. Sizes are in bytes.

Indexes and documents

# List indexes and read a definition
curl -s "$ENDPOINT/indexes?api-version=$API&\$select=name" -H "$AUTH" | jq -r '.value[].name'
curl -s "$ENDPOINT/indexes/kb-v12?api-version=$API" -H "$AUTH" > kb-v12.json

# Count documents
curl -s "$ENDPOINT/indexes/kb-v12/docs/\$count?api-version=$API" -H "$AUTH"

# Look up one document by key
curl -s "$ENDPOINT/indexes/kb-v12/docs/doc-123?api-version=$API" -H "$AUTH"

# Keyword search
curl -s -X POST "$ENDPOINT/indexes/kb-v12/docs/search?api-version=$API" \
  -H "$AUTH" -H "Content-Type: application/json" \
  -d '{"search":"reset vpn password","select":"id,title","top":5,"count":true}'

# Delete one document
curl -s -X POST "$ENDPOINT/indexes/kb-v12/docs/index?api-version=$API" \
  -H "$AUTH" -H "Content-Type: application/json" \
  -d '{"value":[{"@search.action":"delete","id":"doc-123"}]}'

Replace id and title with your own field names. The key field in the delete body must match your index's key field.

Aliases

curl -s "$ENDPOINT/aliases?api-version=$API" -H "$AUTH" | jq

# Point the alias at a new index
curl -s -X PUT "$ENDPOINT/aliases/kb?api-version=$API" \
  -H "$AUTH" -H "Content-Type: application/json" \
  -d '{"name":"kb","indexes":["kb-v13"]}'

Query the alias from apps so you can switch indexes without a deployment.

Indexers

IDX=kb-indexer

# List indexers
curl -s "$ENDPOINT/indexers?api-version=$API&\$select=name" -H "$AUTH" | jq -r '.value[].name'

# Last run
curl -s "$ENDPOINT/indexers/$IDX/status?api-version=$API" -H "$AUTH" \
  | jq '{service: .status, last: (.lastResult | {status, startTime, endTime,
         itemsProcessed, itemsFailed, errorMessage})}'

# Per-document errors and warnings
curl -s "$ENDPOINT/indexers/$IDX/status?api-version=$API" -H "$AUTH" \
  | jq '.lastResult | {errors: [.errors[]? | {key, errorMessage, statusCode}],
         warnings: [.warnings[]? | {key, message}]}'

# Run now (202 Accepted)
curl -s -o /dev/null -w "%{http_code}\n" -X POST \
  "$ENDPOINT/indexers/$IDX/run?api-version=$API" -H "$AUTH"

# Stop future scheduled runs
curl -s "$ENDPOINT/indexers/$IDX?api-version=$API" -H "$AUTH" \
  | jq 'del(."@odata.context", ."@odata.etag") | .disabled = true' > indexer.json
curl -s -X PUT "$ENDPOINT/indexers/$IDX?api-version=$API" \
  -H "$AUTH" -H "Content-Type: application/json" -d @indexer.json
lastResult.status Meaning
inProgress Currently running
success Finished
transientFailure Failed. Read errorMessage and errors
reset Reset, waiting for a run

Each indexer runs one instance at a time. A second run fails with "Another indexer invocation is currently in progress; concurrent invocations are not allowed." A started run can't be paused or stopped, so disabled affects future runs only.

Reset (full re-index)

# 204 No Content; nothing is re-indexed until you run the indexer
curl -s -o /dev/null -w "%{http_code}\n" -X POST \
  "$ENDPOINT/indexers/$IDX/reset?api-version=$API" -H "$AUTH"
curl -s -o /dev/null -w "%{http_code}\n" -X POST \
  "$ENDPOINT/indexers/$IDX/run?api-version=$API" -H "$AUTH"

Caution

A reset clears the high-water mark, can't be undone, and causes a full re-index of every document. With a skillset, that can mean repeating paid enrichment and embedding calls. It doesn't remove orphaned documents from the index. Estimate the time and cost first.

Metrics

az monitor metrics list --resource "$SVC_ID" \
  --metric ThrottledSearchQueriesPercentage SearchLatency SearchQueriesPerSecond \
  --interval PT5M --aggregation Average -o table

Other useful metrics are DocumentsProcessedCount, IndexStorageUsage, IndexVectorUsage and SkillExecutionCount.

Logs (KQL)

Send OperationLogs to a workspace first. Otherwise there's nothing to query.

az monitor diagnostic-settings create --name search-logs --resource "$SVC_ID" \
  --workspace "<workspace resource id>" \
  --logs '[{"category":"OperationLogs","enabled":true}]'
// Query volume and latency per index
AzureDiagnostics
| where ResourceProvider == "MICROSOFT.SEARCH" and TimeGenerated > ago(1h)
| where OperationName == "Query.Search"
| summarize queries = count(), p95_ms = percentile(DurationMs, 95)
    by IndexName_s, bin(TimeGenerated, 5m)

// HTTP status by operation
AzureDiagnostics
| where ResourceProvider == "MICROSOFT.SEARCH" and TimeGenerated > ago(24h)
| where OperationName in ("Query.Search", "Indexing.Index")
| summarize count() by OperationName, resultSignature_d

If a column doesn't exist in your workspace, add | getschema to the first two lines to see the available names.

Quick error map

Symptom Likely cause Check or fix
401 or 403 with a token Role-based access isn't enabled, or the role is missing authOptions, role assignment on the service
403 with an API key Wrong key, or a query key used for an admin call Use the correct key, or check --disable-local-auth
Timeout or no connection Public access disabled, or IP rules or private DNS publicNetworkAccess, ipRules, private endpoint connection
404 on an index Wrong index name, or an old alias target GET /indexes, GET /aliases
207 on upload Some documents failed in the batch Read each result's status and errorMessage
429 or 503 on queries Query load exceeds capacity ThrottledSearchQueriesPercentage, add replicas
Documents are missing from results Indexer failures or storage full Indexer status, servicestats

Prevent it next time

  • Alert on ThrottledSearchQueriesPercentage, on storage and vector usage, and on indexer runs with failures.
  • Prefer Entra ID and managed identities. Don't give admin keys to apps that only query.
  • Save index, indexer, skillset and data source definitions in source control.