How to Test Microsoft Graph API Requests
A practical guide to testing Microsoft Graph requests with Graph Explorer, Postman, cURL, Python and Node.js, including permissions and throttling.
Short answer: start with Graph Explorer for a quick request, then move to Postman or code when you need repeatability. Before sending anything, confirm the Graph version and endpoint, choose delegated or application authentication, and grant the permission required by that endpoint. Use a Microsoft 365 Developer sandbox for write requests. Inspect the HTTP status, JSON body and response headers; when Graph returns 429, honor Retry-After or use exponential backoff.
1. Choose a safe way to test
| Tool | Best for | What to watch |
|---|---|---|
| Graph Explorer | Learning endpoints, sample queries and signed-in tenant prototypes | Write operations can change tenant data; use a developer sandbox and consent only to permissions you need. |
| Postman | Reusable collections and explicit delegated or app-only authentication | You must configure an app and permissions. The documented collection defaults to the global cloud. |
| cURL, Python or Node.js | Reproducing a request in a script, CI job or application | You must acquire and protect an access token yourself. |
Microsoft recommends signing in to a Microsoft 365 Developer sandbox rather than production when experimenting, because operations can affect tenant data. See the Graph Explorer overview.
2. Confirm the request before you send it
- Endpoint and version: use the documented service root, normally
https://graph.microsoft.com/v1.0for generally available APIs orhttps://graph.microsoft.com/betawhen the endpoint documentation says beta is required. Beta behavior can change. - HTTP method: use
GETfor reads,POSTto create or invoke actions,PATCHto update, andDELETEto remove. Confirm the endpoint reference because some actions usePOSTwithout creating a resource. - Path and query: copy the resource path exactly. Encode query values and use documented OData parameters such as
$select,$filter,$topand$orderbyonly where supported. - Authentication model: delegated calls run on behalf of a signed-in user; application calls run without a user. The endpoint permission table tells you which scopes (delegated) or roles (application) are accepted.
- Headers and body: send
Authorization: Bearer TOKEN. For JSON requests addContent-Type: application/jsonand send the schema required by the endpoint.
3. Test a request in Graph Explorer
- Open Graph Explorer and choose a sample query, or enter your own URL.
- Select the HTTP method and API version.
- Run the request without signing in to inspect sample data where available.
- Sign in to your sandbox tenant when you need tenant data or advanced operations.
- Review the response preview, status, headers and generated code snippets. Use the permissions panel to add only the delegated permissions required by the request.
- For a write, verify the target resource and payload again before selecting Run query.
A successful response proves only that this request worked for this identity and tenant. Record the URL, method, status, response body and relevant headers so you can reproduce it outside the browser.
4. Test repeatably with Postman
- Import Microsoft’s Microsoft Graph Postman collection.
- Create or select an environment containing your tenant ID, client ID and (for app-only authentication) a client secret or certificate reference. Keep secrets in Postman’s secret or environment fields, never in a shared collection.
- Choose the documented delegated authentication flow when a user must approve or supply access. Choose app-only when a background service acts without a signed-in user.
- Grant admin consent where the permission requires it, and make sure the permission type matches the endpoint’s permission table.
- Send a small read request first. Add request headers and a JSON body only after authentication succeeds.
- Save the request and response metadata in a private collection so another developer can reproduce the test.
The Microsoft collection is configured for the global Microsoft Entra service and graph.microsoft.com. For a national cloud, update the Graph service root and the authorization and token endpoints as described in Microsoft’s Postman documentation.
5. Runnable request examples
The examples below list a user. Replace TOKEN with an access token whose permissions include the endpoint’s required permission. The same inspection pattern works for other resources.
cURL
curl --request GET \
--url 'https://graph.microsoft.com/v1.0/me?$select=id,displayName,userPrincipalName' \
--header 'Authorization: Bearer TOKEN' \
--header 'Accept: application/json' \
--include
--include prints response headers so you can inspect request-id, status and throttling metadata.
Python
import os
import requests
token = os.environ["GRAPH_TOKEN"]
url = "https://graph.microsoft.com/v1.0/me"
params = {"$select": "id,displayName,userPrincipalName"}
headers = {"Authorization": f"Bearer {token}", "Accept": "application/json"}
response = requests.get(url, params=params, headers=headers, timeout=30)
print("status:", response.status_code)
print("request-id:", response.headers.get("request-id"))
print("retry-after:", response.headers.get("Retry-After"))
response.raise_for_status()
print(response.json())
Node.js
const token = process.env.GRAPH_TOKEN;
const url = new URL('https://graph.microsoft.com/v1.0/me');
url.searchParams.set('$select', 'id,displayName,userPrincipalName');
const res = await fetch(url, {
headers: {
Authorization: `Bearer ${token}`,
Accept: 'application/json'
}
});
console.log('status:', res.status);
console.log('request-id:', res.headers.get('request-id'));
console.log('retry-after:', res.headers.get('retry-after'));
const body = await res.json();
if (!res.ok) throw new Error(JSON.stringify(body));
console.log(body);
Testing a JSON write safely
curl --request POST \
--url 'https://graph.microsoft.com/v1.0/me/sendMail' \
--header 'Authorization: Bearer TOKEN' \
--header 'Content-Type: application/json' \
--data '{"message":{"subject":"Graph test","body":{"contentType":"Text","content":"Sandbox test"},"toRecipients":[{"emailAddress":{"address":"dev@example.com"}}]},"saveToSentItems":true}' \
--include
Run write examples only in a sandbox with a test account and recipient. Check the endpoint documentation for the exact body and permission before sending.
6. Delegated versus application authentication
| Question | Delegated | Application |
|---|---|---|
| Is a user signed in? | Yes | No |
| What authorizes the call? | User consent plus delegated scopes | Application roles granted to the app, often with admin consent |
| Typical use | Interactive web or desktop action | Daemon, scheduled job or service |
| What to verify | The signed-in user can access the resource and the token contains the needed scope | The app has the endpoint’s role and tenant policy permits the operation |
A 401 usually means the token is missing, expired, malformed or issued for the wrong audience. A 403 usually means the identity is valid but lacks permission, consent or tenant access. Do not fix either error by blindly adding broad permissions; check the endpoint’s permission table and decode the token claims in a safe development environment.
7. Read every part of the response
- Status: treat any non-2xx response as a failed request until you understand the error.
- Body: Graph errors normally include an
error.code, message and additional details. Preserve the full JSON while debugging. - Headers: save
request-idfor support and correlation. Some operations returnRetry-AfterorLocation. - Pagination: when a collection includes
@odata.nextLink, request that URL until it is absent. Do not assume the first page is complete. - Empty results: an empty collection can be a valid result caused by filters, permissions or tenant data, rather than a transport failure.
8. Handle throttling and batching
Microsoft Graph signals throttling with HTTP 429 Too Many Requests. Follow the Retry-After value, then retry. If it is absent, use exponential backoff with jitter. Microsoft’s throttling guidance recommends reducing request frequency and avoiding immediate retries.
async function graphGetWithRetry(url, token, maxAttempts = 5) {
for (let attempt = 0; attempt < maxAttempts; attempt++) {
const response = await fetch(url, {
headers: { Authorization: `Bearer ${token}`, Accept: 'application/json' }
});
if (response.status !== 429) return response;
const retryAfter = Number(response.headers.get('retry-after'));
const delaySeconds = Number.isFinite(retryAfter) && retryAfter > 0
? retryAfter
: Math.min(60, 2 ** attempt);
await new Promise(resolve => setTimeout(resolve, delaySeconds * 1000));
}
throw new Error('Graph remained throttled after retries');
}
For JSON batching, the outer response can be 200 while individual operations inside the batch have status 429. Inspect every subresponse and retry only failed operations using their individual retry delays. A batch status alone does not prove that all requests succeeded.
9. Common errors and fixes
| Symptom | Likely cause | Fix |
|---|---|---|
401 Unauthorized |
Missing, expired or wrong-audience token | Acquire a new token for Microsoft Graph and send it as a Bearer token. |
403 Forbidden |
Missing scope or role, missing consent, or tenant policy | Check the endpoint permission table, grant the correct consent and retry with the intended identity. |
404 Not Found |
Wrong API version, path or resource identifier | Copy the current endpoint URL and verify that the resource exists in this tenant. |
400 Bad Request |
Invalid OData query, JSON schema or required field | Remove optional parameters, validate JSON and add fields back one at a time. |
429 Too Many Requests |
Service or resource throttling | Wait for Retry-After; otherwise use exponential backoff. Reduce concurrency and polling. |
| Works in Graph Explorer but not code | Different identity, scopes, tenant or headers | Compare the exact URL, token claims, API version and headers. Reproduce the Explorer request with its code snippet. |
| Works globally but not in a national cloud | Global service and login endpoints in the request | Use the national cloud Graph root and matching authorization and token endpoints. |
| Batch says 200 but data is missing | One or more subrequests failed | Parse each subresponse status and error; retry failed items individually or in a later batch. |
10. Performance, reliability and cost
- Request only fields you need with
$select; paginate deliberately and avoid downloading large collections unnecessarily. - Prefer change tracking or change notifications to repeatedly scanning resources when the API supports them.
- Limit concurrency, reuse connections and set explicit timeouts in scripts.
- Log method, URL, tenant, status, request ID, latency and retry count, while redacting tokens and personal data.
- Test reads first, then writes with deterministic fixtures in a sandbox. Keep a rollback or cleanup step for every write test.
- Graph service limits are endpoint-specific. Check the current API reference and service-specific limits instead of assuming one universal quota.
- Graph API calls themselves do not have a single universal per-request price in this workflow; your costs usually come from the infrastructure running your test or application. Monitor those separately.
11. Or skip the browser setup
If your goal is to capture a visual result from a page that uses Microsoft Graph, ScreenshotNeo provides a single website screenshot API request. It accepts the cookie or consent banner as a visitor and removes more than 60 known consent platforms, newsletter popups and chat widgets before capture. Bot checks, blank pages, timeouts, failed loads and cache hits are not billed, and response headers identify the page verdict and billing status. Its MCP server lets Claude, Cursor and other MCP clients call take_screenshot, get_page_info and capture_pdf.
See the ScreenshotNeo API documentation for options such as custom headers, cookies, authorization, JavaScript, waiting for selectors, blocking requests and PDF output.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
There are 1,000 screenshots per month free with no card. Paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.
12. FAQ
Can I test Graph without signing in?
Graph Explorer provides sample queries without sign-in. Sign in when you need your tenant’s data or operations that require delegated permissions.
Should I use v1.0 or beta?
Use v1.0 for stable production contracts. Use beta only when the endpoint documentation requires a preview API and you accept behavior changes.
Why did the same request return different data?
Compare the tenant, signed-in user or app identity, permissions, filters, API version and resource state. Graph responses are context-dependent.
Do SDKs remove the need to handle throttling?
Microsoft Graph SDKs include retry handlers in common scenarios, but you still need to understand Retry-After, batch subresponses and your service’s limits.
What should I save from a failed request?
Save the timestamp, method, URL without secrets, status, sanitized body, request ID, retry headers and token tenant or audience claims.


