Skip to content

limitry.json

limitry.json is optional: use it if you want your limits in your repository, changed through pull requests. Otherwise create them in the app, from the API or with the CLI (Limits). The file lists the limits you want. limitry sync reads it (./limitry.json unless you pass another path) and makes your limits match it. To run that on every merge, see Sync limits from GitHub.

{
"limits": [
{
"name": "AI budget",
"subjectKind": "user",
"action": "ai-call",
"amount": 200,
"unit": "cents",
"window": "month",
"failMode": "closed"
},
{
"name": "API burst",
"subjectKind": "apikey",
"action": "api-request",
"amount": 60,
"window": "minute",
"rolling": true
},
{
"name": "Exports at once",
"subjectKind": "workspace",
"action": "export",
"amount": 3,
"window": "concurrent"
}
]
}
Field Description
limits (array, required) Every limit you want, at most 200. Each entry takes the fields below, and its name is unique.

Each entry takes the same fields as creating a limit:

FieldDescription
namestring, requiredA name for the limit, unique among its environment's active limits.
subjectKindstring, requiredYour own label for who is checked.
subjectIdstringOne subject; omit or null for every subject of the kind. A subject's own limit replaces the kind's limits with the same action and window.
actionstring, requiredThe action to limit; * for every action.
amountnumber, requiredHow many units each subject may use per window.
unitstringWhat the amount counts (display only). Default: units.
windowstring, requiredminute, hour, day, week, month or year: a period of every of them, refilled where start says (or rolling: see rolling). total: a balance that never resets, topped up by grants. concurrent: at most amount held reservations at once.
everynumberHow many window units a period lasts: window: "month", every: 3 is a quarter, window: "hour", every: 5 five hours. Default: 1.
startstringWhere a period begins. calendar: UTC boundaries (midnight, Monday, the 1st, 1 January). anchor: from the subject's anchor, its billing date, set with subjects.update (its first check until then), so an annual plan can refill monthly from its start day. first_use: at the first check after the previous period ended. Default: calendar.
rollingbooleanCount the last window instead of the calendar one (minute, hour or day only). Default: false.
enabledbooleanWhether checks enforce it. Default: true.
failModestringWhat your code should do when Limitry can't be reached: open (allow) or closed (deny). Default: open.

From the API's Create a limit.

Names are the key. A sync creates a limit whose name the environment doesn’t have, changes one whose fields differ, and leaves alone one that already matches. Each entry describes its limit in full: a field you leave out means its default, as when you create a limit. Remove "failMode": "closed" from the file and the next sync sets it back to open.

Terminal window
limitry sync --dry-run # what a sync would change; changes nothing
limitry sync # make your limits match the file
limitry sync other.json # read another file

A key acts in its own environment, so a test key syncs your test limits and a live key your live ones. Limitry checks the whole file before it changes anything: if one entry is wrong, nothing changes and the error says which entry to fix. Add --json for the API’s answer as JSON.

Once a sync runs on every merge, the file decides. If someone changes one of these limits in the app, the next sync sets it back to what the file says. Change the file instead.

Limits the file doesn’t name are left alone, so limits you manage in the app keep working beside the file. To make the file the whole list, add --archive-missing to both commands in the workflow. A sync then archives every active limit that isn’t in the file. Archiving keeps a limit’s usage history and credits, and you can restore it from the Limits page or with limitry limits restore <limit-id>. Check the plan before you merge: it lists the limits a sync would archive.

The SDKs have the same operation, limits.sync, if you build the list in code, for example from your own plan catalog:

import { createClient } from "@limitry/sdk";
const limitry = createClient(); // reads LIMITRY_API_KEY
const plan = await limitry.limits.sync({
limits: [
{
name: "AI budget",
subjectKind: "user",
action: "ai-call",
amount: 200,
unit: "cents",
window: "month",
},
],
dryRun: true,
});
console.log(plan.created, plan.updated, plan.archived);

Leave out dryRun (dry_run) to apply it, and pass archiveMissing (archive_missing) to archive limits the list doesn’t name. The answer lists the names created, updated (with each field’s old and new value), archived and unchanged.