The Python SDK does two things. guard and reserve put a limit around
your code in a with block. And every API operation is a typed method,
so your editor knows each keyword and answer. It needs Python 3.10 or
later.
guard checks the limit, then runs the block only if it’s allowed.
Here it counts one AI call against a limit such as 100 a day:
from limitry import LimitReached, Limitry, Unavailable
limitry =Limitry() # reads LIMITRY_API_KEY
try:
with limitry.guard(subject={"kind": "user", "id": "usr_123"},action="ai-call"):
reply =call_model(prompt)
except LimitReached as error:
print(f"Try again in {error.retry_after_seconds} seconds")
except Unavailable:
print("Limitry is unreachable and this limit fails closed")
A refusal raises LimitReached, with reason (limit_reached or
too_many_reservations), limit_id and retry_after_seconds.
When you only know the cost afterwards, such as an AI reply your product
pays for by the token, reserve reserves what it will probably cost
(5 cents here), runs the block and charges what it actually used. If
the block raises, the reservation is released:
reservation.cost(price_cents(response.usage)) # your prices, in cents
If Limitry can’t be reached, each limit’s fail mode decides: open
lets the work run, closed raises Unavailable, never LimitReached.
“Can’t be reached” means no
decision within 1.5 seconds (timeout), retries included: a network
error, a 408, a 429 or a 5xx is retried at most twice with the same
key inside that time. A 402 (the workspace is past its plan) counts the
same way. A 503 with code moving (a busy workspace’s counts moving
between Limitry’s servers, a few seconds) or busy (too many checks at
once; nothing was charged) is not “can’t be reached”: the check, commit
or release is sent again with the same key for up to 30 seconds, each
try with its own timeout, before the fail mode decides. Each retry
waits the answer’s Retry-After plus a random, growing backoff, so
clients spread out instead of retrying together. Both take timeout and fail_mode to force one.
reservation.cost() takes a whole number from 0 to 1,000,000,000 and raises
ValueError for anything else, before the work’s usage could be lost.
reservation.extend() raises the API’s error, such as for an expired
reservation.
Each API operation is a method, named like its command in the
CLI: limitry limits create is
limitry.limits.create().
from limitry import Limitry
limitry =Limitry()
limitry.limits.create(
name="AI replies per day",
subject={"kind": "user"},
action="ai-call",
amount=100,
window="day",
)
limits = limitry.limits.list()
Ids in the path are arguments, and everything else is a keyword, in
Python’s style: failMode is fail_mode=. Answers keep the
API’s own names.
A method returns the response’s data. A failure raises an ApiError
with the error’s code, message, status and
request_id.
Every write sends an idempotency key, and a
failed attempt (a network error, a timeout, a 5xx, a 429) is
retried with the same key, so it happens once. To make your own retry
safe too, pass your key: request_options={"idempotency_key": key}.
A request waits up to 60 seconds for an answer (timeout=).
Every method, linked to its fields and a full example under
Endpoints:
Limitry() reads your key from the LIMITRY_API_KEY environment
variable. Without it, it raises an error naming the variable. To pass
the key yourself: Limitry(token=key).
A test key (lim_test_…) acts in test, and a live key (lim_live_…)
in live. See Test and live.
A personal access token names the workspace, and acts in test
unless you name live:
Limitry(token=token, workspace="acme", environment="live").