An API that connects multiple Microsoft services, enabling data access and automation across platforms
Use POST /teamwork/sendActivityNotificationToRecipients in batches of up to 100 recipients, but pace those batches below the 2 requests/second per-app-per-tenant limit rather than launching all 40 simultaneously.
1. Throughput and throttling
The documented Teams limits are ^1^:
| Endpoint | Per app | Per app per tenant | Other endpoint-specific limit |
|---|---|---|---|
/users/{user-id}/teamwork/sendActivityNotification |
50 rps | 5 rps | No dedicated per-user limit listed |
/teamwork/sendActivityNotificationToRecipients |
20 rps | 2 rps | Up to 100 recipients/request |
Graph evaluates all applicable limits; whichever is reached first causes throttling. For one app sending into one tenant, the per-app-per-tenant value is therefore the practical governing limit.
For 4,000 users:
- Individual endpoint: 4,000 requests at no more than 5 rps, giving a theoretical request-submission floor of about 800 seconds.
- Bulk endpoint: 40 requests of 100 recipients at no more than 2 rps, giving a theoretical request-submission floor of about 20 seconds.
- Actual processing and client presentation can take longer.
The bulk endpoint is purpose-built for multiple recipients, permits at most 100 recipients in each request, and returns 202 Accepted. Its token must also have at least 45 minutes remaining; otherwise, the call returns 412 Precondition Failed ^2^.
Do not dispatch all 40 bulk requests simultaneously. Concurrency is not itself the published control; request rate is. Use:
- Batches of 100 users.
- A rate limiter below 2 rps for that tenant—starting around 1 request/second provides headroom.
- A small bounded concurrency, such as 1–2 in-flight calls, only to absorb network latency.
- Per-batch durable state and an idempotency strategy. If updating the same logical notification,
chainIdcan override an earlier notification; do not assume it is a general request-deduplication receipt. - Retry
429only after theRetry-Afterinterval. If that header is absent, use exponential backoff with jitter ^3^.
Conceptually:
foreach (var batch in recipients.Chunk(100))
{
await rateLimiter.WaitAsync(); // Configure below 2 requests/second/tenant
var response = await SendBulkNotificationAsync(batch);
if (response.StatusCode == HttpStatusCode.TooManyRequests)
{
await DelayFromRetryAfterOrExponentialBackoff(response);
// Retry this batch; don't advance its durable status yet.
}
}
If the same user can receive multiple alerts in a short period, Teams automatically throttles an app above 20 notifications per minute per user ^4^.
2. Meaning of 202 and 204
- Bulk endpoint —
202 Accepted: the request was accepted for asynchronous processing; it does not mean processing is complete ^2^. - Individual endpoint —
204 No Content: the endpoint successfully fulfilled the request and has no response body ^5^.
Neither endpoint’s documentation defines these responses as proof that:
- the notification was rendered in a particular Teams client;
- a toast was shown;
- the user saw or opened it; or
- the user responded.
Treat both as API-operation success, not end-user delivery/read confirmation.
3. Delivery limitations
Successful submission can still differ from visible presentation because:
- The target user must have the sending Teams app installed ^6^.
- Users control notification settings; the app cannot control how notifications are shown.
- Priority notifications are not supported.
- Toast notifications lead to the activity feed, while activity-feed entries can deep-link into the app.
- Activity-feed notifications are stored for 30 days ^4^.
The supplied API contracts do not document a delivery-status endpoint, callback, recipient-level receipt, read receipt, or guarantee that a successful request was displayed. In particular, the bulk 202 response does not provide a documented polling location for later recipient-level status.
For operational tracking, record:
- alert/event ID;
- batch and recipient IDs;
- submission time;
- HTTP status;
- Graph request/correlation headers;
- retry count and final submission outcome;
- the user’s acknowledgement in your application.
That acknowledgement is the reliable business-level signal available to your crisis workflow—not the Graph submission response.
4. Crisis-alert architecture implication
Activity-feed notifications support alerts and user actions, but priority notifications are not supported ^6^. Therefore, do not treat this API alone as a guaranteed emergency-broadcast channel.
Use the bulk API for fast Teams distribution, then implement application-level escalation:
- Submit 100-recipient batches through a durable queue.
- Retry only throttled/transient failures.
- Track acknowledgements through the Teams app.
- After a defined timeout, escalate unacknowledged alerts through an independently approved emergency channel.
- Load-test in a nonproduction tenant, including simulated
429and partial-batch operational failures.
References
- Microsoft Graph service-specific throttling limits
- teamwork: sendActivityNotificationToRecipients
- Microsoft Graph throttling guidance
- Best practices for using Microsoft Teams activity feed notifications
- userTeamwork: sendActivityNotification
- Send activity feed notifications to users in Microsoft Teams