For the complete documentation index, see llms.txt. This page is also available as Markdown.

Mystic Morpho REST API

Read real-time Morpho data on the chains Mystic is on, including vaults and markets data, APYs, TVL, liquidity, rewards and much more.

Introduction

The Mystic Morpho API lets you read all kinds of Morpho data in real-time on the chains Morpho is on. Track your positions, liquidate, manage risk or build your own Morpho-powered app using Mystic's Morpho API. Note that to use it, you will need your own API Key. Please contact us to get an API Key and get started -> Contact us.

Base URL

https://api.mysticfinance.xyz
https://staging-api.mysticfinance.xyz

OpenAPI Specification

The full machine-readable API spec available for AI agents, code generators, and API clients:

Contents

  • Why Mystic

  • Authentication

  • Quickstart

  • API reference

    • General

    • Vaults

    • Borrows

    • Portfolio

    • Transactions

    • Historical

    • Realtime

    • Liquidations

  • Supported protocols and chains

  • Errors

Why Mystic

Use one API for DeFi earn and borrow data, risk context, eligibility screening, portfolio tracking, position history, and live on-chain state.

Mystic is the API layer for onchain earn and borrow features. Use one integration to discover vaults, compare risk and return, screen out markets that should never reach a user, size a borrow, track positions and returns after a deposit, and see what happened when capital move across 1.5K+ vaults, 5.5k+ borrow markets, and 6+ chains.

Use Mystic when your product needs to do more than show APYs, to decide which vaults and markets are eligible, explain why one beats another, tell a user what they hold and what it has earned, and report what happened after capital moved, including the liquidations.

Authentication

To call the API, you need to an API Key. Contact the Mystic team to get one and add it to the header of the api call like below. API Key usage is billed monthly and the Key will expire if payment fails. An expired key returns 401.

Rate limit: 20 - 100 requests/second on data endpoints depending on your API plan. When you are over the limit, it returns 429

Contact us to get your API Key

Quickstart

Read a vault and a user's position in it, in 5 steps:

  1. Fetch the list of vaults

  2. Fetch a vault's details

  3. Fetch a vault's allocations

  4. Check a user's position in that vault

  5. Check the whole portfolio

Read a borrow market and a user's position in it, in 6 steps:

  1. Fetch the list of borrow markets

  2. Fetch a market's details

  3. Fetch a market's allocations

  4. Check a user's borrow positions

  5. Narrow to one market

  6. Check the whole portfolio

Two conventions before you start:

Rates are decimal fractions, not percentages. 0.0752 means 7.52%. This holds for every APY, LLTV, utilization, fee and liquidation penalty in every request and response, minApy=0.05 filters at 5%, not 5 basis points.

Amounts are decimal strings, not numbers. "12403118.44", already scaled by the token's decimals.

All examples use JavaScript with axios for HTTP.

Vaults

1. Fetch the list of vaults

Filter and rank across every supported network in one call. This is the endpoint a "where should I deposit?" screen is built on:

Example response (GET /vaults?assetGroup=usd&minTvl=1000000&minApy=0.05&sortBy=apy&perPage=10):

Read apy.instantaneous.reward against apy["30day"].reward before you render a headline rate.

2. Fetch a vault's details

Split the vaultId and read the vault in full:

The detail response is the same object as the list plus allocations, governance, collaterals, capacity, protocolSpecific and the full score breakdown, see the vault object for every field.

A missing vault is a 404, never a 200 with a null body, so a failed lookup throws 404 in axios.

3. Fetch a vault's allocations

Where the deposits actually go, the answer to "what risk am I taking?":

Example response, largest share first:

4. Check a user's position in that vault

Example response:

5. Check the whole portfolio

One call for every total across every indexed network:

Example response (GET /portfolio/summary/0xd8dA…6045):

Putting the vault flow together

Borrows

1. Fetch the list of borrow markets

Example response:

Bad-debt and unverified markets are excluded by default. Pass includeBadDebt=true / includeUnverified=true for the unfiltered set; both are also reported in tags and flags when present.

supplyingVaults is omitted from list rows to keep them small. Add ?expand=supplyingVaults when you need it in a list, an unrecognised expand value is a 400, not a silently slim response.

2. Fetch a market's details

The detail response adds reallocatableLiquidity, capacity, oracle, irm, fee, performanceFee, vaultCaps and the fully-expanded supplyingVaults, see the market object.

