Graph Batch Endpoint
Ein kurzer Überblick über den Batch-Endpunkt von Microsoft Graph, mit dem du mehrere Aufrufe in einer einzigen Anfrage bündeln kannst – inklusive…

Das ist nur ein kleiner Blogbeitrag, aber für die meisten von euch wahrscheinlich sehr hilfreich – vor allem, wenn du viel mit Microsoft Graph arbeitest. Häufig hat man das Problem, dass man mehrere Aufrufe ausführen möchte und dann entweder über die einzelnen Elemente iterieren muss oder eine lange Kette von Aufrufen hat. Genau dafür gibt es den Graph Batch Endpoint.
Beim Schreiben eines anderen Blogbeitrags habe ich herausgefunden, dass es für MS Graph einen Batch-Endpunkt gibt. In diesem Beitrag zeige ich dir, wie du ihn verwenden kannst, und gebe dir außerdem ein Beispielskript, das du für deine eigenen Automatisierungen anpassen kannst.
Der Batch-Endpunkt lohnt sich vor allem dann, wenn die einzelnen Aufrufe voneinander unabhängig sind und du die Ergebnisse ohnehin gemeinsam weiterverarbeitest. Typische Beispiele sind Übersichtsseiten, Reports oder geplante Jobs, die mehrere Datenquellen gleichzeitig abfragen. Bei stark voneinander abhängigen Aufrufen oder bei Operationen, die sehr unterschiedlich lange dauern, ist es dagegen oft übersichtlicher, einzelne Requests zu verwenden.

Warum der Graph Batch-Endpunkt wichtig ist
Wer schon einmal ein Skript geschrieben hat, das Dutzende einzelner Microsoft-Graph-Aufrufe abfeuert, kennt das Problem: Jeder Aufruf bringt Netzwerk-Latenz mit sich, und man läuft schnell in das Throttling von Graph hinein. Der Batch-Endpunkt erlaubt es dir, bis zu 20 Anfragen in einem einzigen Payload zu bündeln. Das reduziert die Anzahl der Roundtrips deutlich und macht deinen Code spürbar sauberer.
Statt viele Antworten nacheinander abzuwarten, bekommst du eine einzige konsolidierte Antwort zurück. Gerade bei Dashboards oder Reports, die mehrere Kennzahlen gleichzeitig brauchen, ist das ein echter Gewinn. Mehr Details zu den Limits und zum Verhalten findest du in der offiziellen Microsoft-Learn-Dokumentation zum JSON-Batching.
Wie kann ich diesen Endpunkt verwenden?
Die Verwendung ist sehr einfach. Du musst lediglich ein JSON aufbauen, in dem die verschiedenen Aufrufe aufgelistet sind. Ein Beispiel stammt aus dem Intune-Portal, um eine Zusammenfassung des Tenant-Status abzurufen. Dieses JSON sieht so aus:
{
"requests": [
{
"id": "getDeviceComplianceStateSummary",
"method": "GET",
"url": "/deviceManagement/deviceCompliancePolicyDeviceStateSummary",
"headers": {"x-ms-command-name": "fetchDeviceComplianceStateSummaryBatch"},
},
{
"id": "fetchSubscriptionState",
"method": "GET",
"url": "/deviceManagement/subscriptionState",
"headers": {"x-ms-command-name": "fetchSubscriptionStateBatch"},
},
{
"id": "getFailedAppCount",
"method": "POST",
"url": "/deviceManagement/reports/getFailedMobileAppsSummaryReport",
"body": {"filter": ""},
"headers": {"Content-Type": "application/json", "x-ms-command-name": "fetchFailedAppCountBatch"},
},
{
"id": "getDeviceConfigPolicySummary",
"method": "POST",
"url": "/deviceManagement/reports/getDeviceConfigurationPolicyStatusSummary",
"body": {
"filter": "(PolicyBaseTypeName eq 'DeviceManagementConfigurationPolicy') or (PolicyBaseTypeName eq 'Microsoft.Management.Services.Api.DeviceConfiguration') or (PolicyBaseTypeName eq 'Microsoft.Management.Services.Api.DeviceManagementIntent')"
},
"headers": {"Content-Type": "application/json", "x-ms-command-name": "fetchDeviceConfigPolicySummary"},
},
]
}
Wie du siehst, musst du am Ende nur die Methode, den Header, die URL und optional den Body definieren. Es ist sehr einfach. Jeder Eintrag im Array bekommt seine eigene id, und der Endpunkt nutzt genau diese id, um jedes Ergebnis wieder dem Aufruf zuzuordnen, der es erzeugt hat. So weißt du jederzeit, welche Antwort zu welchem Aufruf gehört.
Wie kann ich ihn in einem Skript verwenden?
Anbei findest du ein Beispielskript von mir, wie du ihn in Python verwenden kannst. Es authentifiziert sich interaktiv im Browser, baut den Batch-Body auf und schickt alles in einem einzigen POST an den $batch-Endpunkt. Du kannst die Liste der Requests einfach um deine eigenen Aufrufe erweitern oder bestehende Einträge anpassen:
import requests
from azure.identity import InteractiveBrowserCredential
credential = InteractiveBrowserCredential()
token = credential.get_token("https://graph.microsoft.com/.default")
def call_graph(access_token: str, url: str, body, method: str = "GET"):
headers = {
"Authorization": f"Bearer {access_token}",
"Content-Type": "application/json",
}
if method == "GET":
response = requests.get(
url,
headers=headers,
)
else:
response = requests.post(
url,
headers=headers,
json=body,
)
response.raise_for_status()
return response.json()
url = "https://graph.microsoft.com/beta/$batch"
body = {
"requests": [
{
"id": "getDeviceComplianceStateSummary",
"method": "GET",
"url": "/deviceManagement/deviceCompliancePolicyDeviceStateSummary",
"headers": {"x-ms-command-name": "fetchDeviceComplianceStateSummaryBatch"},
},
{
"id": "fetchSubscriptionState",
"method": "GET",
"url": "/deviceManagement/subscriptionState",
"headers": {"x-ms-command-name": "fetchSubscriptionStateBatch"},
},
{
"id": "getFailedAppCount",
"method": "POST",
"url": "/deviceManagement/reports/getFailedMobileAppsSummaryReport",
"body": {"filter": ""},
"headers": {"Content-Type": "application/json", "x-ms-command-name": "fetchFailedAppCountBatch"},
},
{
"id": "getDeviceConfigPolicySummary",
"method": "POST",
"url": "/deviceManagement/reports/getDeviceConfigurationPolicyStatusSummary",
"body": {
"filter": "(PolicyBaseTypeName eq 'DeviceManagementConfigurationPolicy') or (PolicyBaseTypeName eq 'Microsoft.Management.Services.Api.DeviceConfiguration') or (PolicyBaseTypeName eq 'Microsoft.Management.Services.Api.DeviceManagementIntent')"
},
"headers": {"Content-Type": "application/json", "x-ms-command-name": "fetchDeviceConfigPolicySummary"},
},
]
}
response = call_graph(token.token, url, body, "POST")
print(response)

