# Withdrawability + realized rate for one earn row

Endpoint reference for the 1delta API. Index: https://docs.1delta.io/llms.txt · every endpoint: https://docs.1delta.io/llms-full.txt

---

### GET /v1/data/earn/metrics

- operationId: `earn-metrics`
- docs: https://docs.1delta.io/1delta-api/earn-metrics/
- markdown: https://docs.1delta.io/1delta-api/earn-metrics.md
- tags: Earn

Withdrawability + realized rate for one earn row

**How likely is it that funds in this row get locked up — and what did it actually pay?** One market or vault, one window, measured from the recorded hourly series rather than quoted.

"Locked up" is four unrelated mechanisms, and the response keeps them as **separate blocks that are never blended into one score**:

| block | mechanism | what it is |
|---|---|---|
| `exit.capacityUsd` / `exit.capacityRatio` | **liquidity** — the pool is lent out | time-weighted quantiles of what could be withdrawn at once: `worst`, `p01`, `p05`, `p10`, `p25`, `median`, `p75`, `max`, `latest` (USD, and as a share of TVL). Read `p05` as "in 95 % of observed hours at least this much was withdrawable" |
| `exit.thresholds[]` | **liquidity**, at a size `x` | `pInst` (share of time with < x withdrawable), **`pHorizon[h]`** (share of start instants from which the market stayed below x for the whole `h` hours — the number to lead with: a market dry 23 of every 24 hours that clears nightly has `pInst ≈ 0.96` and `pHorizon[24] = 0`), dry-run episode statistics (`episodes`, `meanHours`, `medianHours`, `worstHours`, `pOver24h`, `meanResidualWaitHours`), `currentlyDry` / `currentRunHours`. Censored runs (touching the window edge or an outage) are counted, never dropped |
| `exit.contractual` | **contractual** — cooldown, queue, maturity | deterministic, from the row's own meta: `withdrawalMode`, `cooldownSeconds`, `instantFeeBps`, `hasInstantLeg` / `hasQueuedLeg`, `termDays` for fixed-term rows, `instantLiquidityRatio`, and a `note` for what no field can carry |
| `exit.administrative` | **administrative** — freeze, pause, cap | flag transitions inside the window from the origin's on-change log (`events[].changed`), the current flags, and `inactiveHours`. Lending rows only (`available`) |
| — | **solvency** — a haircut, not a wait | deliberately absent; see `/v1/data/lending/pools` risk fields |

`exit.mechanism` says which blocks apply: `liquidity` (pools, instant vaults, the instant leg of two-legged vaults), `contractual` (cooldown / queue / request-based vaults — **no liquidity statistics are computed**, because their liquidity is 0 by definition and "p = 1" for a known 7-day wait would be the wrong answer), `fixed-maturity` (PTs and fixed-term loans — an early exit is a sale, so capacity describes pool depth, not a wait).

`rate` carries the quoted deposit rate averaged by the time each quote was in force (`quoted.twaPct`, with `observedHours`), the **realized** return from the accumulator where one exists (`realized`: vault share price, or a lending supply index where backfilled — exact first-to-last ratio, `aprPct` and `apyPct`, negative preserved, `flat` flagged), and `gapPp = realized − quoted`.

**Every probability is a lower bound** (`window.lowerBound`): the series are hourly point samples, so a trough inside one hour is invisible. `window.coverage` is the share of the requested window actually observed; gaps longer than `window.gapCapHours` are outages, excluded rather than carried. Always render `coverage` next to any number from this endpoint.

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `earnUid` | query | string | yes | The row: a `vault.<provider>:<chainId>:<address>` earn uid from `/v1/data/earn`, or a lending `marketUid`. |
| `days` | query | integer | no | Lookback window in days (1–1095). |
| `x` | query | string | no | CSV of USD sizes to compute `thresholds[]` for (≤ 8). Default: a `1e4 … 1e8` ladder trimmed to rungs the market could ever satisfy. |
| `horizons` | query | string | no | CSV of horizons in hours for `pHorizon`. |
| `runs` | query | string | no | `1` to include up to 50 dry-run episodes per threshold (`runs[]`). |

**Response `200`** — The metrics envelope: `window`, `exit` (mechanism, contractual, capacityUsd, capacityRatio, thresholds[], administrative) and `rate` (quoted, realized, gapPp).

| Field | Type | Description |
| --- | --- | --- |
| `success` | true |  |
| `data` | object | Informational payload. `null` when the endpoint only builds calldata. |
| `actions` | null |  |

**Example response**

```json
{
  "success": true,
  "data": {}
}
```

**Response `400`** — Validation error

**Response `429`** — Rate limited. Unauthenticated callers share a per-IP budget; send an `x-api-key` header to lift it. Retry with exponential backoff.

**Response `500`** — Unexpected server error. Safe to retry with backoff.

**Response `502`** — An upstream data source or protocol origin failed (`error.code` is `ORIGIN_FAILED`). `error.details` carries the per-origin status. This is also what a missing or malformed required parameter currently returns, rather than a 400.