liquidity is everything reachable; reallocatableLiquidity is the slice that only arrives if a public allocator acts. Borrowing against the first number and assuming the second is instant is how a large draw fails at signing time.

3. Fetch a market's allocations

Which vaults back the market, and how much each has in it, depth is only as real as the vaults supplying it:

4. Check a user's borrow positions

Example response:

healthFactor is collateral × liquidation threshold ÷ debt, and is null when there is no debt, a position that cannot be liquidated has no health factor. Do not treat null as zero: it is the safest possible state, not the most dangerous one.

5. Narrow to one market

There is no per-market position route, borrow positions come back for the whole wallet, so filter the response by marketId:

Scope the fan-out with allowedNetworks when you already know the chain — it is one indexer round-trip per network, so naming the one you need is faster and no more expensive:

To see how a position got where it is, every borrow, repayment and liquidation against it — read /portfolio/events/{address} with eventType=borrow,repay,liquidation.

6. Check the whole portfolio

The same two calls as the vault flow. For a borrower, three fields carry the answer:

lowestHealthFactor is what an alerting system should watch. It is null only when the wallet has no debt anywhere.

Putting the borrow flow together

API reference

Every route below is prefixed with https://api.mysticfinance.xyz/v1/yields and all are GET endpoints.

The list envelope

Every list response, without exception:

errors is a degradation, not a failure. Ask for allowedNetworks=plume,atlantis and you get every Plume vault plus unsupportedNetworks: ["atlantis"] , rather than a 400 that discards the results you could have had.

Shared query parameters

Every list endpoint accepts these. Endpoint sections document only what they add.

Field
Type
Required
Description

page

integer

Default 1.

perPage

integer

Default 50, max 500.

allowedNetworks

string

Slugs or chain ids: plume,base or 98866,8453. Unknown entries land in errors.unsupportedNetworks.

disallowedNetworks

string

Same format, excluded instead.

chainId

integer

Single-network alias for callers migrating from /morphoCache.

search

string

Free-text match on name, symbol or address.

sortOrder

enum

asc | desc. Default desc.

/v1 is required on every route below.

General

Route
Credits
Description

/health

0

Per-network cache age

/networks

0

Supported networks with vault/market counts

/protocols

0

Protocols and their declared capabilities

/tags

0

Tag vocabulary with live counts

/curators

2

Curators with vault counts and TVL

/assets

2

Every asset used by a vault or market

/assets/{network}/{address}

2

One asset, with where it is used

GET /v1/yields/health

Freshness per chain, so you can decide what staleness you tolerate rather than trusting a bare 200. Exempt from rate limiting.

Request — no parameters.

Response

null for an age means that cache has never been written for that chain.

GET /v1/yields/networks

Request — no parameters.

Response

A supported network with zero counts is a real state (a chain whose cron has not populated yet), reported rather than hidden.

GET /v1/yields/protocols

Capabilities are advertised, not implied — you can see that a protocol cannot answer share-price history instead of discovering it as an empty series.

Request: no parameters.

Response

GET /v1/yields/tags

Counts come from the same derivation the vault list uses, so ?tags=stablecoin returns the number shown here.

Response

Full vocabulary: stablecoin, eth, btc, rwa, v2, incentivised, blue-chip, bad-debt, unverified.

GET /v1/yields/curators

Request — shared parameters only.

Response — list envelope, highest TVL first.

Curators named Unknown are omitted rather than aggregated into a fake entity.

GET /v1/yields/assets

Every asset that appears in a vault or market we index — derived, not curated. An asset outside that set has nowhere to be deposited, so listing it would be noise.

Request

Field
Type
Required
Description

assetGroup

csv

usd | eth | btc | rwa | other

Plus the shared parameters.

Response — list envelope, most-used first.

The same symbol on two chains is two entries, different tokens, different prices.

GET /v1/yields/assets/{network}/{address}

Request

Field
Type
Required
Description

network

path

Network slug.

address

path

Asset contract address.

Response: one asset object as above. 404 when the asset is not used by anything we index.


Vaults

Route
Credits
Description

/vaults

1

List vaults — the main discovery endpoint

/vaults/{network}/{vaultId}

1

One vault, in full

/vaults/{network}/{vaultId}/apy

1

APY breakdown alone — a cheap poll

/vaults/{network}/{vaultId}/tvl

1

TVL and liquidity alone

/vaults/{network}/{vaultId}/allocations

1

Where the vault deploys its assets

/vaults/{network}/{vaultId}/campaigns

2

