TypeScript SDK
The SDK does two things. guard and reserve put a limit around your
code in one call. And every API operation is a typed method, so your
editor knows each field and answer.
Install
Section titled “Install”npm install @limitry/sdkSet LIMITRY_API_KEY to a key from API keys in the app. Use a test
key (lim_test_…) while you build.
Guard costly work
Section titled “Guard costly work”guard checks the limit, then runs your code only if it’s allowed. Here
it counts one AI call against a limit such as 100 a day:
import { createClient, LimitReached, Unavailable } from "@limitry/sdk";
const limitry = createClient(); // reads LIMITRY_API_KEY
try { const reply = await limitry.guard( { subject: { kind: "user", id: "usr_123" }, action: "ai-call" }, () => callModel(prompt), );} catch (error) { if (error instanceof LimitReached) { console.log(`Try again in ${error.retryAfterSeconds} seconds`); } else if (error instanceof Unavailable) { console.log("Limitry is unreachable and this limit fails closed"); }}A refusal throws LimitReached, with reason (limit_reached or
too_many_reservations), limitId and retryAfterSeconds.
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 your code and charges what it actually used. If
your code throws, the reservation is released:
import { createClient } from "@limitry/sdk";
const limitry = createClient();
const answer = await limitry.reserve( { subject: { kind: "user", id: "usr_123" }, action: "ai-call", cost: 5 }, async (reservation) => { const response = await callModel(prompt); reservation.cost(priceCents(response.usage)); // your prices, in cents return response; },);If Limitry can’t be reached, each limit’s fail mode decides: open
lets the work run, closed throws Unavailable, never LimitReached.
“Can’t be reached” means no decision within 1.5 seconds (timeoutMs),
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 1.5 seconds,
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. reservation.cost() takes a whole number from 0 to 1,000,000,000 and
throws a RangeError for anything else, before the work’s usage could be
lost. reservation.extend() throws the API’s error, such as for an
expired reservation. See
Add Limitry with your agent for more.
Methods
Section titled “Methods”Each API operation is a method, named like its command in the
CLI: limitry limits create is
limitry.limits.create().
import { createClient } from "@limitry/sdk";
const limitry = createClient();
await limitry.limits.create({ name: "AI replies per day", subject: { kind: "user" }, action: "ai-call", amount: 100, window: "day",});const { items } = await limitry.limits.list();- Ids in the path are arguments, and everything else is one object:
limitry.limits.get(limitId). - A method returns the response’s data. A failure throws an
ApiErrorwith the error’scode,message,statusandrequestId. - Every write sends an idempotency key, and a
failed attempt (a network error, a
5xx, a429) is retried with the same key, so it happens once. To make your own retry safe too, pass your key as the last argument:limitry.checks.create(check, { idempotencyKey: id }).
Every method, linked to its fields and a full example under Endpoints:
Checks
| Method | What it does |
|---|---|
limitry.checks.create() | Check whether a subject may act |
Reservations
| Method | What it does |
|---|---|
limitry.reservations.commit() | Commit a reservation |
limitry.reservations.extend() | Extend a reservation |
limitry.reservations.release() | Release a reservation |
Limits
| Method | What 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
| Method | What 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
| Method | What 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
| Method | What 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
| Method | What it does |
|---|---|
limitry.workspace.eventTypes.list() | List event types |
limitry.workspace.webhookDeliveries.list() | List an endpoint's deliveries |
limitry.workspace.webhookDeliveries.redeliver() | Redeliver an event |
limitry.workspace.webhookEndpoints.create() | Add a webhook endpoint |
limitry.workspace.webhookEndpoints.delete() | Remove a webhook endpoint |
limitry.workspace.webhookEndpoints.enable() | Re-enable a webhook endpoint |
limitry.workspace.webhookEndpoints.get() | Get a webhook endpoint |
limitry.workspace.webhookEndpoints.list() | List webhook endpoints |
limitry.workspace.webhookEndpoints.test() | Send a test event |
Approvals
| Method | What it does |
|---|---|
limitry.workspace.approvalActions.list() | List the actions you can ask approval for |
limitry.workspace.approvalRequests.create() | Ask a person to approve an action |
limitry.workspace.approvalRequests.get() | Check an approval request |
limitry.workspace.approvalRequests.redeem() | Redeem an approved request |
Audit events
| Method | What it does |
|---|---|
limitry.workspace.auditEvents.list() | List workspace audit events |
Personal access tokens
| Method | What it does |
|---|---|
limitry.user.me() | Get the current user |
limitry.user.revokeToken() | Revoke the token making this request |
Your key
Section titled “Your key”createClient()reads your key from theLIMITRY_API_KEYenvironment variable. Without it, it throws an error naming the variable. Where there’s noprocess.env, such as a browser or a Cloudflare Worker, pass the key:createClient({ token: env.LIMITRY_API_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:
createClient({ token, workspace: "acme", environment: "live" }).
Your own timeout or fail mode
Section titled “Your own timeout or fail mode”createLimits(client, options) gives you guard and reserve with a
different wait for a decision (timeoutMs, 1500 ms by default) or a
fixed fail mode:
import { createClient, createLimits } from "@limitry/sdk";
const limits = createLimits(createClient(), { timeoutMs: 500 });Calling a path directly
Section titled “Calling a path directly”limitry.GET, limitry.POST and the other HTTP methods call any path,
with the same types. They return { data, error, response } instead of
throwing, and unwrap turns that into the data or an ApiError.