Findra

HTTP API reference

Same JSON endpoints the web app uses: live registry snapshot (on-chain Pyth feeds), search, chart symbols, and asset metadata. Base path in production is your deployed origin; locally, Vite proxies /api to the Findra oracle (npm run dev:server, default 127.0.0.1:10100).

Maintenance: when you add or change routes or response shapes in server/src/server.mjs and server/src/legacy/legacyUiCompat.mjs, update this page in the same change so the public reference stays accurate.

Conventions

  • Method: GET only for the routes below (unless noted).
  • CORS: responses include Access-Control-Allow-Origin: * where applicable.
  • Aliases: /api/assets/… and /api/priceassets/… are equivalent for snapshot, search, chart-search, feed-assets, and metadata. Prefer /api/assets/… for new integrations.
  • Content-Type: JSON bodies use application/json; charset=utf-8.

GET /health

Liveness and snapshot meta without the full row payload. Useful for probes and dashboards.

Example response

{
  "ok": true,
  "service": "findra-oracle",
  "ws": "/ws",
  "feeds": 2990,
  "rows": 2990,
  "horizons": ["1m", "5m", "10m", "15m", "30m", "1h", "2h", "1D", "1W", "1M"],
  "enrichmentAssetCount": 1200,
  "enrichmentFeedIndex": null,
  "benchmarksPriceDiffsAt": null,
  "benchmarksM1CacheSize": null,
  "iconsMissingCount": null,
  "iconsMissingByCategory": null,
  "categoryRollups": []
}

GET /api/assets

Aliases: GET /snapshot, GET /api/priceassets

Full snapshot in the shape the Markets UI expects: one row per price feed (Solana price account as id), symbol, extra (Pyth metadata, categories, optional CoinGecko, optional https:// icon URLs). Rolling horizon percentages and sparklines may be null until a benchmarks layer is attached. Large response; cache sensibly client-side.

Example response (truncated)

{
  "meta": {
    "feedCount": 420,
    "rowCount": 418,
    "horizons": ["1m", "5m", "10m", "15m", "30m", "1h"],
    "enrichmentVersion": 5,
    "enrichmentAssetCount": 380,
    "enrichmentFeedIndex": 418
  },
  "rows": [
    {
      "id": "0x…feed-id…",
      "symbol": "Crypto.BTC/USD",
      "1m": 0.01,
      "5m": 0.05,
      "price": 67234.5,
      "extra": {
        "findraAssetId": "btc-usd__a1b2c3d4-…",
        "imageUrl": "/cdn/assets/btc__00000000-0000-4000-8000-000000000001/findra/large.png",
        "category": "crypto",
        "categoryLabel": "Crypto",
        "pyth": { "description": "…", "base": "BTC" },
        "coinGecko": { "id": "bitcoin", "name": "Bitcoin", "marketCapRank": 1 },
        "sparkline": [67200, 67210, …]
      }
    }
  ]
}

GET /api/assets/metadata

Alias: GET /api/priceassets/metadata

One of findraAssetId, feedId, or instrument (canonical chart id) is required. Returns display name, description, optional https:// image URL, and a small CoinGecko subset when present.

Example: /api/assets/metadata?instrument=PYTH_SPOT_CRYPTO.BTC/USD_USD

Example response

{
  "findraAssetId": "btc-usd__…",
  "feedId": "0x…",
  "feedIds": ["0x…"],
  "symbol": "Crypto.BTC/USD",
  "instrument": "PYTH_SPOT_CRYPTO.BTC/USD_USD",
  "displayName": "Bitcoin",
  "description": "Bitcoin / US Dollar",
  "imageUrl": "/cdn/assets/btc__00000000-0000-4000-8000-000000000001/findra/large.png",
  "tickerSlug": "bitcoin",
  "coinGecko": {
    "id": "bitcoin",
    "name": "Bitcoin"
  }
}

404 JSON: { "error": "Not found" } · 400 if no lookup key provided.

GET /api/assets/feed-assets

Aliases: /api/search/feed-assets, /api/priceassets/feed-assets

Full feed list with search keywords for client-side icon and label lookup. Cached short (e.g. max-age 30).

Example response (truncated)

{
  "meta": { "rowCount": 418, "horizons": ["1m", "5m", …], "feedAssetCard": "v1" },
  "items": [
    {
      "id": "0x…",
      "symbol": "Crypto.BTC/USD",
      "instrument": "PYTH_SPOT_CRYPTO.BTC/USD_USD",
      "displayName": "Bitcoin",
      "imageUrl": "/cdn/assets/btc__00000000-0000-4000-8000-000000000001/findra/large.png",
      "searchKeywords": "crypto btc usd bitcoin …"
    }
  ]
}

GET /cdn/…

The oracle serves static icons at GET /cdn/… from server/data/cdn/. Snapshots prefer those URLs when files exist; otherwise responses may still include https:// until cached. Seed the tree with npm run cdn:import (see _docs/README.md).

WebSocket /ws

On connect, the server sends one full snapshot JSON (same shape as GET /api/assets). Broadcasts repeat on an interval (configurable server-side). Use for live Markets and Signals UIs.

About Findra

Extra data layer

Branding, stated honestly: we describe Findra as an extra data layer on market feeds—meaning we merge oracle prices with curated metadata and tooling in one place. That is not the same as launching a new chain or token; it is the stack we ship today.

If we add on-chain or protocol-level claims later, we will say so explicitly here. For now: browser product + merge layer + clear attribution on data sources.