Rate limits
On this page
A rate limit caps how many requests a caller may make in a period. Every
route has limits by default, so you write nothing to get them: 600 a
minute for each signed-in caller, and 120 a minute for each address on
routes open to anyone. A refused request gets the built-in
rate_limited failure: a 429 with a Retry-After header
(Rate-limited requests).
You change a limit with rate_limit:, on an API in app.jadpo (the
default for its routes) or on one route. Here a public form allows five
requests an hour from each address:
// domains/sales/routes/demo_requests.jadpoPOST / { authentication: RouteAuthentication.none rate_limit: { requests: 5 within: 1h by: RateLimitKey.ip } input: RequestDemo status: SuccessStatus.no_content run: request_demo(input)}
Who is counted: by:
by: says what a limit counts requests by:
RateLimitKey.caller, the default, counts each signed-in caller separately, or each address when the caller isn't signed in.RateLimitKey.ipcounts each address.
The address is the client's address as your app finds it (Client addresses). By default that is the address the connection comes from. If your app runs behind a proxy or load balancer of your own, you name the header that proxy sets, so the limits count each client, not the balancer. An IPv4 client is counted by its address. An IPv6 client is counted by its /64, the smallest block a network hands one subscriber, so moving to another address in its own block doesn't get it a fresh count.
The magic-link routes Jadpo generates (Magic links) are limited by address without you writing anything.
Where the counts are kept: rate_limits: { store: }
With no store named, each server counts in its own memory. To share the
counts between servers, name a Redis connection as the store, with
rate_limits: { store: cache } in app.jadpo's application { }
block. If the store can't be reached, each server counts in its own
memory, so limits stay roughly right and no request fails. Caches follow
the same rule (Caches).
The store must be a connection with driver: Driver.redis or
driver: Driver.cloudflare_rate_limits; anything else is a compile
error (rate_limits.store_invalid).
The same store also holds provider rate limits, a package's limits on calls to its own API (Calling). Your servers then share one count of calls to a provider, rather than each spending the whole allowance.
On Cloudflare Workers, a Worker's instances share no memory, so a
deployable built for Workers that serves an API must name a store, or
the compiler refuses it (rate_limits.store_required). The next section
shows the store Workers uses.
What a client sees
A refused request gets a 429 with Retry-After and the standard
RateLimit headers, its wording from translations like any failure's
(Rate-limited requests). Each limit
also appears in its route's OpenAPI entry and in jadpo routes.
Counting on Workers
An edge deployable built for Workers counts its rate limits in Cloudflare's binding:
application { connections: { limits: { driver: Driver.cloudflare_rate_limits namespace: 1001 } } rate_limits: { store: limits } deployables: { edge: { serves: [main] target: Target.wasm } }}
A connection with driver: Driver.cloudflare_rate_limits runs only on
Workers. Its namespace: is the first of the namespace ids in your
Cloudflare account that its bindings take. A binding counts one limit
over one window, so each limit your app counts there is a binding of its
own: LIMITS_120_PER_60S counts 120 a minute. The bindings are numbered
from namespace:, in order of window and then of requests, and
jadpo build writes each into the Wrangler file's ratelimits.
This store can count only windows of 10 or 60 seconds. A limit with any
other window is a compile error, which names Redis as a store that can
count it. The default limits are all per minute, so only a limit you
write yourself, such as within: 1h, can raise it.
The binding says only whether a request is within the limit, so a refused client is told to try again after the window's whole length. If the Worker hasn't got a binding, or the binding throws or doesn't answer within 500 ms, the instance counts in its own memory instead. That is logged once for each instance, and no request fails for it.
Rate limits on Workers has the details. What is built says how a Redis store counts.
Markdown for agents: /docs/rate-limits.md.Built from Jadpo main at a707efb.