Microsoft Teams Error Guide: Graph JSON Batch 'Number of requests exceeds maximum' — Chunk to 20
Fix Microsoft Graph JSON batch errors in Teams automation: keep batches to 20 requests, check each sub-response, order dependent calls, and retry only failures.
- #microsoft-teams
- #adaptive-cards
- #troubleshooting
- #errors
Stuck on this Microsoft Teams error? Get the free incident triage checklist
A one-page PDF — the exact steps to isolate, fix, and verify a production error like this one. No spam, unsubscribe anytime.
Overview
The Microsoft Graph $batch endpoint rejects a JSON batch that contains more than 20 individual requests. When your Teams provisioning or bulk-messaging code packs too many operations into one batch, Graph returns a 400 before running any of them.
{
"error": {
"code": "BadRequest",
"message": "Number of requests (34) exceeds the maximum allowed (20).",
"innerError": { "date": "2026-07-09T10:12:44", "request-id": "b1e2..." }
}
}
A subtler failure: the batch returns HTTP 200 overall, but individual sub-responses carry 429/403/404 that your code ignores.
Symptoms
- Bulk Teams operations (add many members, create many channels, send many chat messages) fail entirely with a 400 batch error.
- The batch call returns 200 but some operations silently didn’t happen.
- Sub-requests that depend on an earlier one (create channel, then post to it) fail with 404 because they ran out of order.
- Intermittent 429s appear inside sub-responses under load even though the batch itself “succeeded”.
- A single bad sub-request appears to poison the perceived success of the whole batch.
Common Root Causes
- Over-20 batch — more than 20 requests in one
$batchbody. - Ignoring sub-status — treating the batch’s outer 200 as success without checking each
responses[].status. - Missing dependency ordering — not using
dependsOnfor requests that must run sequentially. - No per-request throttle handling — a sub-response 429 with
Retry-Afterisn’t retried. - Non-idempotent retries — retrying an entire batch (including already-succeeded ops) on partial failure, causing duplicates.
Diagnostic Workflow
A valid batch keeps requests at 20 or fewer, with explicit ids and dependsOn where needed:
curl -X POST "https://graph.microsoft.com/v1.0/\$batch" \
-H "Authorization: Bearer ${T}" -H "Content-Type: application/json" \
-d '{
"requests": [
{ "id": "1", "method": "POST",
"url": "/teams/{team-id}/channels",
"headers": { "Content-Type": "application/json" },
"body": { "displayName": "incident-4821", "membershipType": "standard" } },
{ "id": "2", "method": "POST", "dependsOn": ["1"],
"url": "/teams/{team-id}/channels/{channel-id}/messages",
"headers": { "Content-Type": "application/json" },
"body": { "body": { "content": "War room opened" } } }
]
}'
Always iterate the sub-responses and act on each status:
{
"responses": [
{ "id": "1", "status": 201, "body": { "id": "19:abc..." } },
{ "id": "2", "status": 429, "headers": { "Retry-After": "12" },
"body": { "error": { "code": "TooManyRequests" } } }
]
}
Chunk a large workload into batches of 20 in code:
# Pseudocode: split 34 ops into 20 + 14
for chunk in $(split_into_batches ops.json 20); do
post_batch "$chunk"
handle_sub_429s_and_retry "$chunk"
done
Example Root Cause Analysis
A tenant-onboarding tool provisioned a Team per new customer: it created the team, added 25 members, and created 8 channels — packing all 34 Graph calls into one $batch. It failed with “Number of requests (34) exceeds the maximum allowed (20)” and, because the code only checked the outer HTTP status, logged a confusing 400 with no per-operation detail.
The rewrite chunked operations into batches of 20, used dependsOn so channel creation waited for team creation, and inspected every responses[].status. It retried only the sub-requests that returned 429 (respecting Retry-After) instead of resubmitting the whole batch, which had previously created duplicate channels on retry. Onboarding went from flaky to deterministic, and partial failures became individually retriable instead of an all-or-nothing 400.
Prevention Best Practices
- Cap every JSON batch at 20 requests; chunk larger workloads in code.
- Inspect each
responses[].status— never treat the outer 200 as blanket success. - Use
dependsOnfor ordered operations (create-then-use) so sequential dependencies hold. - Retry only the failed sub-requests, honoring per-response
Retry-After, and make operations idempotent. - Add jitter/backoff across chunks to avoid self-inflicted throttling on bulk runs.
- Log per-request outcomes with their
idso partial failures are diagnosable.
Quick Command Reference
# Count requests before posting a batch
jq '.requests | length' batch.json # must be <= 20
# Post the batch and pretty-print sub-responses
curl -s -X POST 'https://graph.microsoft.com/v1.0/$batch' \
-H "Authorization: Bearer $T" -H 'Content-Type: application/json' \
-d @batch.json | jq '.responses[] | {id, status}'
# List only failed sub-requests
curl -s ... | jq '.responses[] | select(.status >= 400)'
Conclusion
Graph JSON batching fails loudly at the 20-request ceiling and fails quietly when you ignore per-request statuses. For reliable Teams bulk automation, chunk to 20, order dependent calls with dependsOn, check every sub-response, and retry only what actually failed — idempotently and respecting Retry-After. That turns brittle all-or-nothing batches into predictable, individually recoverable operations.
Fixed it? Get 500 Microsoft Teams & DevOps AI prompts — free
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.
Did this fix your issue?
Get 500 Battle-Tested DevOps AI Prompts — Free
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.