Ein Teil des Inhalts ist außerdem base64-codiert. Diesen kannst du wie folgt decodieren:
import base64
dec = base64.b64decode(encoded_string)
Tipps und Best Practices für den Batch-Endpunkt
Im produktiven Einsatz lohnt es sich, ein paar Punkte im Hinterkopf zu behalten. Erstens gilt das Limit von 20 Anfragen pro Batch. Für größere Workloads musst du deine Anfragen also in mehrere Batches aufteilen und diese nacheinander oder kontrolliert parallel senden. Ein einfacher Helper, der eine lange Liste in 20er-Blöcke zerlegt, spart dir hier viel manuelle Arbeit und macht den Code wartbarer.
Zweitens können einzelne Anfragen innerhalb eines Batches über die Eigenschaft dependsOn Abhängigkeiten definieren. Das ist praktisch, wenn ein Aufruf erst abgeschlossen sein muss, bevor ein anderer startet. Und drittens solltest du immer den Statuscode jeder einzelnen Teil-Antwort prüfen, denn die äußere Anfrage kann ein erfolgreiches Ergebnis zurückliefern, obwohl ein einzelner innerer Aufruf fehlgeschlagen ist.
Wenn du tiefer in die Automatisierung mit Microsoft Graph einsteigen möchtest, schau dir gerne meine weiteren Beiträge auf jannikreinhard.com an, in denen ich viele Intune- und Graph-Szenarien behandle. Sobald du den Batch-Endpunkt einmal verwendest, wirst du wahrscheinlich nie wieder einzelne Aufrufe in einer Schleife abfeuern wollen.