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:
GETonly 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/search
Aliases: /api/search, /api/priceassets/search
Query: q (string, optional), limit (default 25, max 80).
Example response
{
"q": "sol",
"limit": 25,
"hits": [
{
"id": "0x…",
"symbol": "Crypto.SOL/USD",
"instrument": "PYTH_SPOT_CRYPTO.SOL/USD_USD",
"name": "Solana",
"displayName": "Solana",
"findraAssetId": "sol-usd__…",
"tickerSlug": "solana",
"imageUrl": "/cdn/assets/sol__00000000-0000-4000-8000-000000000002/findra/large.png",
"category": "crypto",
"categoryLabel": "Crypto",
"tokenUrl": "/token?feed=0x…&instrument=PYTH_SPOT_…"
}
]
}
GET /api/assets/chart-search
Aliases: /api/search/chart, /api/priceassets/chart-search
TradingView-style symbol rows for OpenCharts: ticker, description, optional imageUrl (https:// or /cdn/), instrument, feedId.
Query: q, limit (default 50).
Example response (array)
[
{
"ticker": "Crypto.BTC/USD",
"symbol": "Crypto.BTC/USD",
"full_name": "Crypto.BTC/USD",
"description": "Bitcoin / US Dollar",
"exchange": "PYTH",
"instrument": "PYTH_SPOT_CRYPTO.BTC/USD_USD",
"feedId": "0x…",
"imageUrl": "/cdn/assets/btc__00000000-0000-4000-8000-000000000001/findra/large.png"
}
]
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.