Rate Limits

Rate limits for API requests

Concurrent Live Activities Limits#

iOS supports up to 5 concurrent Live Activities per app on a device. The exact capacity is dynamic: iOS may allow fewer depending on device conditions and other active or scheduled Live Activities.

ActivitySmith mirrors this iOS limit server-side. This gives API clients a clear, actionable response instead of allowing APNs to accept a start that iOS may not create on the device.

Paid accounts receive a 429 response when a targeted device has reached the enforced concurrency threshold. During a trial, starting a sixth Live Activity ends the oldest active Live Activity before starting the new one.

Limit responses always include the highest active count among targeted devices. When multiple targeted devices have mixed capacity, the response also includes blocked_devices and targeted_devices to explain which targets prevented the start.

End Live Activities as soon as they are no longer useful so iOS can release their capacity.

API Rate Limits#

Each feature has its own request allowance per account. Requests to one feature do not reduce the allowance for another.

FeatureRequests per minuteEndpoints
Push Notifications60POST /push-notification
App Icon Badge Count60POST /badge
Live Activities300PUT and DELETE /live-activity/stream/{stream_key}, POST /live-activity/start, POST /live-activity/update, POST /live-activity/end
Lock Screen Widget metrics300POST /metrics/{metric_key}/value

API keys belonging to the same account share each feature's allowance. For example, reaching the metric update limit does not block Push Notifications. The Live Activity endpoints share one Live Activities allowance.

IP address limits: 600 requests per minute per feature. These allowances are also independent, so metric requests do not consume the Push Notification IP allowance. Requests with missing or invalid credentials count toward the relevant IP limit. Browser preflight (OPTIONS) requests do not count.

These rate limits are enforced to ensure fair usage and availability of the API for all users. If you require higher limits, please contact us at [email protected].

Response Headers#

Use the RateLimit and RateLimit-Policy response headers to track your remaining allowance and when it resets. The headers use the IETF draft-8 format. When both IP and account limits apply, they appear as comma-separated entries:

RateLimit-Policy: "public-api-ip:notifications"; q=600; w=60, "account:notifications"; q=60; w=60
RateLimit: "public-api-ip:notifications"; r=599; t=60, "account:notifications"; r=59; t=60
  • q: request allowance for the named policy.
  • w: policy window in seconds.
  • r: requests remaining after this request, never below zero.
  • t: seconds until the policy resets, not a Unix timestamp.

Policies may also include an opaque pk partition identifier. Clients should ignore parameters they do not recognize. Browser clients can read these headers through Access-Control-Expose-Headers.

Unauthenticated requests receive the relevant feature's IP policy only. Authenticated requests also receive that feature's account policy. A request is blocked when either applicable allowance is exhausted.

Handling 429 Responses#

A request-rate 429 Too Many Requests response also includes Retry-After, expressed as seconds until the blocking policy resets:

HTTP/1.1 429 Too Many Requests
RateLimit-Policy: "account:notifications"; q=60; w=60
RateLimit: "account:notifications"; r=0; t=30
Retry-After: 30

Wait at least Retry-After seconds before retrying. If several policies show zero remaining requests, wait until all have reset. Add a small random delay to avoid sending a burst when the window opens. Other requests for the same feature may consume its allowance during the wait, so the next attempt can still be rate limited.

Live Activity capacity errors also use HTTP 429, but they are separate from request-rate limits. Their body contains limit and active; free device capacity by ending an existing Live Activity before starting another. A clock-based retry alone does not resolve a capacity error.