Build a Watchlist
Price up to 100 assets in one request, poll on the rate budget, and render changes correctly.
A watchlist is a set of asset IDs and one request that prices them all. The batch endpoints take up to 100 distinct IDs, so a list of any reasonable size costs one request a minute.
1. Keep a List of Asset IDs
Store asset IDs, not symbols. Resolve a symbol once with GET /v1/assets/map (see Track an asset), or let users pick from the ranked listing:
curl -s "https://api.cantoncap.cc/v1/assets/listings/latest?convert=USD&sort=fdv&limit=100" \
-H "X-API-Key: $CANTONCAP_API_KEY"Each listing row has the asset_id, name, symbol, its rank over the whole listing and the same quotes as the quote endpoints.
2. Price the Whole List in One Request
GET /v1/assets/quotes/latest takes the IDs comma-separated, and up to three conversion currencies. Ask for only the change windows you show with price_change_periods.
curl -s "https://api.cantoncap.cc/v1/assets/quotes/latest?ids=ast_515c3eaa72bd4a8b9403297879d78ef6,ast_2d9c41e0b7a64f1e9c3b5a8d7e6f1c20,ast_7a1f0c9e3d5b4e2a8c6f1b0d9e7a3c54&convert=USD&price_change_periods=24h,7d" \
-H "X-API-Key: $CANTONCAP_API_KEY"400 invalid_request; de-duplicate first, as above. IDs the API cannot answer for (unknown, unpublished, or without a quote in that currency) are left out of data rather than failing the request.3. Render It Correctly
- Format prices from the decimal strings, without converting to floats. See Decimals and precision.
price_changes_pctvalues are percentage points:"2.31"is +2.31%. A null window has no data; show a dash.- Show each row's age from
last_updated_at, and mark rows older than you accept rather than hiding the age. - An asset missing from the response is unpriced right now, not zero.
4. Refresh on a Budget
Prices are recalculated about once a minute. One batch request a minute keeps a watchlist of up to 100 assets current and uses a small part of even the Developer plan's budget. Serve your users from your own cache and refresh it on a timer, so a thousand viewers still cost one request a minute.
let cache = { quotes: new Map(), at: 0 };
async function refresh(ids) {
cache = { quotes: await priceWatchlist(ids), at: Date.now() };
}
await refresh(WATCHLIST);
setInterval(() => refresh(WATCHLIST).catch(console.error), 60_000);
// Your own endpoint answers from the cache, never from the API directly.
export function watchlistHandler() {
return Object.fromEntries(cache.quotes);
}For second-by-second updates, subscribe to asset_quote:<asset_id> for each asset on one WebSocket connection instead of polling. See Streaming.