Reward campaigns: token, budget, end date

/vaults/{network}/{vaultId}/holders

2

Holder count and largest positions

GET /v1/yields/vaults

Defaults to all networks, an omitted network filter is not silently treated as Plume.

Request

Field
Type
Required
Description

sortBy

enum

tvl (default), apy, liquidity, name, score, createdAt, holders

view

enum

full (default) | slim — see below

expand

string

score. Adds the component breakdown a list otherwise omits. An unrecognised value is a 400.

allowedAssets / disallowedAssets

string

Vault assets, by symbol or address

assetGroup

string

usd | eth | btc | rwa | other

allowedProtocols / disallowedProtocols

string

See /protocols

curators

string

Names or addresses

tags

string

See /tags

minTvl / maxTvl

number

USD

minApy / maxApy

number

Fractions. 0.05 is 5%.

apyInterval

enum

instantaneous, 1hour, 1day (default), 7day, 30day — which window minApy/maxApy/sortBy=apy read

minLiquidity

number

USD

minVaultScore

number

0–100. Vaults with no computed score are excluded — an unscored vault cannot satisfy a floor.

version

enum

v1 | v2 | all (default)

Plus the shared parameters.

Response: list envelope of vault objects.

view=slim returns identity, asset, APY, TVL and tags only — same vaults, same order, same filters, smaller payload:

It is a parameter rather than a second route so filtering and sorting cannot diverge between the two.

GET /v1/yields/vaults/{network}/{vaultId}

Request

Field
Type
Required
Description

network

path

Network slug, e.g. plume.

vaultId

path

Vault contract address.

Response — one vault object, including the detail-only fields. 404 when it does not exist.

The vault object

instantaneous is the spot rate,a null APY window is meaningful. It means no data covers that period, a two-day-old vault genuinely has no 30-day APY. The spot rate is never copied in.

Three allocation kinds exists, the first is the idle for idle liquidity, the second is market for market allocation and the third is vault for vault allocation. The vault allocation is only available for v2 vaults.

GET /v1/yields/vaults/{network}/{vaultId}/apy

GET /v1/yields/vaults/{network}/{vaultId}/tvl

GET /v1/yields/vaults/{network}/{vaultId}/allocations

Largest share first. The same array the detail response carries inline.

GET /v1/yields/vaults/{network}/{vaultId}/campaigns

Response

GET /v1/yields/vaults/{network}/{vaultId}/holders

Requires an indexer. See coverage.

Requestpage, perPage (default 50, max 500).

Response

On an unindexed network the list is empty, totalCount is null, the network is in errors.unsupportedNetworks, and unavailableReason says why — rather than an empty array that reads as "this vault has no holders".


Borrows

Route
Credits
Description

/borrow/markets

1

List markets

/borrow/markets/{network}/{marketId}

1

One market

/borrow/markets/{network}/{marketId}/allocations

1

Which vaults supply it, and how much

GET /v1/yields/borrow/markets

Request

Field
Type
Required
Description

sortBy

enum

tvl (default), supplyApy, borrowApy, utilization, liquidity, lltv, score

expand

string

supplyingVaults, score. Unrecognised values are a 400.

allowedCollateralAssets / disallowedCollateralAssets

string

Symbol or address

allowedBorrowAssets / disallowedBorrowAssets

string

The loan asset, by symbol or address

allowedProtocols / disallowedProtocols

string

See /protocols

tags

string

See /tags

minTvl / maxTvl

number

Total supplied, USD

minLltv / maxLltv

number

Fractions. 0.86 is 86%.

minUtilization / maxUtilization

number

Fractions

minBorrowApy / maxBorrowApy

number

Fractions

minMarketScore

number

0–100

hasLiquidity

boolean

Only markets with borrowable liquidity

includeBadDebt

boolean

Default false

includeUnverified

boolean

Default false

Plus the shared parameters.

Response: list envelope of market objects.

GET /v1/yields/borrow/markets/{network}/{marketId}

Request

Field
Type
Required
Description

network

path

Network slug.

marketId

path

The 32-byte market id (without the network prefix).

The market object

liquidity vs reallocatableLiquidity. The first is total reachable liquidity: the market's own free liquidity plus whatever a public allocator can move in. The second is that reallocatable component alone. They behave differently, free liquidity is available now, reallocatable liquidity depends on an allocator acting, and a borrower sizing a draw needs to know which is which.

