Skip to content

Python SDK

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.

Terminal window
pip install limitry

Set LIMITRY_API_KEY to a key from API keys in the app. Use a test key (lim_test_…) while you build.

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:

from limitry import Limitry
limitry = Limitry()
with limitry.reserve(
subject={"kind": "user", "id": "usr_123"}, action="ai-call", cost=5
) as reservation:
response = call_model(prompt)
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:

Checks

MethodWhat it does
limitry.checks.create()Check whether a subject may act

Reservations

MethodWhat it does
limitry.reservations.commit()Commit a reservation
limitry.reservations.extend()Extend a reservation
limitry.reservations.release()Release a reservation

Limits

MethodWhat it does
limitry.limits.archive()Archive a limit
limitry.limits.create()Create a limit
limitry.limits.credits.create()Add credits to a balance
limitry.limits.credits.list()List a balance's credits
limitry.limits.get()Get a limit
limitry.limits.list()List limits
limitry.limits.restore()Restore an archived limit
limitry.limits.sync()Sync limits from a list
limitry.limits.update()Change a limit

Subjects

MethodWhat it does
limitry.subjects.get()Get a subject
limitry.subjects.reset()Reset a subject's limits
limitry.subjects.update()Set a subject's billing date

Usage

MethodWhat it does
limitry.usage.query()Get a limit's usage over time
limitry.usage.standing()Get where a subject stands
limitry.usage.subjects()List who is using a limit now

Workspace

MethodWhat it does
limitry.workspace.get()Get the current workspace
limitry.workspace.invitations.list()List pending invitations
limitry.workspace.members.list()List workspace members
limitry.workspace.teams.list()List teams
limitry.workspace.usage()Get your plan's usage this period

Webhooks

MethodWhat it does
limitry.workspace.event_types.list()List event types
limitry.workspace.webhook_deliveries.list()List an endpoint's deliveries
limitry.workspace.webhook_deliveries.redeliver()Redeliver an event
limitry.workspace.webhook_endpoints.create()Add a webhook endpoint
limitry.workspace.webhook_endpoints.delete()Remove a webhook endpoint
limitry.workspace.webhook_endpoints.enable()Re-enable a webhook endpoint
limitry.workspace.webhook_endpoints.get()Get a webhook endpoint
limitry.workspace.webhook_endpoints.list()List webhook endpoints
limitry.workspace.webhook_endpoints.test()Send a test event

Approvals

MethodWhat it does
limitry.workspace.approval_actions.list()List the actions you can ask approval for
limitry.workspace.approval_requests.create()Ask a person to approve an action
limitry.workspace.approval_requests.get()Check an approval request
limitry.workspace.approval_requests.redeem()Redeem an approved request

Audit events

MethodWhat it does
limitry.workspace.audit_events.list()List workspace audit events

Personal access tokens

MethodWhat it does
limitry.user.me()Get the current user
limitry.user.revoke_token()Revoke the token making this request

AsyncLimitry has the same methods to await, and guard and reserve as async with:

from limitry import AsyncLimitry
async def answer(prompt: str) -> str:
async with AsyncLimitry() as limitry:
async with limitry.reserve(
subject={"kind": "user", "id": "usr_123"}, action="ai-call", cost=5
) as reservation:
response = await call_model(prompt)
reservation.cost(price_cents(response.usage))
return response.text
  • 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").