Xulo Docs

REST API

Rate limits

Each REST key has its own request buckets. The headers on each response show the limit, what is left and when it refills.

Buckets

Requests are counted in buckets. A bucket holds a number of requests and refills at a steady rate, so a short burst is fine as long as the average stays under the rate. Each REST key has its own buckets. Two keys on one server do not share them.

Bucket Counts Refills Holds
send Each POST that does not name a segment or a tag, such as a list of ids 50 a second 100
broadcast Each POST to included_segments or tag 30 a minute 10
read Each GET /api/v1/notifications/{id} 20 a second 40
audience Sends to the same segment or tag on one app, from any of its keys 10 a minute 10
ip Every call with a valid key from one IP address, across keys 6,000 a minute 6,000
anonymous Calls from one IP address that return 401 or 403 120 a minute 120

A call draws from ip first, then from its key's send, broadcast or read bucket, then, for a segment or tag send, from audience. A call that one bucket refuses does not draw from the buckets after it. A call with a missing, wrong or IP-locked key draws only from anonymous, never from a key's buckets.

Headers

Each response carries the bucket closest to running out, and the list of buckets the call drew from:

HTTP
RateLimit-Limit: 100
RateLimit-Remaining: 99
RateLimit-Reset: 1
RateLimit-Policy: 6000;w=60;burst=6000;name="ip", 50;w=1;burst=100;name="send"
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 99
X-RateLimit-Reset: 1792310401
Header Meaning
RateLimit-Limit How many requests that bucket holds.
RateLimit-Remaining Requests left in that bucket.
RateLimit-Reset Seconds until that bucket is full again.
RateLimit-Policy Each bucket the call drew from: requests per window, w the window in seconds, burst how many it holds, and name.
X-RateLimit-Limit The same as RateLimit-Limit.
X-RateLimit-Remaining The same as RateLimit-Remaining.
X-RateLimit-Reset A Unix timestamp in seconds: when that bucket is full again. On a 429 it is the moment Retry-After runs out, that is now plus Retry-After.

Over the limit

Over the limit, the call returns 429 with a Retry-After header, in seconds, and the headers above for the bucket that refused it:

JSON
{ "message": "Too many requests. Retry after 1 second." }

Wait for Retry-After, then retry. Send an Idempotency-Key so that a retried send cannot go out twice. A replayed call counts against the buckets like any other call.

Size limits

Limit Value Over the limit
Ids per list 500 A list of more than 500 Subscription IDs or External IDs returns 422.
Devices per send 1,000,000 A segment or tag send reaches every matching subscribed install, up to 1,000,000. A larger audience returns 422 with "This send reaches 1,200,000 devices. One send reaches at most 1,000,000."
Throttle 1 to 100,000 a minute 422 with "throttle_rate_per_minute is a whole number from 1 to 100000."
Message 2,000 characters 422
Title 120 characters 422
Apple payload 4KB, checked with the longest language 422 with "This notification is larger than 4KB." It is not cut short.
Link 500 characters 422
Buttons 1 422 with "Send one button."
Data Keys up to 40 characters, values up to 2,000 Longer keys and values are cut to that length.