Skip to content

Rolling windows and concurrency

A minute, hour or day limit resets at the UTC boundary by default — so 100 calls at 23:59 and 100 more at 00:01 are both allowed. Set rolling: true and the limit counts the last 60 seconds, 60 minutes or 24 hours at the moment of each check instead: no burst at the boundary.

Terminal window
curl -X POST https://api.limitry.com/v1/limits \
-H "Authorization: Bearer $LIMITRY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name":"Calls in any 24 hours","subjectKind":"user","action":"call",
"amount":100,"window":"day","rolling":true}'

Use is counted in 60 slices of the window (a second for a minute, a minute for an hour, 24 minutes for a day), and the oldest slice counts whole — so a rolling limit never allows more than its amount, and may refuse at most one slice early. In a check’s answer, resetsAt (and retryAfterSeconds on a denial) is when enough use leaves the window.

month, total and concurrent limits cannot be rolling. In the app: Window → In any 60 seconds / 60 minutes / 24 hours.

A limit with window concurrent caps work in flight: amount is how many at once. Each reservation takes one slot — whatever its cost, which still counts against the subject’s other limits — and gives it back when it is committed, released or expires.

Terminal window
# At most 3 exports at once per user
curl -X POST https://api.limitry.com/v1/limits ... \
-d '{"name":"Exports at once","subjectKind":"user","action":"export",
"amount":3,"window":"concurrent"}'
# Start one: a reservation holds the slot
curl -X POST https://api.limitry.com/v1/checks ... \
-d '{"subject":{"kind":"user","id":"u_42"},"action":"export","mode":"reserve"}'
# Finished (or failed): give it back
curl -X POST https://api.limitry.com/v1/reservations/$ID/release ...
  • A consume or preview check against a concurrent limit is allowed while a slot is free but takes none — instant work holds nothing.
  • When every slot is held, retryAfterSeconds is when the earliest hold expires (a release can free one sooner).
  • Always settle in a finally: an unsettled hold keeps its slot until it expires.

A hold lasts ttlSeconds (default 300, at most 3,600). For work that runs longer, extend it as a heartbeat:

Terminal window
curl -X POST https://api.limitry.com/v1/reservations/$ID/extend ... \
-d '{"ttlSeconds":600}'

If your worker dies, it stops extending and the slot frees itself.

A limit cannot change to or from concurrent (one counts use, the other open holds) — create a new limit instead.

Rolling limits report like others; the Usage page’s “Right now” shows the last window, to the hour. For a concurrent limit the chart shows the work it let start each day, and “Right now” the slots each subject holds — among subjects checked in the last hour. limit.exceeded fires at most once an hour per subject for both.