Vaults

GET /v1/vaults — search and filter 800+ vaults by chain, protocol, tier, APR, TVL, and available liquidity.

GET /v1/vaults

List all vaults with filtering, sorting, and pagination.

Query Parameters

ParameterTypeDefaultDescription
pagenumber1Page number
limitnumber20Results per page, max 100
chainstringFilter by chain (comma-separated: ethereum,base)
protocolstringFilter by protocol ID (comma-separated: morpho,aave)
curatorstringFilter by curator ID (comma-separated)
assetstringFilter by asset symbol (comma-separated: USDC,WETH)
riskTierstringFilter by risk tier (comma-separated, case-insensitive: prime, core, edge)
minRiskScorenumberMinimum risk score (0–10, inclusive). Vaults without a score are excluded.
maxRiskScorenumberMaximum risk score (0–10, inclusive). Vaults without a score are excluded.
minScorenumberAlias of minRiskScore (vaults.fyi-style, 0–10). See below.
min_scorenumbersnake_case alias of minScore.
searchstringSearch vault name, symbol, asset, protocol, curator
minTvlstringMinimum TVL in USD
stablecoinstringFilter stablecoin vaults (true/false)
lsdstringFilter LSD vaults (true/false)
singleExposurestringFilter single-exposure vaults (true/false)
noILstringFilter no impermanent loss (true/false)
auditedstringFilter audited vaults (true/false)
highConfidencestringFilter high-confidence vaults (true/false)
depositablestringFilter by current deposit capacity (true/false). See below.
sortBystringSort field: tvl_usd, available_liquidity_usd, liquidity_deficit_usd, apr_net, borrow_apr_net, borrow_reward_apr, utilization, real_yield_share, name, total_score, last_synced_at
sortOrderstringdescSort order: asc or desc

The depositable filter

Sourced from the ERC-4626 maxDeposit(address(0)) call against each vault. Captures everything that gates new deposits at the contract level: caps reached, vault paused, supply queue saturated, gates blocking new shares.

Each vault response carries deposit_status ("open", "closed", or "unknown"). The filter maps to those states:

  • depositable=true → returns open and unknown vaults — the "not explicitly closed" set. Use this when you don't want to drop vaults that haven't been on-chain probed yet (typically non-ERC4626 protocols).
  • depositable=false → returns only closed vaults (confirmed maxDeposit == 0 on chain). Useful for monitoring exit-only positions.
  • Omit the parameter for the default unfiltered list.

For strict server-side filtering (only confirmed-open, exclude unknowns), filter on the response field client-side: data.filter(v => v.deposit_status === 'open'). A ?deposit_status=open query is on the roadmap.

The minScore filter (vaults.fyi mirror)

Integrators familiar with vaults.fyi minVaultScore (0–100, Reputation Score) often want the same pattern on Philidor's 0–10 risk score.

vaults.fyiPhilidor equivalent
minVaultScore=80minScore=8.0 or riskTier=prime
minVaultScore=70minScore=7.0 or riskTier=prime,core
minVaultScore=50minScore=5.0 or riskTier=prime,core,edge

Filter server-side with minScore (or min_score), the 0–10 mirror of vaults.fyi minVaultScore / 10:

const res = await fetch(
  'https://api.philidor.io/v1/vaults?minScore=7.0&depositable=true&asset=USDC&limit=100'
);
const { data } = await res.json();

minScore and min_score are aliases of the canonical minRiskScore — all three accept a 0–10 score and exclude vaults that carry no score. Combine with riskTier for tier-band filtering.

Note: riskTier values are case-insensitiveprime and Prime both resolve to the same tier.

Liquidity availability and deficits

Lending-vault results separate immediately available capital from over-utilization:

available_liquidity_usd = max(0, tvl_usd × (1 − clamp(utilization, 0, 1)))
liquidity_deficit_usd   = max(0, tvl_usd × (utilization − 1))

liquidity_status makes the interpretation explicit:

StatusMeaning
availableAvailable liquidity is positive and the deficit is zero.
exhaustedAvailable liquidity and deficit are both zero.
deficitRaw utilization exceeds 100%; available liquidity is zero and the deficit is positive when TVL is positive.
unknownTVL or utilization is unknown or inapplicable; both dollar fields are null.

