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" } ]}The file
Section titled “The file”| Field | Description |
|---|---|
limits (array, required) |
Every limit you want, at most 200. Each entry takes the fields below, and its name is unique. |
Each limit
Section titled “Each limit”Each entry takes the same fields as creating a limit:
| Field | Description |
|---|---|
name | A name for the limit, unique among its environment's active limits. |
subjectKind | Your own label for who is checked. |
subjectId | One 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. |
action | The action to limit; * for every action. |
amount | How many units each subject may use per window. |
unit | What the amount counts (display only). Default: units. |
window | minute, 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. |
every | How many window units a period lasts: window: "month", every: 3 is a quarter, window: "hour", every: 5 five hours. Default: 1. |
start | Where 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. |
rolling | Count the last window instead of the calendar one (minute, hour or day only). Default: false. |
enabled | Whether checks enforce it. Default: true. |
failMode | What 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.
Syncing
Section titled “Syncing”limitry sync --dry-run # what a sync would change; changes nothinglimitry sync # make your limits match the filelimitry sync other.json # read another fileA 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.
When the file is the source of truth
Section titled “When the file is the source of truth”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.
From your code
Section titled “From your code”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);from limitry import Limitry
limitry = Limitry() # reads LIMITRY_API_KEY
plan = limitry.limits.sync( limits=[ { "name": "AI budget", "subjectKind": "user", "action": "ai-call", "amount": 200, "unit": "cents", "window": "month", } ], dry_run=True,)print(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.