# Rate limits, quotas & errors

> Per-plan monthly quotas, per-minute burst limits, rate-limit headers, and every error shape the API returns.

Plans are compared on [the pricing page](/#pricing); this page is the full
fine print. Limits are enforced at the API gateway, per account — all your
keys share the same budgets.

## Monthly quotas

| | Free | Pro | Advanced |
|---|---|---|---|
| Elevation lookups / month | 10,000 | 1,000,000 | 5,000,000 |
| Data out / month | 500 MB | 50 GB | 250 GB |

- **Lookups are counted per point, not per request.** A
  `points` call with 512 coordinates, or a `sample` call with
  `samples=512`, costs 512 lookups. Raster calls don't consume lookups.
- **Data out** covers raster-extract response bytes and
  [COG downloads](/docs/downloads) (charged at full file size). Point
  lookup responses are tiny JSON and don't count.
- Quotas are charged only on successful (2xx) responses and reset at the
  start of each calendar month (UTC).
- Need more? [contact@darly.io](mailto:contact@darly.io).

## Per-minute burst limits

| | Anonymous | Free | Pro | Advanced |
|---|---|---|---|---|
| STAC requests / min | 30 (per IP) | 300 | 1,000 | 1,000 |
| Elevation points / min | — | 100 | 3,000 | 12,000 |
| Raster calls / min | — | 2 | 20 | 60 |
| Max points per request | — | 100 | 512 | 512 |
| Max raster size (px per side) | — | 512 | 2,500 | 2,500 |
| STAC page size (default / max) | 10 / 100 | 10 / 100 | 10 / 100 | 10 / 100 |

Elevation points/min is point-weighted like the monthly quota: one 512-point
batch consumes 512 units of a Pro plan's 3,000/min budget. Burst capacity is
reserved before a request is processed, so requests that finish with a `4xx`
or `5xx` still consume per-minute capacity. They are never charged to monthly
quotas. A request rejected by the burst limiter does not consume additional
capacity; wait until `X-RateLimit-Reset` before retrying. There are no overage
bills: past a monthly quota the API returns `429` until the month rolls over or
you upgrade.

STAC requests/min is page-weighted: `/search` and `/collections/{id}/items`
calls consume one unit per 10 items requested (a `limit=100` page costs 10
units; everything else, including metadata routes, costs 1). Small pages and
narrow filters stretch your budget further than crawling at `limit=100`.

## Rate-limit headers

Every response carries the state of the limiter that applied to it:

```text
X-RateLimit-Limit: 300
X-RateLimit-Remaining: 297
X-RateLimit-Reset: 1783041000
```

`X-RateLimit-Reset` is a Unix timestamp (seconds). On `429`, wait until the
reset before retrying — a simple sleep-until-reset loop is more effective
than exponential backoff here, because windows are fixed, not sliding.

## Error reference

Key off the **HTTP status code** (and `X-RateLimit-*` headers), not the body
text.

| Status | When | Body |
|---|---|---|
| `400` | Invalid elevation parameters | `{"status": "INVALID_REQUEST", "error": "…"}` |
| `401` | Elevation or download call without authentication | `{"detail": "Authentication is required for the elevation API"}` / `…to download assets"}` |
| `403` | Plan doesn't include the endpoint (e.g. downloads on Free) | `{"detail": "The current plan does not include this endpoint"}` / `…downloads"}` |
| `404` | Unknown route, collection, item, or asset — including collections outside your plan | `{"detail": "Not found"}` |
| `405` | `POST` to a STAC route other than `/search` | `{"detail": "Method not allowed"}` |
| `429` | Per-minute limit hit | `{"detail": "Rate limit exceeded"}` + `X-RateLimit-*` headers |
| `429` | Monthly quota exhausted | `{"detail": "Monthly lookup quota exceeded"}` / `{"detail": "Monthly data-out quota exceeded"}` |
| `500` | Elevation backend failure | `{"status": "SERVER_ERROR", "error": "…"}` |
| `502` | Upstream service unavailable | `{"detail": "…"}` |

An invalid or revoked key is **not** an error on STAC routes — it falls back
to anonymous access silently. See
[authentication](/docs/authentication#auth-errors).

## Playing nice

- Batch point lookups into `points` calls instead of many `point` calls —
  same lookup cost, far fewer requests against the per-minute window.
- Cache responses on your side; elevation for a fixed location doesn't
  change between data releases.
- Use `bbox`/`datetime` filters and reasonable `limit`s on STAC search
  instead of paginating the world.