supplyingVaults is tiered. Lists carry the identity fields (everything /morphoCache/lite published, plus ids); the detail endpoint and ?expand=supplyingVaults carry every field. Expanded fields are omitted rather than nulled, so 'cap' in vault distinguishes "not asked for" from "no cap set".

vaultCaps is not supplyingVaults. It is what a public allocator could move, which is the number that decides whether a large borrow can be filled.

GET /v1/yields/borrow/markets/{network}/{marketId}/allocations


Portfolio

Wallet-scoped reads. Every response carries errors.unsupportedNetworks naming any chain we could not see, for a balance figure that matters more than anywhere else in the API.

Route
Credits
Description

/portfolio/summary/{address}

3

Everything below, in one call

/portfolio/positions/{address}

2

Vault positions across networks

/portfolio/positions/{address}/{network}/{vaultId}

2

One position

/portfolio/borrow-positions/{address}

2

Collateral, debt, LTV, health factor

/portfolio/total-returns/{address}/{network}/{vaultId}

2

Lifetime realised + unrealised return

/portfolio/partial-returns/{address}/{network}/{vaultId}

2

Return between two timestamps

/portfolio/historical-positions/{address}

2

Every vault ever held, including exited

/portfolio/historical-balances/{address}/{timestamp}

2

Balances reconstructed at a past date

/portfolio/idle-assets/{address}

2

Holdings not deployed anywhere we index

/portfolio/best-vault/{address}

3

Best vault per idle asset

/portfolio/best-deposit-options/{address}

3

Top N vaults per idle asset

Shared portfolio parameters

Field
Type
Required
Description

address

path

Wallet address.

page, perPage

integer

Default 1 / 50, max 500.

allowedNetworks

csv

Slugs or chain ids.

allowedAssets

csv

Symbols or addresses.

minUsdValueThreshold

number

Default 0.01. Suppresses dust — a wallet that ever touched a vault keeps a rounding-dust position forever.

GET /v1/yields/portfolio/summary/{address}

Replaces roughly five separate requests, and is priced accordingly.

An average health factor would hide one position about to be liquidated behind several safe ones, so the lowest is reported instead.

GET /v1/yields/portfolio/positions/{address}

Response — list envelope of positions.

GET /v1/yields/portfolio/positions/{address}/{network}/{vaultId}

One position object as above. 404 when the wallet holds nothing in that vault.

GET /v1/yields/portfolio/borrow-positions/{address}

The indexer models collateral and debt as separate rows; they are re-joined here so a position is one object, not two halves.

healthFactor is collateral × liquidation threshold ÷ debt. It is null with no debt — a position that cannot be liquidated has no health factor, and returning Infinity would force every caller to special-case it.

GET /v1/yields/portfolio/total-returns/{address}/{network}/{vaultId}

Current value plus everything withdrawn minus everything deposited, denominated in the vault asset — a USD figure would conflate yield with the asset's own price movement.

GET /v1/yields/portfolio/partial-returns/{address}/{network}/{vaultId}

Period performance rather than lifetime. Contributions inside the window are subtracted, so a mid-window deposit does not read as a gain.

RequestfromTimestamp and toTimestamp are both required (a period return needs a period); toTimestamp must be after fromTimestamp. Either omitted, or reversed, is a 400.

closingValueIsCurrent is stated rather than implied: a toTimestamp in the past cannot be honoured exactly, because we hold no historical share price for every vault. The closing value is the current balance.

GET /v1/yields/portfolio/historical-positions/{address}

Every vault the wallet has ever held, including fully-exited ones, with when each opened and last saw activity. positions only shows current holdings, so a wallet that exited last month vanishes from it entirely.

Request — the shared portfolio parameters plus fromTimestamp, toTimestamp, eventType, and includeClosed (default true).

GET /v1/yields/portfolio/historical-balances/{address}/{timestamp}

Replayed from the transfer stream: Σdeposits − Σwithdrawals up to that instant.

Request: timestamp is a path parameter in unix seconds; non-numeric or non-positive is a 400.

It reports net contributed, not position value — yield accrued between events is not recoverable from transfers alone. Both limits are on the response rather than in a footnote.

GET /v1/yields/portfolio/idle-assets/{address}

Wallet balances not deployed into anything we index.

Request

Field
Type
Required
Description

address

path

Wallet address.

page, perPage

integer

Default 1 / 50, max 200.

allowedNetworks

csv

Slugs or chain ids.

allowedAssets / disallowedAssets

csv

Symbols or addresses.

minUsdValueThreshold

