Markets
GET /v1/markets — Aave, Spark, Compound, Morpho, and Kamino K-Lend markets with supplied, borrowed, and per-reserve detail.
A market is the user-facing lending market a depositor picks:
| Protocol | Market is… | Reserves in it |
|---|---|---|
| Aave v2 / v3 | the Pool deployment | every aToken reserve in that pool |
| Aave v4 | the spoke (not the hub) | every reserve on that spoke |
| SparkLend | the Pool | every spToken reserve |
| Compound v3 | the Comet | one — the Comet's base asset |
| Morpho Blue | the loan/collateral pair | none — see below |
| Kamino K-Lend | the lending market | every reserve in that market |
Ethereum alone runs Aave V3 Main, Lido, EtherFi and Horizon side by side over overlapping assets with different collateral policies — which is exactly the distinction this surface exposes.
In Aave V4 the market is the spoke — Main, Bluechip, EtherFi Cash — not the liquidity hub.
Aave V4 separates the two: a hub pools liquidity per asset and owns the rate accounting, while
several spokes draw on it, each with its own collateral set and risk policy. Markets are keyed on
the spoke because that is what a depositor chooses; hub_address records which hub it draws from,
so markets sharing liquidity remain identifiable.
Every reserve inside a market is also served as its own vault row —
reserves[].vault_id links the two — and a market's totals are aggregated from exactly those rows
at request time, so the two surfaces cannot disagree.
Not to be confused with GET /v1/vault/{id}/markets, which returns a single Morpho vault's
allocation across isolated markets. That is a different surface and requires a Data-tier key.
GET /v1/markets
List lending markets with supplied/borrowed rollups.
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
| page | number | 1 | Page number |
| limit | number | 20 | Results per page (1–100) |
| protocol | string | — | Filter by protocol id, e.g. aave or kamino |
| version | string | — | Filter by protocol version, e.g. v4 or klend |
| chain | number or string | — | Chain: integer id (e.g. 10) or slug (e.g. ethereum, solana) |
| sortBy | string | total_supplied_usd | total_supplied_usd, total_borrowed_usd, reserve_count, or name |
| sortOrder | string | desc | Sort order: asc or desc |
Example
curl "https://api.philidor.io/v1/markets?protocol=aave&version=v4"Response
{
"data": [
{
"id": "aave-v4-10-etherfi-cash",
"protocol_id": "aave",
"protocol_version": "v4",
"chain_id": 10,
"market_key": "etherfi-cash",
"name": "Aave V4 EtherFi Cash",
"address": "0xdffcc3536d932eb51df51a7f5fa407c4270d5308",
"hub_address": "0x66753c4e3fc84f1ed0e3c267c927284e9d90c572",
"source": "onchain_rpc",
"reserve_count": 22,
"borrow_observed_count": 2,
"total_supplied_usd": 243900000,
"total_borrowed_usd": 18800000,
"utilization": 0.3542,
"last_synced_at": "2026-08-23T18:00:00Z",
"protocol_name": "Aave",
"chain_name": "Optimism"
}
],
"meta": { "page": 1, "limit": 20, "total": 12, "totalPages": 1 }
}Two market shapes, two totals sources
Pool-shaped markets (Aave, Spark, Compound, Kamino K-Lend) are parents: vaults belong to them and their totals are summed from those rows. Morpho Blue markets are one loan asset against one collateral asset — they are not parents, because MetaMorpho vaults allocate across many Blue markets rather than belonging to one.
totals_source always tells you which you are reading:
| Value | Meaning |
|---|---|
members | summed from this market's own /v1/vaults rows |
protocol_api | reported by the protocol (pair-shaped markets) |
Pair-shaped markets also carry loan_asset_symbol, collateral_asset_symbol and
lltv; these are null on pool-shaped markets, whose reserves each carry their own.
Morpho Blue is permissionless — anyone can deploy a market naming any oracle, and the upstream API
reports its nominal state. On Ethereum and Base alone that is ~5,978 markets, led by fabrications
such as a market quoting $5.3B supplied at 2,980% APY. We serve only the ~378 markets Morpho
itself lists; is_protocol_listed records that.
How the totals are computed
| Field | Definition |
|---|---|
total_supplied_usd | Sum of member reserve TVL — the supply side, borrowed amounts included |
total_borrowed_usd | Sum of utilization × TVL over reserves with an observed utilization |
utilization | total_borrowed_usd over the supplied value of those observed reserves only |
A reserve whose utilization is unknown contributes to supplied but not to borrowed.
borrow_observed_count tells you how many of the reserve_count reserves had one, so a partly
observed market is visible as such rather than silently understated. When no reserve in a market
has an observed borrow side, total_borrowed_usd and utilization are null — never 0.
Only active, non-shutdown reserves roll up into these numbers.
source
| Value | Meaning |
|---|---|
protocol_api | Discovered from the protocol's own API |
onchain_rpc | Discovered on-chain |
Kamino K-Lend markets use the same member-reserve totals model as the EVM pool-shaped markets. Their addresses are case-sensitive Solana base58 account keys, and their protocol version is klend.
Some Aave V4 instances are operated by third parties rather than the Aave DAO and are absent from the official Aave API — EtherFi Cash on Optimism is one. Those are indexed directly from chain, from a reviewed allowlist of hub addresses. Their numbers come from the same on-chain reads as every other Aave V4 row.
GET /v1/markets/{id}
Get a market with every reserve it contains.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
| id | string | Market ID, e.g. aave-v4-10-etherfi-cash |
The ID is {protocol}-{version}-{chain}-{market_key}. Version is part of it because one protocol
can run several generations whose market names collide — Main exists in both v3 and v4.
IDs are stable. A market's key is assigned the first time we see its contract address and is
never recomputed, so an upstream rename or the appearance of a similarly-named market elsewhere
cannot change an existing /v1/markets/{id} URL or re-point it at a different market.
Example
curl https://api.philidor.io/v1/markets/aave-v4-10-etherfi-cashResponse
{
"data": {
"market": {
"id": "aave-v4-10-etherfi-cash",
"name": "Aave V4 EtherFi Cash",
"chain_id": 10,
"total_supplied_usd": 243900000,
"total_borrowed_usd": 18800000,
"reserve_count": 22
},
"reserves": [
{
"vault_id": "aave-10-0x8f2c...",
"asset_symbol": "USDC",
"asset_address": "0x0b2c639c533813f4aa9d7837caf62653d097ff85",
"chain_id": 10,
"supplied_usd": 49423733,
"supply_apr": 0.0094,
"borrowed_usd": 18596000,
"borrow_apr": 0.03553,
"borrow_apr_net": 0.03553,
"utilization": 0.3763,
"fee_rate": 0.1,
"borrow_status": "open",
"is_depositable": true,
"deposit_capacity_usd": 7350000,
"risk_tier": "Core",
"total_score": 7.4,
"is_shutdown": false
}
]
}
}reserves is ordered by supplied value descending and includes shutdown reserves, flagged via
is_shutdown, so a market's history stays readable. Follow vault_id into
GET /v1/vaults/{id} for scores, exposures, and risk vectors.
Aave V4 availability
Aave V4 availability is read on-chain, from the two gates a borrow actually passes: the spoke's
reserve flags (paused, frozen, borrowable) and the hub's per-spoke gate (active, halted, and the
draw cap measured against what the spoke already owes). borrow_status is open only when both
were read and pass, capped when the hub draw cap has no room left, paused or disabled when
either gate says so, and unknown only when a read did not resolve. This holds for on-chain-discovered
instances too: on EtherFi Cash (Optimism) the chain reports USDC and WETH as open for borrowing and
the collateral-only liquid tokens as disabled.
deposit_capacity_usd is the hub's add-cap headroom (the same figure the Aave API publishes as the
supply cap, a sync fresher). It is null when the spoke is uncapped or when the headroom could not
be observed (hub gate, added assets or price unread) — read is_depositable alongside it, which is
null rather than true whenever the hub gate could not be observed and false when the cap has
no room left.
GET /v1/markets/{id}/events
Published risk events scoped to one lending market: incidents, bad debt and liquidation cascades (PHI-244). This is the surface that answers "what has gone wrong on this market" — raw protocol telemetry (routine repays, accruals, cap changes) is deliberately not part of it.
Scoping semantics
Market-scoped events carry scope: "protocol" with the on-chain market id (for Morpho Blue, the bytes32 market id) as their entity key. Vault-scoped BadDebt rows attributed to the same market are included, since bad debt is socialized across the market's lenders but recorded per affected vault. A market with no vault allocation still produces market-scoped events — coverage does not depend on a MetaMorpho vault holding the market.
Path Parameters
| Parameter | Description |
|---|---|
id | Market id, e.g. morpho-v1-1-pt-reusd-10dec2026-usdc-91-5 |
Query Parameters
| Parameter | Description |
|---|---|
limit | Max rows, 1-100 (default 50) |
Example
curl "https://api.philidor.io/v1/markets/morpho-v1-1-pt-reusd-10dec2026-usdc-91-5/events"Response
{
"data": [
{
"id": 7771205,
"market_id": "morpho-v1-1-pt-reusd-10dec2026-usdc-91-5",
"event_type": "Incident",
"severity": "Critical",
"title": "Liquidation cascade detected on Morpho Blue market 0x1e9d6146...a4fc64",
"description": "Cumulative liquidations of approximately $35,188,279 occurred ...",
"scope": "protocol",
"vault_id": null,
"loss_amount_usd": null,
"remediation_status": "unresolved",
"tx_hash": null,
"block_number": null,
"occurred_at": "2026-08-25T04:48:35.000Z",
"detected_at": "2026-08-25T18:20:00.000Z",
"data": {
"cumulative1hUsd": 35188279,
"cascadeTrigger": "$35,188,279 liquidated within 1 hour"
}
}
]
}Events are newest first. Only published, non-retracted rows are served.
Access
/v1/markets is anonymous, like /v1/vaults. An API key raises rate limits.