Limits.

Two rate limiters, a few fixed ceilings, and your package's allowances. Every answer tells you where you stand, so a program can pace itself instead of finding a limit by hitting it.

Working — v1 at api.ardaform.net

The account's rate

  • Calls are counted per account, across all its keys and projects: api_per_min a minute, with bursts up to api_burst. Both are in limits on GET /v1/auth; the test account on these pages has 600 a minute and a burst of 1200.
  • It is a token bucket, not a fixed minute: the bucket holds api_burst calls and refills continuously at api_per_min. A quiet program can spend a burst at once; a busy one gets the steady rate.
  • A call counts once its key is known — including one refused afterwards for its scope or a missing dataset. A call refused for its key is not counted, and neither are GET /v1 and GET /v1/openapi.json, which need no key.

The headers

On every answer to a keyed call — not only on the refusal:

Answer headers ran live 2026-10-27 · test project
x-ratelimit-limit: 1200                   the bucket depth (api_burst)
x-ratelimit-remaining: 1199               calls left in it
x-ratelimit-reset: 1                      seconds until it is full again
x-ratelimit-policy: 600;w=60;burst=1200   the rate, the window, the depth
  • Limit is the bucket depth, not the per-minute rate: Remaining counts down from it. The rate is in Policy.
  • Pace from Remaining: when it gets low, slow to api_per_min.

When it is spent

Answer · 429 Too Many Requests not run — the shape, from the API's code
retry-after: 1

{
  "error": {
    "code": "RATE_LIMITED",
    "message": "Your account is limited to 600 API calls a minute, with bursts to 1200. Wait 1s.",
    "limit": 600,
    "burst": 1200,
    "retry_after": 1
  }
}
  • Wait Retry-After seconds, then carry on. It is the time for one call to come back, so a program that keeps calling at its full speed will meet it again: slow to api_per_min.
  • Not run here because it means spending the test account's whole burst.
  • If the counter is unavailable on our side the API lets calls through rather than refusing them; the web server's limit still applies.

The web server's limit

In front of the API, the web server allows 100 requests a second from one address, with a burst of 40, to protect the machine. Past it the answer is a 503 with an HTML page, not JSON and not the account's 429. Slow down and retry with a backoff. A program inside its account's rate never meets it.

Sizes and pages

WhatLimitOver it
A request body10 MB (10,485,760 bytes) — every endpoint, files included413 TOO_LARGE, as JSON with limit_bytes — the same answer whether the web server in front or the API refuses it
A file's bytesthe 10 MB body: 10 MB sent as it is, about 7.5 MB as base64413 TOO_LARGE
Records in one write10,000 (max_records_per_call)400 TOO_MANY
A page of records or changes1–1000, default 100clamped, not refused
A page of files1–1000, default 200clamped
A page of webhook deliveries1–200, default 50; 50 with include=payloadcut to the cap
A record key, a partition value190 characterscut to 190, not refused
A setting's value64 KB as stored400 INVALID_VALUE
A webhook's data (from the app)64 KiBrefused, and the app is told

Storage

Records and files share one allowance with your app's code. A write is checked against three ceilings, innermost first, before anything is written, and each has its own word because each is a different conversation:

CeilingWordWho moves it
the dataset's own storage_limit_bytes (retention never)403 DATASET_FULLyou, on the dataset
the project's storage limit, set in the portal403 PROJECT_FULLthe account owner, in the portal
the account's storage_mb403 STORAGE_FULLthe account owner, by changing package

Each carries limit_bytes, used_bytes and adding_bytes. What a PUT replaces counts as freed, so republishing a catalogue of the same size always fits. A dataset with retention_mode days or space makes its own room on each write and says so in pruned. See retention.

Webhooks

  • webhooks — whether the package includes them (403 NO_WEBHOOKS on create, change and test; reads still work).
  • webhook_endpoints — how many the account may have, across all projects (403 LIMIT_REACHED); 0 is no limit.
  • webhook_per_min — deliveries a minute ArdaForm makes for the account; an app's call over it is refused on the device, and a Test over it is 429 RATE_LIMITED.
  • The delivery log keeps 500 deliveries or seven days per webhook.