number

Default 1 — below that, gas exceeds the yield.

What the scope actually is. Balances are read against a curated asset universe — every asset our vaults and markets use, plus the chain's native token and a short extras list. It is not every ERC-20 in the wallet.

That is a deliberate trade: an open scan needs a token indexer we do not run on Ethereum or Base, and it surfaces airdropped spam with fabricated prices, which in a "here is what you could be earning" endpoint is actively harmful. The consequence is that a holding outside the universe will not appear. "Idle" is also relative to what we index — a token deployed in a protocol we do not cover reads as idle here.

GET /v1/yields/portfolio/best-vault/{address}

Highest-yielding vault for each idle asset, with projected annual earnings on that balance. Restricted to vaults denominated in the same asset — a recommendation that silently requires a swap would be misleading.

Request: the idle-asset parameters plus:

Field
Type
Required
Description

apyInterval

enum

Window to rank on. Default 1day.

minTvl

number

Only consider vaults above this TVL, USD.

maxVaultsPerAsset

integer

Default 3, max 10. Used by best-deposit-options.

alwaysReturnAssets

boolean

Default false. Keep assets with no matching vault instead of dropping them.

GET /v1/yields/portfolio/best-deposit-options/{address}

Same request and shape, but each entry carries a vaults array of up to maxVaultsPerAsset options instead of a single vault.

projectedUsdAnnualEarnings is the point: an APY alone does not tell a user whether moving $40 is worth the gas.


Transactions

A wallet's on-chain activity against everything we index — the feed a portfolio UI renders as a history tab. The separate /liquidations API is market-scoped: it answers "what was liquidated in this market", not "what happened to me".

Route
Credits
Description

/portfolio/events/{address}

3

Every event against this wallet

/portfolio/events/{address}/{network}/{vaultId}

2

Events for one vault

GET /v1/yields/portfolio/events/{address}

Deposits, withdrawals, borrows, repayments and liquidations, newest first.

Request

Field
Type
Required
Description

address

path

Wallet address.

eventType

string

deposit, withdraw, borrow, repay, liquidation

fromTimestamp / toTimestamp

integer

Unix seconds.

includeClosed

boolean

Default true.

Plus the shared portfolio parameters.

Response

A liquidation carries an extra block:

GET /v1/yields/portfolio/events/{address}/{network}/{vaultId}

The same feed, scoped to one vault. Same parameters, same event shape.


Historical

Every route takes an explicit window and an optional granularity. This replaces the legacy timeRange=1M enum, which cannot express "since I deposited" — the range a portfolio UI actually needs.

Route
Credits
Description

/historical/{network}/{vaultId}

2

TVL, APY, liquidity and implied asset price in one series

/historical/{network}/{vaultId}/apy

2

APY only

/historical/{network}/{vaultId}/tvl

2

TVL only

/historical/{network}/{vaultId}/share-price

3

Assets per share over time

/historical/{network}/{vaultId}/daily-flows

3

Net deposits and withdrawals per day

/historical/asset-prices/{network}/{address}

3

Asset price over time

/historical/borrow/{network}/{marketId}

2

Market supply, borrow, rates, utilization

Shared parameters

Field
Type
Required
Description

fromTimestamp

integer

Start of the window, unix seconds, inclusive.

toTimestamp

integer

End of the window, unix seconds, inclusive.

granularity

enum

1hour | 1day | 1week. Omit for the raw series.

page

integer

Default 1.

perPage

integer

Default 500, max 1000.

Buckets are aligned to the epoch and keep their closing value. These are point-in-time readings, so averaging within a bucket would invent a number that never existed at any instant.

GET /v1/yields/historical/{network}/{vaultId}

404 when we hold no history for that vault.

GET /v1/yields/historical/{network}/{vaultId}/apy

GET /v1/yields/historical/{network}/{vaultId}/tvl

GET /v1/yields/historical/{network}/{vaultId}/share-price

Requires indexed share supply.

On Ethereum and Base it returns an empty series with an explicit unavailableReason and the network in errors.unsupportedNetworks, rather than an empty array that reads like "this vault has no history".

GET /v1/yields/historical/{network}/{vaultId}/daily-flows

Net flow per day, counted from actual transfers.

Counted from transfers rather than differenced from TVL: a TVL delta conflates flows with yield accrual and price movement, so a vault that took no deposits but whose asset appreciated would show a fabricated inflow. Requires an indexer.

GET /v1/yields/historical/asset-prices/{network}/{address}

