Delta query for a single collection
Delta query allows an application to retrieve only objects that were created, updated, or deleted since its previous synchronization. A typical user synchronization starts with:
GET https://graph.microsoft.com/v1.0/users/delta
Microsoft Graph returns one or more pages containing the current users. The application follows every @odata.nextLink and eventually receives an @odata.deltaLink. That link represents the synchronized state of the user collection and is used to request later changes.
The same pattern exists for groups, applications, devices, and several other Microsoft Graph resources. Ordinarily, each collection has its own delta cycle and its own saved token.
The directoryObjects/delta endpoint is different because directoryObject is the base type for multiple Microsoft Entra resources. Its response can therefore contain different derived resource types in the same change feed.
Track several directory object types
The resource types are selected with the OData isof function. The following request starts one delta synchronization for users, groups, and devices:
GET https://graph.microsoft.com/v1.0/directoryObjects/delta?$filter=isof('microsoft.graph.user') or isof('microsoft.graph.group') or isof('microsoft.graph.device')
What's great about this approach is that it is not three requests combined on the client. Microsoft Graph creates one delta cycle whose state includes all three selected types.
The endpoint supports the following directory object types:
applicationadministrativeUnitappRoleAssignmentdevicedirectoryRolegrouporgContactservicePrincipaluser
It cannot combine arbitrary Microsoft Graph resources. For example, messages, calendar events, SharePoint list items, and directory users cannot share a delta token because they don't belong to the same directoryObject collection.
What can be the most useful scenario?
A unified Microsoft Entra inventory
Consider an internal inventory or security application that maintains a local catalog of identities and assets in Microsoft Entra ID. The catalog contains users, groups, and devices and supports searches such as:
- Which user owns or uses a device?
- Which groups should be reviewed when a user leaves the organization?
- Which directory objects were created, changed, or deleted since the previous synchronization?
The application treats these resources as one directory inventory and refreshes them on the same schedule. In this case, maintaining three independent delta loops adds orchestration without providing much isolation. A combined directoryObjects/delta feed gives the application one polling operation and one synchronization checkpoint per tenant.
This works best when the resource types have the same owner, destination, synchronization frequency, and recovery process.
Permissions
The application needs the least-privileged permission for every resource type included in the filter.
| Scenario | Endpoint | Permission | Notes |
|---|---|---|---|
| Track users | GET /directoryObjects/delta?$filter=isof('microsoft.graph.user') |
User.Read.All |
Supported for delegated work or school accounts and application authentication. |
| Track groups | GET /directoryObjects/delta?$filter=isof('microsoft.graph.group') |
Group.Read.All |
Required in addition to the user permission when both types are requested. |
| Track devices | GET /directoryObjects/delta?$filter=isof('microsoft.graph.device') |
Device.Read.All |
Required when devices are included in the combined feed. |
| Track users, groups, and devices | Combined isof filter |
User.Read.All, Group.Read.All, Device.Read.All |
The combined request requires the union of the least-privileged permissions. |
All three permissions require administrator consent for both delegated and application access. Delegated personal Microsoft accounts aren't supported by this endpoint.
A background inventory service will typically use application permissions because it runs without a signed-in user.
Process the initial response
A page can contain several resource types. Each object includes @odata.type, which the application should use to dispatch the object to the appropriate handler.
{
"@odata.context": "https://graph.microsoft.com/v1.0/$metadata#directoryObjects",
"@odata.nextLink": "https://graph.microsoft.com/v1.0/directoryObjects/delta?$skiptoken=...",
"value": [
{
"@odata.type": "#microsoft.graph.user",
"id": "87d349ed-44d7-43e1-9a83-5f2406dee5bd",
"displayName": "Adele Vance",
"userPrincipalName": "AdeleV@contoso.com"
},
{
"@odata.type": "#microsoft.graph.group",
"id": "72052a9a-c466-4995-8210-95a1c1221995",
"displayName": "All Company",
"groupTypes": [
"Unified"
]
},
{
"@odata.type": "#microsoft.graph.device",
"id": "4c4c4544-0038-3210-8058-b6c04f4c5032",
"displayName": "LAPTOP-042",
"operatingSystem": "Windows"
}
]
}
The application must follow the complete URL in @odata.nextLink. It shouldn't reconstruct the URL or change its query parameters. When no more initial pages remain, Microsoft Graph returns @odata.deltaLink instead:
{
"@odata.context": "https://graph.microsoft.com/v1.0/$metadata#directoryObjects",
"@odata.deltaLink": "https://graph.microsoft.com/v1.0/directoryObjects/delta?$deltatoken=...",
"value": []
}
The application stores this complete URL as the checkpoint for the tenant.
Retrieve later changes
On the next synchronization, call the saved delta link exactly as returned by Microsoft Graph:
GET {saved-delta-link}
Prefer: return=minimal
The optional Prefer: return=minimal header asks Microsoft Graph to return only properties that changed. Without it, an object contains the property set established by the initial request, including properties whose values haven't changed.
A later page can again contain any of the selected types:
{
"@odata.context": "https://graph.microsoft.com/v1.0/$metadata#directoryObjects",
"@odata.deltaLink": "https://graph.microsoft.com/v1.0/directoryObjects/delta?$deltatoken=...",
"value": [
{
"@odata.type": "#microsoft.graph.user",
"id": "87d349ed-44d7-43e1-9a83-5f2406dee5bd",
"jobTitle": "Senior Retail Manager"
},
{
"@odata.type": "#microsoft.graph.device",
"id": "4c4c4544-0038-3210-8058-b6c04f4c5032",
"@removed": {
"reason": "changed"
}
}
]
}
The first object represents an updated user. The second indicates that the device was removed from the active collection. The changed reason means that the object was soft-deleted and might still be recoverable from the deleted-items collection. A deleted reason means it can no longer be restored.
After processing all pages, replace the previously stored delta link with the new one returned by Microsoft Graph.
Design the synchronization defensively
A combined feed simplifies polling, but it doesn't remove the normal delta-query requirements.
Use id and @odata.type together
Object IDs are important for upserts and deletions, while @odata.type determines the resource model and destination handler. Don't assume that every entry has the same properties.
Make processing idempotent
The same object or change can appear more than once. Objects can also be repeated across pages, and response ordering isn't guaranteed. Upsert by object ID and make deletion processing safe to repeat.
Expect eventual consistency
A directory change might not appear immediately because Microsoft Entra ID is distributed. If a change isn't returned straight away, retry the saved delta link later rather than modifying it.
Protect the delta link
The delta link is an opaque state token. Its query parameters describe the original filter and selected properties. Don't parse it, append new filters, or attempt to add another resource type to an existing cycle. Start a new initial synchronization if the scope must change.
Handle synchronization resets
Directory-object delta tokens are valid for a limited period, documented as seven days. Microsoft Graph can also return 410 Gone when internal maintenance or migration invalidates the synchronization state. In either case, discard the old checkpoint and perform a new full synchronization.
This is an important trade-off of the combined endpoint: a reset affects the complete users-groups-devices inventory rather than only one resource type.
When separate delta queries are better
The combined endpoint isn't automatically better than /users/delta, /groups/delta, and /devices/delta.
Keep separate delta cycles when:
- different services or teams own the resource types
- each resource has a different polling frequency
- permissions must be isolated between applications
- one resource needs different selected properties or processing behavior
- failures and full resynchronizations should be isolated
- the application benefits from strongly typed, resource-specific SDK operations
The combined feed is most useful when the selected types form one logical dataset and are always synchronized together.
Conclusion
The directoryObjects/delta endpoint is an unusual but useful Microsoft Graph capability. Instead of tracking only one concrete resource collection, it can return changes for multiple Microsoft Entra directory object types through a single delta cycle.
For a unified directory inventory, combining users, groups, and devices can reduce polling and checkpoint orchestration. The application follows one sequence of next links, stores one delta link, and routes each returned object according to @odata.type.
The simplification comes with tighter coupling. The application needs permissions for every selected type, and an expired or reset token requires the entire combined scope to be synchronized again. Use the endpoint when the resources genuinely share a consumer, lifecycle, and recovery strategy; otherwise, separate resource-specific delta queries remain the safer design.
For more information, see: