Skip to content

Rolling windows and concurrency

By default, a minute, hour or day limit resets at the UTC boundary. A user could make 100 calls at 23:59 and 100 more at 00:01, and both bursts would be allowed. Set rolling: true and the limit counts the last 60 seconds, 60 minutes or 24 hours at the moment of each check instead.

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}'

Limitry counts use in 60 slices of the window: a second for a minute, a minute for an hour, 24 minutes for a day. The oldest slice counts in full, so a rolling limit never allows more than its amount, and may refuse up to one slice early. In a check’s answer, resetsAt (and retryAfterSeconds when refused) is when enough use has left the window.

month, total and concurrent limits can’t be rolling. In the app, choose In any 60 seconds, In any 60 minutes or In any 24 hours as the window.

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

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: the reservation holds a 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 the slot 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 taken, retryAfterSeconds is when the earliest hold expires. A release can free one sooner.
  • Settle in a finally block. A hold you never settle keeps its slot until it expires.

A hold lasts ttlSeconds: 300 by default, 3,600 at most. For work that runs longer, extend the hold 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 can’t change to or from concurrent, because one counts use and the other counts open holds. Create a new limit instead.

Rolling limits report like any other. On the Usage page, Right now shows the last window, to the hour. For a concurrent limit, the chart shows how much work the limit let start each day, and Right now shows the slots each subject holds, among subjects checked in the last hour. For both, Limitry sends limit.exceeded at most once an hour per subject.