# Rate limits

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](rate-limited-requests.md)).

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:

```jadpo
// domains/sales/routes/demo_requests.jadpo
POST / {
    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.ip` counts each address.

The address is the client's address as your app finds it
([Client addresses](client-addresses.md)). 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](magic-links.md)) 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](caches.md)).

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](calling.md)). 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](rate-limited-requests.md)). 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:

```jadpo
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](rate-limits-on-workers.md) has the details.
[What is built](built-in-services-built.md) says how a Redis store counts.
