# Quotas & rate limits | Rijwind docs

# Quotas & rate limits

Every Rijwind plan ships with a monthly request budget. We measure usage
in **quota units**, not raw HTTP requests — most endpoints cost one unit
per call, with a few exceptions.

## Cost per endpoint

| Endpoint                       | Cost                       | Notes                                            |
| ------------------------------ | -------------------------- | ------------------------------------------------ |
| `GET /tiles/v1/token`          | 1 unit                     | Per **map session**, not per tile — one map view is one unit. Terrain + contour tiles are included (no extra cost). See [styles](/styles). |
| `GET /search/geocode/v1/forward`       | 1 unit                     | Free-text address → coords.                      |
| `GET /search/geocode/v1/autocomplete` | **0.1 unit**               | Same backend, billed at a tenth so you can fire on every keystroke. |
| `GET /search/geocode/v1/reverse`      | 1 unit                     | Coords → nearest place.                          |
| `POST /directions/v1`               | 1 unit                     | Two or more waypoints.                           |
| `POST /directions-matrix/v1`              | `M × N` units              | `M = sources`, `N = targets`. Capped at 2500.    |
| `POST /isochrone/v1`           | `5 × L × C` units          | `L = locations`, `C = contours`. Capped at L×C ≤ 4 → max 20 units. |
| `POST /map-matching/v1`        | 1 unit                     | Snap a GPS trace to roads. Trace capped at 100 points. |
| `POST /optimized-route/v1`     | `N` units                  | `N = locations`. Optimal visiting order. Capped at 20 locations. |
| `POST /elevation/v1`           | 1 unit                     | [Elevation](/elevation) for points (≤ 512) or a route polyline. |
| `GET /styles/v1/{style}/static/…` | 1 unit (**2** for `@2x`)   | Server-rendered [static map image](/static-maps). |

Sub-unit costs (autocomplete = 0.1) are tracked internally with
integer arithmetic at `×10` resolution; the dashboard rounds to whole
units when displaying usage.

## When usage resets

The quota period is a **per-user 30-day cycle**, not a calendar month.
On a paid plan the cycle starts at your current billing anniversary
(the moment your subscription was last renewed); on the free plan it
starts at the moment you verified your email. The next reset moment
is shown on your [dashboard](https://rijwind.com/dashboard) so two
users on the same plan can reset on different days.

The boundary is checked on every billable request: when the cycle
rolls over, the counter resets to zero before the request is counted.
There is no separate cron job to wait for.

## Hard cap vs soft cap

Each plan has a `cap_mode`:

- **`hard`** — once you reach `monthly_request_limit`, every billable
  request returns `429 quota_exhausted` until the next period rolls
  over. This is the default for free + low-tier plans.
- **`soft`** — requests keep being served past `monthly_request_limit`,
  and the overage is billed per 1,000 requests at the end of the period.
  You set a **spending cap** (it defaults to your plan price); once the
  capped overage is reached, further requests return `429
  quota_exhausted` until the next period rolls over. So a normal spike is
  never blocked, but your bill stays bounded by the cap you choose. This
  is the default for paid plans.

You can see your plan's cap mode on the
[Plans page](https://rijwind.com/pricing) and your current usage on the
[dashboard](https://rijwind.com/dashboard).

## Request-shape caps (hard, plan-independent)

A few endpoints reject oversized request shapes before billing — these
caps protect the routing backend from runaway workloads and are not
plan-specific.

| Endpoint        | Cap                                   | Response                                          |
| --------------- | ------------------------------------- | ------------------------------------------------- |
| `/directions-matrix/v1`    | `M × N ≤ 2500`                        | `400 matrix_too_large` if exceeded.               |
| `/isochrone/v1` | `len(locations) × len(contours) ≤ 4`  | `400` if exceeded.                                |
| `/map-matching/v1` | `len(shape) ≤ 100`                 | `400` if exceeded. Use `encodedPolyline` for longer traces. |
| `/optimized-route/v1` | `2 ≤ len(locations) ≤ 20`       | `400` if outside the range.                       |

Asking for more than a cap allows is a request-shape error, not a
quota error — fix the request, retry.

## Per-IP and burst limits

The `/tiles/v1/token` endpoint additionally enforces a per-IP rate cap
at the CDN edge. This is independent of monthly quota — its job is to
bound the blast radius of a leaked token, not to ration anyone.

For now there is no hot-path per-second rate limit on the other v1
endpoints; quota is the only ceiling. If we add one, it will land here
first as a `Retry-After` header on `429`.
