> ## Documentation Index
> Fetch the complete documentation index at: https://pegana.xyz/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# REST API

> JSON over HTTPS. No API key. Soft rate limit. The canonical read path for Pegana state.

The REST API is the default integration path. Public, no auth, soft per-IP rate limit
(\~300 req/min in production; repo default is 60/min). Every dashboard, embed widget,
and the Telegram bot read through it.

**Base URL:** `https://api.pegana.xyz`

## Endpoints at a glance

| Method | Path                          | Purpose                                       |
| ------ | ----------------------------- | --------------------------------------------- |
| GET    | `/v1/assets`                  | List all tracked assets with latest snapshot  |
| GET    | `/v1/assets/{symbol}`         | One asset's detail card (24h series included) |
| GET    | `/v1/assets/{symbol}/state`   | Current peg state for one asset               |
| GET    | `/v1/assets/{symbol}/history` | Discount history (raw or 1-minute aggregate)  |
| GET    | `/v1/alerts`                  | Global feed of state transitions              |
| GET    | `/v1/stats`                   | Aggregate counters + delivery health          |
| GET    | `/healthz`                    | Liveness probe                                |
| GET    | `/readyz`                     | Readiness probe (db/redis/snapshot age)       |

The full schema lives in the [API Reference](/docs/api-reference/introduction).

## Read one asset's state

```bash theme={"theme":"github-dark"}
curl https://api.pegana.xyz/v1/assets/USDC/state
```

```json theme={"theme":"github-dark"}
{
  "asset": "USDC",
  "state": "PEGGED",
  "since": "2026-05-26T14:32:11Z",
  "discount": "-0.0002",
  "intrinsic_usd": "1.0000",
  "market_usd": "0.9998"
}
```

This is the single most-used endpoint. It returns the **current** peg state with the
two underlying values. Use it for dashboards, pre-flight checks, automation gates.

## List all assets

```bash theme={"theme":"github-dark"}
curl 'https://api.pegana.xyz/v1/assets?class=stable_fiat'
```

Returns an array of `AssetCard` objects with optional `series_24h` (hourly avg
discount). Filter by `class` or `peg` query params. `class` is a case-sensitive
exact match — valid values are `lst`, `stable_fiat`, `stable_dn`, `stable_cdp`,
`stable_yield`, `stable_fx`, and `synth_lev`. An unknown value (e.g. `stablecoin`)
returns an empty array with HTTP 200.

## Asset history

```bash theme={"theme":"github-dark"}
curl 'https://api.pegana.xyz/v1/assets/USDC/history?bucket=1m&limit=120'
```

* `bucket=raw` — discount snapshots as recorded by the engine
* `bucket=1m` — 1-minute aggregate (use for charts; smaller payload)
* `from`, `to` — ISO 8601 timestamps; default last 24h
* `limit` — clamped 1–5000, default 500

## Pagination

We do not paginate `/v1/assets` (a few dozen rows). For `/v1/alerts` and `/history`, use the
`since` / `from` + `limit` parameters and walk forward by adjusting the lower bound.

## Errors

We return standard HTTP status codes:

| Code  | Meaning                                                                 |
| ----- | ----------------------------------------------------------------------- |
| `200` | Success                                                                 |
| `400` | Bad request (invalid query param)                                       |
| `404` | Asset not found, or no snapshots yet                                    |
| `429` | Rate-limited (per-IP; \~300 req/min in production, repo default 60/min) |
| `500` | Server error — open a Sentry-style report                               |
| `503` | Not ready (readiness probe only)                                        |

Error bodies are `{"error": "...", "message": "..."}` (plus an optional `asset`) — short, machine-readable. Branch on `error`, never on `message`.

## CORS

`https://pegana.xyz`, `https://www.pegana.xyz`, and `http://localhost:3000` are
allowed by default. Add more origins via the `CORS_EXTRA_ORIGINS` env var on
self-hosted instances.

## Rate limit

Soft per-IP sliding-window: **\~300 req/min in production** (the repo default is
**60 req/min** — the production host raises it). The limit resets on a 60-second
sliding window. `/v1/audit.csv` is stricter at **10 req/min**. `/healthz` and
`/readyz` are exempt.

The `429` response includes a `Retry-After: 60` header (the sliding-window upper
bound) so clients know when to retry, but the API does **not** emit `X-RateLimit-*`
headers — your remaining budget is not client-discoverable, so handle `429`
defensively and back off. If you hit `429`, slow down or contact
`raffxweb3@gmail.com` for a higher limit.

## What about authenticated routes?

The `/v1/me/*`, `/v1/auth/*`, and webhook management endpoints require a JWT — see
[authentication](/docs/authentication). These are user-scoped routes (your subscriptions,
your delivery history, your webhook configuration).

## Next

<CardGroup cols={2}>
  <Card title="WebSocket stream" icon="bolt" href="/docs/guides/websocket">
    Sub-second push for state changes — better than polling.
  </Card>

  <Card title="API Reference" icon="code" href="/docs/api-reference/introduction">
    Full OpenAPI spec with try-it-now.
  </Card>
</CardGroup>