The raw utilization field is not clamped and can exceed 1. A deficit status is a distress signal, but does not by itself prove realized bad debt.

This is a point-in-time catalog metric, not a guaranteed executable withdrawal quote. Protocol queues, pauses, gates, market movements, and transaction ordering may constrain the amount a user can withdraw. Use freshness_seconds and withdraw_status alongside it for routing decisions.

Borrow availability

borrow_status is the reserve-level signal for whether new borrowing is currently possible. It is deliberately separate from withdraw-side liquidity_status, the numeric borrow_apr, and the observation-quality field borrow_apr_status. In particular, borrow_apr: 0 can be an open idle market or a disabled reserve; consumers must read the status rather than infer it from the rate.

StatusMeaning
openSource-backed protocol state permits new borrowing.
disabledBorrowing is not enabled for this reserve.
pausedA protocol pause or reserve freeze blocks borrowing.
cappedAn observed hard borrow cap has no remaining capacity.
unknownPhilidor cannot currently prove the borrow permission.
not_applicableThe product has no reserve-level borrow market.

If a protocol still publishes a rate while paused or capped, the API may retain that numeric quote for context; borrow_status takes precedence for availability.

Borrow incentive decomposition

Money-market rows keep the canonical gross borrow quote in borrow_apr and report borrower incentives separately:

borrow_apr_net = borrow_apr - borrow_reward_apr

All three fields are decimal fractions. A negative borrow_apr_net is valid when priced rebates exceed the gross rate. Points programs remain visible in the detail response but contribute zero until they have an observable market price. Position-wide “net APR” claims such as Aave Merit net-position campaigns are retained as evidence but excluded because they cannot be attributed to the borrow leg alone.

borrow_incentive_status prevents missing provider data from silently becoming a zero rebate:

StatusMeaning
observedOne or more active borrower incentive campaigns were observed.
noneThe incentive source was checked successfully and no campaign was active.
unavailablePhilidor could not establish the current incentive state; net APR is null.
not_applicableThe product has no borrower-side market.

has_borrow_rewards is true for priced incentives and unpriced points. Full borrow_rewards rows are detail-only; list responses expose the aggregate fields so bulk scans remain compact.

gross_interest_apy is the lending market's gross supply-side interest before the protocol reserve factor or take, derived as borrow_apr × utilization and returned as a decimal fraction. It is distinct from base_apr, which is the net-of-fee interest contribution. Treat it as an operational pre-fee proxy, not an exact inverse of base_apr, because protocol sources can publish borrow and supply rates on different APR/APY bases. It is independent from borrow_status: existing debt can keep the value nonzero when new borrowing is paused, capped, or disabled, and a real observed zero stays zero. The field is null outside covered money markets or when its inputs are unavailable.

Example

curl "https://api.philidor.io/v1/vaults?riskTier=Prime&asset=USDC&sortBy=apr_net&sortOrder=desc&limit=5"

Response

{
  "data": [
    {
      "id": "morpho-1-0x...",
      "name": "Gauntlet USDC Core",
      "address": "0x...",
      "protocol_id": "morpho",
      "chain_id": 1,
      "chain_name": "Ethereum",
      "asset_symbol": "USDC",
      "tvl_usd": 125000000,
      "apr_net": 0.0523,
      "base_apr": 0.041,
      "utilization": 0.72,
      "borrow_apr": 0.046,
      "borrow_reward_apr": 0.012,
      "borrow_apr_net": 0.034,
      "borrow_incentive_status": "observed",
      "has_borrow_rewards": true,
      "borrow_status": "open",
      "gross_interest_apy": 0.03312,
      "active_loans_usd": 90000000,
      "available_liquidity_usd": 35000000,
      "liquidity_deficit_usd": 0,
      "liquidity_status": "available",
      "total_score": 8.5,
      "risk_tier": "Prime",
      "strategy_type": "isolated_lending",
      "curator_name": "Gauntlet",
      "is_audited": true,
      "deposit_status": "open",
      "is_depositable": true,
      "deposit_capacity_usd": 4250000,
      "last_synced_at": "2026-02-25T12:00:00Z"
    }
  ],
  "meta": {
    "page": 1,
    "limit": 5,
    "total": 42,
    "totalPages": 9
  }
}

