---
title: API overview
description: Base URL, authentication, and how the surface is divided.
---

import Callout from '../../../components/Callout.astro';

```
https://api.zevauth.net
```

Every endpoint is under `/v1`. Requests and responses are JSON.

## Three surfaces, three credentials

The API is divided by **who is asking**, and the credential differs for each.

| Surface | Credential | For |
| --- | --- | --- |
| `/v1/auth/*` | Publishable key | Your frontend, signing people in |
| `/v1/users`, `/v1/organizations` | Secret key | Your backend, administering |
| `/v1/me/*` | Session token | A signed-in person, about themselves |

All three use `Authorization: Bearer …`. They are not interchangeable: handing
`/v1/me` a publishable key is refused, because that key ships in every browser
bundle and would otherwise read anybody's profile.

```bash
curl https://api.zevauth.net/v1/environment \
  -H "Authorization: Bearer pk_live_..."
```

## Origin locking

A publishable key is accepted only from your project's verified domain, its
subdomains, or a registered callback origin. A request from anywhere else is
refused. That is what stops a copy of your key working on a phishing site.

Requests with no `Origin` header, like `curl`, are allowed: a browser always
sends one, so its absence means the request is not coming from a page.

## Rate limits

Each environment has a per-minute request limit, readable from
`GET /v1/environment`. Development allows 1,000 requests a minute,
production 6,000.

The budget is per **environment**, so one project's traffic never spends
another's, and every response tells you where you stand:

```
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 994
X-RateLimit-Reset: 1767225660
```

Watch `X-RateLimit-Remaining` and slow down before you are refused. A client
that only finds out at the `429` has already lost the request.

Exceeding it returns `429` with code `rate_limited` and a `Retry-After` header
in seconds:

```json
{
  "error": {
    "code": "rate_limited",
    "message": "This environment is over its limit of 1000 requests per minute. Retry in 34s."
  }
}
```

<Callout type="info">
It is a **sliding** window, not a fixed one. A fixed window resets on the
minute, so a caller could spend the whole budget in the last second of one
window and the whole budget again in the first second of the next: double the
limit, back to back. The sliding window counts the trailing minute, so that
burst is still counted.
</Callout>

## Idempotency

Endpoints that start a flow, such as magic links, email codes and password
resets, answer identically whether or not the address belongs to anybody, and
can be called repeatedly. This is deliberate: a different response would turn them
into a way to test whether somebody has an account with you.

## Billing counts production only

Usage is measured in **monthly active users**: somebody who authenticated at
least once in the period. Signing in forty times counts once, and development
environments are never billed.