Batch Where Possible
Several Codex endpoints accept multiple inputs in a single request. Use these instead of making separate calls:
| Endpoint | Batching Support |
|---|
getTokenPrices | Up to 25 tokens per request |
onPricesUpdated | Up to 25 tokens per subscription |
filterTokens | Up to 200 results per page |
tokens | Multiple token inputs in one call |
getDetailedPairsStats | Multiple pair inputs in one call |
One getTokenPrices call with 25 tokens = 1 request. Twenty-five individual calls = 25 requests.
Request Only the Fields You Need
GraphQL lets you specify exactly which fields to return. Requesting fewer fields means smaller payloads, faster responses, and less data to parse.
# Instead of requesting everything...
query {
filterTokens(input: { limit: 25 }) {
results {
token { name symbol address networkId decimals createdAt creatorAddress
isScam socialLinks { discord telegram twitter website }
info { circulatingSupply totalSupply } }
priceUSD volume24 liquidity holders marketCap
change1 change4 change12 change24
txnCount1 txnCount4 txnCount12 txnCount24
uniqueBuys1 uniqueBuys4 uniqueBuys12 uniqueBuys24
}
}
}
# ...request only what you'll actually display
query {
filterTokens(input: { limit: 25 }) {
results {
token { name symbol address networkId }
priceUSD volume24 liquidity change24
}
}
}
Use Subscriptions Instead of Polling
If you’re calling a query on a timer to check for updates, switch to a subscription. It’s faster and often more efficient.
| Pattern | Requests Used |
|---|
Polling getTokenPrices every 5 seconds for 1 hour | 720 requests |
Subscribing to onPriceUpdated for 1 hour (assuming ~1 update/sec) | ~3,600 requests |
Subscribing to onPriceUpdated for 1 hour (low-volume token, ~1 update/min) | ~60 requests |
Subscriptions are more efficient for low-to-medium frequency updates. For very high-frequency data (e.g. SOL trade events), subscriptions may actually generate more requests than polling — so consider your token’s volume when choosing.
See Queries vs Subscriptions to find the right approach for your use case.
Proxy Subscriptions Through Your Backend
If multiple users are viewing the same data, don’t create a separate subscription for each user. Instead, subscribe once on your backend and fan out the data to connected clients.
Without proxy: 100 users viewing SOL price = 100 subscriptions
With proxy: 100 users viewing SOL price = 1 subscription
This applies to any shared data: charts, trade feeds, token stats, holder lists.
Cache Data That Doesn’t Change Often
Some data changes rarely and doesn’t need to be fetched on every request:
| Data | How Often It Changes | Cache Duration |
|---|
| Token metadata (name, symbol, decimals) | Rarely | Hours to days |
| Social links, images | Occasionally | Hours |
Network list (getNetworks) | Very rarely | Days |
| Token prices | Constantly | Do not cache |
| Trade events | Constantly | Do not cache |
Cache on your backend and serve from cache to avoid unnecessary API calls.
Use Filters to Reduce Noise
Codex indexes 75M+ tokens across 100+ networks — most of which aren’t useful for most applications. Always apply filters to narrow results:
# Filter for tokens with meaningful activity
query {
filterTokens(
input: {
limit: 25
filters: {
liquidity: { gte: 10000 }
volume24: { gte: 5000 }
txnCount24: { gte: 50 }
}
rankings: { attribute: trendingScore24, direction: DESC }
}
) {
results {
token { name symbol address networkId }
priceUSD volume24 liquidity
}
}
}
Without filters, you’ll get results dominated by inactive, low-liquidity, or spam tokens.
Manage Subscription Lifecycle
Idle subscriptions still consume requests. Implement these patterns to avoid wasting your limit:
- Idle detection — pause subscriptions when users are inactive using hooks like useIdle, and resume when they return
- Page visibility — unsubscribe when the browser tab is hidden, resubscribe when it becomes visible
- Component cleanup — always unsubscribe in cleanup/unmount handlers to prevent orphaned subscriptions
// React example: pause subscription when tab is hidden
const isVisible = // ...determine if this component is in view
useEffect(() => {
const handleVisibility = () => {
if (!isVisible) {
subscription.unsubscribe();
} else {
subscription.resubscribe();
}
};
document.addEventListener("visibilitychange", handleVisibility);
return () => document.removeEventListener("visibilitychange", handleVisibility);
}, [isVisible]);
Use the Right Endpoint for the Job
Some endpoints are more efficient than others depending on what you need:
| Need | Efficient | Less Efficient |
|---|
| Price for 1 token | getTokenPrices | filterTokens (returns much more data) |
| Top pair for a token | listPairsForToken | filterPairs with phrase search |
| Historical OHLCV | getBars / getTokenBars | Reconstructing from getTokenEvents |
| Token discovery | filterTokens with filters + rankings | Fetching all tokens and filtering client-side |
| Multiple token metadata | tokens (batch) | Individual token calls |
Paginate Large Result Sets
For endpoints that return many results, use cursor-based pagination rather than trying to fetch everything at once:
# First page
query {
filterTokens(input: { limit: 50 }) {
results { token { name address } priceUSD }
cursor
}
}
# Next page — pass the cursor from the previous response
query {
filterTokens(input: { limit: 50, cursor: "eyJ..." }) {
results { token { name address } priceUSD }
cursor
}
}
Fetch only the pages you need. Don’t paginate through the entire dataset if you only need the first few pages.