GET /v1/vaults/{id}

Get a vault by its database ID.

Path Parameters

ParameterTypeDescription
idstringVault ID (e.g., morpho-1-0x...)

Example

curl https://api.philidor.io/v1/vaults/morpho-1-0x8eb67a509616cd6a7c1b3c8c21d48ff57df3d458

Returns full vault detail with risk vectors, snapshots, events, and rewards. Same schema as vault detail.

Query Parameters

ParameterTypeDefaultDescription
pointsnumber360Snapshot points embedded in the detail response, max 2160
resolutionnumber360Compatibility alias for points, max 2160

POST /v1/vaults/batch

Look up vaults by database ID or contract address. The combined ids and addresses arrays may contain up to 100 items.

Body

{
  "ids": ["morpho-1-0x8eb67a509616cd6a7c1b3c8c21d48ff57df3d458"],
  "addresses": ["0x8eB67A509616cd6A7c1B3c8C21D48FF57df3d458"]
}

Response

{
  "data": [
    {
      "id": "morpho-1-0x8eb67a509616cd6a7c1b3c8c21d48ff57df3d458",
      "name": "Gauntlet USDC Core",
      "total_score": 8.5,
      "risk_tier": "Prime"
    }
  ]
}

GET /v1/vaults/scores

Lightweight bulk screening endpoint. It returns essential score, TVL, APR, and identity fields without full risk vectors.

Query Parameters

The endpoint supports the same filters as GET /v1/vaults. The default limit is 500 and the max is 1000.

Response

{
  "data": [
    {
      "id": "morpho-1-0x8eb67a509616cd6a7c1b3c8c21d48ff57df3d458",
      "address": "0x8eB67A509616cd6A7c1B3c8C21D48FF57df3d458",
      "chain_id": 1,
      "chain_name": "Ethereum",
      "protocol_id": "morpho",
      "asset_symbol": "USDC",
      "tvl_usd": 125000000,
      "apr_net": 0.0523,
      "total_score": 8.5,
      "risk_tier": "Prime",
      "is_active": true,
      "last_synced_at": "2026-07-01T12:00:00Z"
    }
  ],
  "meta": {
    "page": 1,
    "limit": 500,
    "total": 1,
    "totalPages": 1
  }
}

GET /v1/vaults/changes

Efficient polling endpoint for vault rows modified after a timestamp or returned cursor.

Requires a Data-plan key from 2026-08-11. Until then, anonymous responses keep serving and carry Sunset notice headers. See API Access and Plans.

Query Parameters

ParameterTypeDefaultDescription
sincestringrequiredISO timestamp or cursor from the previous response
limitnumber100Results per request, max 500

Response

{
  "data": {
    "vaults": [
      {
        "id": "morpho-1-0x8eb67a509616cd6a7c1b3c8c21d48ff57df3d458",
        "total_score": 8.5,
        "risk_tier": "Prime",
        "last_synced_at": "2026-07-01T12:00:00Z"
      }
    ],
    "cursor": "2026-07-01T12:00:00.000Z|morpho-1-0x..."
  },
  "meta": {
    "total": 1
  }
}

GET /v1/vaults/with-critical-incidents

List vaults that have had critical incidents in the last 365 days, sorted by TVL.

Example

curl https://api.philidor.io/v1/vaults/with-critical-incidents

Response

{
  "data": [
    {
      "vault_id": "yearn-ethereum-0x...",
      "vault_name": "Yearn yETH",
      "chain_name": "Ethereum",
      "protocol_name": "Yearn",
      "curator_name": null,
      "tvl_usd": 5000000,
      "last_incident_at": "2025-11-30T00:00:00Z",
      "days_since_incident": 87,
      "incident_title": "yETH Stableswap Pool exploit",
      "incident_severity": "major",
      "event_severity": "Critical"
    }
  ]
}

On this page

Raw