Withdrawability + realized rate for one earn row
GET/v1/data/earn/metrics
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.
Plain-text reference — GET /v1/data/earn/metrics
Parameters
| Parameter | 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
| Field | Type | Description |
|---|---|---|
success | True | |
data | object | Informational payload. null when the endpoint only builds calldata. |
actions | null |
Example response
{
"success": true,
"data": {}
}
Request
Responses
- 200
- 400
- 429
- 500
- 502
The metrics envelope: window, exit (mechanism, contractual, capacityUsd, capacityRatio, thresholds[], administrative) and rate (quoted, realized, gapPp).
Validation error
Rate limited. Unauthenticated callers share a per-IP budget; send an x-api-key header to lift it. Retry with exponential backoff.
Unexpected server error. Safe to retry with backoff.
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.