Common Conventions
The parameter formats and response patterns that many routes share. The reference pages link here instead of repeating them, so read this page once before writing a script.
11 Conventions
Pin one scan per script run
Start every script with getAllScans and pin the scanId you use, so every page and every call reads the same scan.
Scan IDs
Most inventory, security, policy and Advisor data is tied to a scan. Scan IDs look like scan-2026-10-14T02:00:00.000Z; URL-encode the colons (%3A) when building URLs by hand. Get them from GET /api/getAllScans, which lists only finished scans, newest first.
Many single-scan routes also accept the literal scanId=latest, meaning the most recent completed scan. Supported routes are marked accepts scanId=latest in the reference. Where it isn't supported, latest behaves like an unknown scan.
An unknown scanId usually returns 500, not 404
Most single-scan routes return 500 with a generic message for a scan that doesn't exist. Always take IDs from getAllScans rather than constructing them.
Scan Pairs (Comparisons)
Comparison routes take two required parameters: baseScanId is the older scan and compareScanId is the newer one. Change status values are added (only in the newer scan), modified (in both, different) and deleted (only in the older scan).
| Status | Message | Cause |
|---|---|---|
400 | Please provide both 'baseScanId' and 'compareScanId' query parameters. | Either parameter missing |
404 | Base scan with ID '<id>' not found. or Compare scan with ID '<id>' not found. | Unknown scan |
400 | Base scan '<base>' must be older than compare scan '<compare>'. | Pair reversed or equal |
Don't add hierarchy counts across levels
Hierarchy change summaries count each change at subscription, resource-group and resource level. Summing across levels triple-counts.
Time Windows
Change, activity log and attribution routes take a window. Both ends are required, as ISO 8601 timestamps such as 2026-10-01T00:00:00Z. Start must be before end, and a window may span at most 366 days per call.
Bounds are inclusive to the whole second: the start is truncated to its second and the whole end second is included. To walk a longer period, start each next window at previous end + 1 second.
| Status | Message |
|---|---|
400 | <name> must be a valid ISO timestamp |
400 | <name> must be before <name> |
400 | time window must not exceed 366 days |
getChangeAttribution compares raw strings
That route compares your timestamps as you sent them, with no whole-second normalisation. Send whole-second Z timestamps for predictable edges.
Cost Date Ranges
Cost explorer and spend accountability take startDate and endDate in YYYY-MM-DD, inclusive. Send both or neither; what happens when only one is sent varies per route and is noted there. The range may span at most 366 days. This cap applies to every caller, including the web UI.
| Status | Message |
|---|---|
400 | Invalid date format. Use YYYY-MM-DD format. |
400 | startDate must not be after endDate |
400 | Invalid calendar date. |
400 | Date range exceeds maximum supported span of 366 days |
List Parameters
Two list formats exist. Each route says which it uses.
Plain comma list (CSV)
a,b,c. Used for GUIDs, ARM types and enum values.
Encoded list
Used by the role-assignments-details filters and getChangeAttribution subscriptionIds. Each value is percent-encoded first, then the values are joined with commas, then the whole parameter is URL-encoded as usual. So a value is encoded twice on the wire: the role name My, Role is sent as My%252C%2520Role. GUIDs and enum values contain nothing to encode, so a plain comma list works for them.
| Status | Message | Cause |
|---|---|---|
400 | <name> contains a value that is not valid percent-encoding | Encoded list with a malformed value |
400 | <name> must list at most 200 values | Most lists cap at 200 values. Split larger lists across calls |
Entity Selectors
Detail routes for one subscription, resource group, resource or management group use entityType to pick the level, then need the identifiers for that level:
| entityType | Identifiers |
|---|---|
subscription | subscriptionId |
resourceGroup | subscriptionId + resourceGroupName |
resource | subscriptionId + resourceGroupName + resourceName |
managementGroup (only where listed) | managementGroupId |
- An
entityTypeoutside the route's list gives400entityType must be one of <values>. Exact missing-identifier messages vary per route and are listed there. subscriptionIdis usually matched exactly as stored, so copy it from a summary response. Resource group and resource names are matched case-insensitively.- Detail routes return items at exactly that level. A resource-group query does not include its resources' items unless the route says otherwise.
Paging
Most routes return everything in one response with no paging. Four paging patterns exist:
| Pattern | Routes | How it works |
|---|---|---|
pageSize + continuationToken | getScanResults | Real database paging. Follow pagination.continuationToken until hasMore is false. Send the same other parameters on every page |
pageSize + continuationToken (offset) | getScanDiff | Optional. Omit pageSize to get the whole diff at once (recommended) |
limit + offset | getResourceGraphChanges | Optional. The response has totalCount and hasMore |
page + pageSize | defender-vulnerability-findings, getDefenderAssessmentDiffDetails, getDefenderAlertDiffDetails, getAdvisorRecommendationDiffDetails | 1-based. The response has totalCount, totalPages, currentPage. A page past the end returns the last page, so stop at totalPages |
skip + take | getAuditLogs | skip up to 10000, take up to 100 |
Compact Summaries and combinedCounts
"Summary" and "hierarchy" routes return compactSummaries[]: one row per entity with small count dictionaries, for example severityCounts: {"High": 3}.
combinedCounts keys join several dimensions with |, for example "High|Unhealthy|false|SoftwareUpdate". Each item is counted in exactly one key, so to count items matching several filters, sum the matching keys. Each route documents its key order. Some routes drop empty parts from the key, giving fewer segments.
Response Fields
- Field names are camelCase. Timestamps are ISO 8601 UTC.
- Optional fields are omitted when they have no value, rather than sent as null, unless a route says otherwise.
- Many responses include internal bookkeeping fields: names starting with
_(_rid,_self,_etag,_attachments,_ts) andid,partitionKey,docType,firstScanSequence,lastScanSequence,scanIds. These are not part of the contract; ignore them. - Resource IDs come in two forms:
resourceIdis the lower-case ARM ID andoriginalResourceIdkeeps Azure's casing.
Hidden Items
Users can hide recommendations and Defender items in the UI. The API returns hidden items too. Defender and Azure Advisor items carry isHidden, and some Defender routes take a showHidden parameter. Security findings, sizing, orphaned and other recommendation routes have no hidden marker at all.
Currency
Cost amounts are in the currency of the underlying data; there is no conversion. Totals that span currencies are raw sums, so check currency and mixedCurrency before adding anything up. VM and disk sizing amounts are USD retail list prices.