Developers

Data API

A public, read-only HTTP API that serves pre-assembled vault, gauge, token, and user-position data — every value the UI shows, computed server-side from the same sources and math, then cached.

  • Base path: /api/v1
  • Network: BNB Smart Chain (chainId 56)
  • Auth: none; open CORS, best-effort rate limiting
  • Methods: GET (+ OPTIONS preflight). No writes.

It exists so clients can read accurate vault data from one place instead of fanning out to RPC, the Stats API, and the subgraph and re-deriving everything. Add a vault to config and it appears here automatically.

Response envelope#

jsonc
{
  "ok": true,
  "data": { /* payload */ },
  "meta": {
    "chainId": 56,
    "network": "bsc",
    "apiVersion": "v1",
    "generatedAt": "2026-06-22T20:48:39.985Z",
    "indexedBlock": 105784153,   // subgraph head when subgraph data was used
    "degraded": false,           // true if any upstream came back partial
    "conventions": { /* ... */ }
  }
}

// Errors:
{ "ok": false, "error": { "code": "not_found", "message": "..." } }

Error codes map to HTTP status: 400 invalid param/address, 404 not found, 429 rate limited, 502 upstream error.

Number conventions#

  • Raw on-chain integers (wei, share base units, 1e36 prices, NFT ids) are decimal strings — no JSON precision loss.
  • Scaled values (human token amounts, USD, percentages) are numbers, or null when an input (e.g. a price) is unavailable.
  • Addresses are EIP-55 checksummed.

Degradation, not fake zeros

meta.degraded is true whenever an upstream came back partial. When degraded, treat null scaled values as unknown, not zero. For portfolios, totals.usd / totalPnlUsd become null (with usdComplete / pnlCompleteflags) if any position's value is unknown — so a missing price can never fake a $0 portfolio.

Endpoints#

EndpointReturns
GET /api/v1/metaProtocol root: chainId, all shared contract addresses, protocol tokens, vault counts, global pause, total TVL.
GET /api/v1/healthPer-source status: { onchain, stats, subgraph } + indexedBlock.
GET /api/v1/vaultsAll vaults assembled. ?kind=cl|v2, ?include=integration[,abi].
GET /api/v1/vaults/{keyOrAddress}A single vault by config key or address; always includes the manifest. ?include=abi.
GET /api/v1/vaults/{keyOrAddress}/historyTime-series for charts. ?period=hourly|daily, ?days=N (1–365).
GET /api/v1/vaults/healthPer-vault rebalance/position health for keeper bots (CL only). ?actionable=true.
GET /api/v1/gaugesEvery gauge + APR breakdown, mapped to the Topaz vault that wraps it. ?alive=true.
GET /api/v1/tokensTokens across the vaults with symbol, decimals, logo, USD price.
GET /api/v1/users/{address}/positionsA wallet's active positions across all vaults + totals + P&L.
GET /api/v1/users/{address}/vaults/{keyOrAddress}One user's position in one vault, incl. interaction history.

Integration manifest#

Returned per vault (single-vault endpoint, or list with ?include=integration). Everything needed to integrate a deposit/withdraw without reading our frontend — you bring the transaction flow:

jsonc
{
  "kind": "cl",                       // or "v2"
  "contracts": { "vault","strategy","pool","gauge","zapper","positionManager","factory" },
  "tokens": { "token0","token1","base","shares" },
  "state": { "isCalm","globalPause","strategyPaused","depositsAvailable","slippageBps" },
  "deposit": {
    "direct": { "approvals":[{token,spender,symbol}], "methods":[...], "preview","gating","slippage" },
    "zap":    { "approvals":[...], "methods":["zapInToken","zapInBoth","zapInBNB"],
                "quote":"simulate via eth_call (callStatic) — do NOT estimate naively",
                "gating":"isCalm() must be true", "slippage","gas":"estimateGas*2 (>=3M)" }
  },
  "withdraw": { "approvals":[...], "methods":[...], "gating":"none (not gated)", "slippage" },
  "abiSignatures": { "vault":[...], "zapper":[...], "erc20":[...] },
  "abi": { /* full JSON ABIs, only when ?include=abi */ },
  "notes": [ /* ... */ ]
}

Build a CL deposit from the manifest#

  1. Read contracts.vault and tokens.token0/token1.
  2. Check state.depositsAvailable (requires isCalm true, not paused/globalPaused).
  3. approve(token0 → vault) and approve(token1 → vault).
  4. Quote with vault.previewDeposit(amount0, amount1)shares.
  5. minShares = shares × (10000 − state.slippageBps) / 10000.
  6. vault.deposit(amount0, amount1, minShares).

(For zaps, static-call the zapper method to get exact shares, then guard below it and budget gasLimit = estimateGas × 2 — see Smart contracts.)

Examples#

bash
# List every vault with full state + metrics
curl https://vaults.topazdex.com/api/v1/vaults

# One vault + integration manifest + full ABIs (by key or address)
curl "https://vaults.topazdex.com/api/v1/vaults/sol-wbnb-mid?include=integration,abi"

# Every gauge mapped back to the Topaz vault + zapper that wraps it
curl "https://vaults.topazdex.com/api/v1/gauges?alive=true"

# A wallet's positions across all vaults (live shares + P&L + totals)
curl https://vaults.topazdex.com/api/v1/users/0xYourWallet/positions

# Source health
curl https://vaults.topazdex.com/api/v1/health

Caching#

Each resource sets Cache-Control: public, s-maxage, stale-while-revalidate and is memoized server-side: vaults 15s, gauges/tokens 60s, history 60s, user positions 10s, health 10s. Public deployments should still enforce real rate limits at the edge/CDN.

Keep reading#

Smart contractsSubgraphDeveloper overview