Two sources, named so you can tell them apart — they have different accuracy:

  • recorded — read from the daily price snapshots. Authoritative, and covers every asset we index including collateral assets no vault is denominated in.

  • implied — derived from a vault's own history, where each point's USD and native totals give the price the pipeline observed. Used for the period before daily recording began, with sourceVault naming the vault it came from.

404 when the asset is neither recorded nor held by any indexed vault.

GET /v1/yields/historical/borrow/{network}/{marketId}


Realtime

Uncached on-chain reads, for when a cache is not good enough — sizing a liquidation, or reconciling against chain state.

Route
Credits
Description

/realtime/{network}/{vaultId}

3

Total assets, total supply and share price from ONE block

/realtime/{network}/{vaultId}/share-price

3

Share price alone

/realtime/{network}/{vaultId}/total-assets

3

Total assets alone

/realtime/{network}/{vaultId}/total-supply

3

Total share supply alone

/realtime/{network}/{vaultId}/underlying-asset-price

3

Asset identity live; price from the cache

GET /v1/yields/realtime/{network}/{vaultId}

Request: network and vaultId path parameters. No query parameters.

Prefer this route if you need more than one value. It is one RPC round-trip instead of several and more importantly the values come from the same block. Four separate calls can straddle a block and produce a share price that never existed. The combined route costs the same as a single sub-resource, so taking the consistent path is never penalised.

sharePrice is null, never 1.0, for a vault with no shares outstanding: 1.0 is a plausible-looking reading and would be indistinguishable from a real one.

The single-value routes

Each reads the same block and returns one slice of the snapshot above.

Route
Response

GET /v1/yields/realtime/{network}/{vaultId}/share-price

{ vaultId, readAt, sharePrice, asset }asset is the symbol here, not the object

GET /v1/yields/realtime/{network}/{vaultId}/total-assets

{ vaultId, readAt, totalAssets: { native, usd } }

GET /v1/yields/realtime/{network}/{vaultId}/total-supply

{ vaultId, readAt, totalSupply }

They cost the same as the combined route, so there is no saving in asking for one value at a time — only the risk of straddling a block.

GET /v1/yields/realtime/{network}/{vaultId}/underlying-asset-price

Only the asset identity is read on-chain. The price comes from the same pricing layer the cached endpoints use, and priceSource says so — a caller reconciling against chain data needs to know which half is live.

Liquidations

Endpoint
Description

GET /liquidations/overview-data?chainId={chainId}&marketId={marketId}

Aggregate summary across all matching positions (counts + USD totals).

GET /liquidations/positions?chainId={chainId}&marketId={marketId}

Paginated list of open borrow positions and their risk of liquidation (LTVs, HFs and more).

GET /liquidations/liquidated-positions?chainId={chainId}&marketId={marketId}

Paginated history of positions that have already been liquidated (most recent first).

Supported protocols, and chains

Protocols

Protocol
Product
Versions
Vaults
Borrow markets
Events
Share-price history
Holders
Positions

Morpho

Vaults + lending markets

V1.1, V2

Chains

Every endpoint takes a chainId query param. Omit it and it defaults to Plume (98866). Unsupported chains return an empty result, not an error. This is a list of Chains and their respective chainId below.

Chain
ChainId

Plume

98866

Flare

14

Citrea

4114

Ethereum

1

Base

8453

Errors

Status
Meaning
What to do

400

Unknown query parameter, wrong type, or a missing required one (e.g. partial-returns without both timestamps)

Fix the request. The message names the offending property.

401

Missing, invalid, disabled or expired API key

Check the x-api-key header.

402

Monthly credit allowance exhausted, on a key with a hard cap

Upgrade the plan or ask support to raise the ceiling. Only keys explicitly opted into a spend ceiling can hit this — the default is that overage is billed.

404

Resource does not exist, or no history for it

Do not retry. Never returned as 200 null.

429

Over your plan's requests/second

Back off for Retry-After seconds.

5xx

Our problem

Retry with backoff; contact support if it persists.

What is not an error

Three things that look like failures and are not:

Unresolvable filter values. ?allowedNetworks=plume,atlantis returns 200 with every Plume result and errors.unsupportedNetworks: ["atlantis"]. Losing nine good networks to one typo would be worse than the warning.

Null values. priceUsd: null, apy["30day"]: null, healthFactor: null and sharePrice: null are all deliberate. They mean "we do not know" or "not applicable".

Last updated