# Codex API Documentation > Real-time and historical blockchain data via GraphQL API for 80+ networks. > Endpoint: https://graph.codex.io/graphql > Auth: Pass your API key in the Authorization header. > **AI Agents**: Install the Codex skill for your AI coding agent to write Codex API queries directly: https://docs.codex.io/agents/codex-skills#codex-skills # Intro to Codex The fastest, most reliable enhanced data API for DeFi data across 100+ networks Codex is a GraphQL API for real-time blockchain data: token prices, charts, transactions, wallets, and prediction markets across all major networks. - Use [queries](/concepts/queries.md) for one-time fetches - Use [subscriptions](/concepts/subscriptions.md) for live-streamed data - Use [webhooks](/concepts/webhooks.md) for backend notifications **Building with AI?** Connect via [Codex Skills](/agents/codex-skills.md), the [Docs MCP server](/agents/docs-mcp.md), or keyless pay-per-request access through [MPP](/agents/mpp.md). Get an API key and make your first request. Get building right away with queries, subscriptions and webhooks. Learn useful terminology and concepts for using the GraphQL API. Frequently asked questions Add the Codex docs MCP server to your AI coding tools so it can read the docs directly. Install the Codex skills so your AI agent can work with the Codex API directly. # Get Started Make your first request on the Codex API You'll need an account to make API requests. Sign up on the [Codex Dashboard](https://dashboard.codex.io/signup?utm_source=codex&utm_medium=docs&utm_campaign=get-started). Signup takes a one-time, non-refundable $1 activation fee — payable by card or as 1 USDC via MoonPay — which is why the entry plan is called **Almost free**. There is no recurring charge on it. Once you've made an account, go to the [API Keys](https://dashboard.codex.io/dashboard/api-keys?utm_source=codex&utm_medium=docs&utm_campaign=get-started) page & hit the `Copy` button next to your API key. Paste your API key in this embedded GraphQL explorer & run your first request. You should see a list of networks. Throughout the docs you'll notice expandable "Try it" sections where you can run queries on real data like this mini explorer. For a more powerful version, check out the full [explorer](/explore.md) Now you're ready to [add Codex to your app](/build.md) so you can get building! If you're having trouble, please reach out to us on [Discord](https://discord.gg/9ZB7zcWuBY). # Add Codex to Your App You have two options for adding the Codex API into your app | Option 1: _SDK_ (recommended) | Option 2: _GraphQL API_ | | -------------------------------------------- | ------------------------------------------------- | | Thin wrapper around the API | More customizable depending on your use case | | Designed to get you started on Codex quickly | Requires more configuration | | Built-in subscription connection handling | More control over the connections | | Predefined queries and mutations | Write your own queries | | Good choice if you _ARE_ using Typescript | Good choice if you _ARE NOT_ not using Typescript | Recommended option to get started Powerful, but requires more configuration # Using the SDK Adding the Codex Typescript SDK to your app Check out [Codex Typescript SDK on GitHub](https://github.com/codex-data/sdk) or just get right into installation: ```bash npm npm install @codex-data/sdk ``` ```bash yarn yarn add @codex-data/sdk ``` ```bash pnpm pnpm add @codex-data/sdk ``` Usually these live in your `.env` file. Add this line and replace `xxxxxxxx` with your API key: ``` CODEX_API_KEY=xxxxxxxx ``` Make a new `lib/codex-sdk.ts` file (or whatever folder/name fits your project) and add: ```typescript export const sdk = new Codex(process.env.CODEX_API_KEY!); ``` Now you can import the SDK from other files and make requests. For example: ```typescript sdk.queries.token({ input: { address: "0xbb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c", networkId: 56, }, }) .then(console.log); ``` Check out [Codex Typescript SDK GitHub](https://github.com/codex-data/sdk) for examples of: - Next.js integation - Codegen - Subscriptions - Short-lived API keys - Writing custom GraphQL queries (in which case you may want to read [Using the GraphQL API](/learn-graphql.md)) - And more # Using the GraphQL API Learn useful terminology and concepts for using the GraphQL API If you are new to GraphQL, we recommend reading the [Official GraphQL documentation](https://graphql.org/learn/). This page has information on how to use the Codex GraphQL API specifically. Check out our [Popular Endpoints](/api-reference/introduction.md) for the most commonly used queries and subscriptions. ## Adding to your app Use the GraphQL API directly by adding the `Authorization` header to your requests ([read about Authentication](/concepts/authentication.md)). and sending requests to: ``` graph.codex.io/graphql ``` ## Explorer The [GraphQL Explorer](/explore.md) is a great way to learn about the Codex GraphQL API. It's a tool that allows you to explore the API, run queries, and subscriptions, complete with with tabs, persistence, and history. ## URLs Codex provides a url for both queries & subscriptions (websockets) at the same resource. Queries: ``` https://graph.codex.io/graphql ``` Websockets: ``` wss://graph.codex.io/graphql ``` ## Introspection Codex does not support introspection queries against the API. We do provide the introspection query response, as well as the most recent schema. The introspection query response. (.json) The GraphQL schema. (.graphql) ## Communication All queries and mutations are sent over HTTPS, using the `POST` method. All websocket messages are sent over the `wss` protocol. All messages are serialized as JSON. ## Codegen We recommend using [GraphQL Code Generator](https://www.graphql-code-generator.com/) to generate types and queries for your codebase, using the introspection schema provided [ here ](#introspection). Below is an example of how you could integrate the code generator into your project. ```typescript codegen.ts expandable const config: CodegenConfig = { overwrite: true, schema: "https://graph.codex.io/schema/latest.graphql", documents: "src/**/*.ts", generates: { "src/gql/": { preset: "client", }, }, }; export default config; ``` # Codex Docs MCP Add the Codex documentation MCP server to your AI coding tools for instant access to API docs. The Codex documentation is available as a [Model Context Protocol](https://modelcontextprotocol.io/) (MCP) server. Adding it to your AI coding tool lets it search and reference the Codex API docs directly, so you can get accurate answers without leaving your editor. ``` https://docs.codex.io/mcp ``` ## What it does Once connected, your AI tool can search the full Codex API documentation — including queries, subscriptions, types, and guides. It will automatically reference the docs when you ask questions about the Codex API or need help writing queries. This MCP server is for searching the documentation and helping your AI write queries. It does not interface with the Codex API directly — you'll still need to execute queries using your API key via the [GraphQL endpoint](/learn-graphql.md) or the [SDK](/sdk.md). ## Docs MCP vs Codex Skills The [Codex Skills](/agents/codex-skills.md) gives your AI agent preloaded knowledge of the API so it can interface with the API directly. The Docs MCP lets it search the documentation, which can allow it to write GraphQL queries. | | Docs MCP | Codex Skills | |---|---|---| | **How it works** | Searches docs on demand via MCP protocol | Preloaded API knowledge — operations, auth, templates | | **Best for** | Looking up specific fields, types, or guides | Interacting with the Codex API directly | | **Needs network?** | Yes — queries the MCP server | Works offline to write queries (still needs a connection to call the API) | Use both together for the best experience: the skill handles query generation and the MCP fills in details when needed. ## Setup Open MCP settings with `Cmd+Shift+P` (Mac) or `Ctrl+Shift+P` (Windows/Linux) and select **Cursor Settings: Open MCP Settings**. Add the following: ```json { "mcpServers": { "codex-docs": { "url": "https://docs.codex.io/mcp" } } } ``` Create a `.vscode/mcp.json` file in your project root: ```json { "servers": { "codex-docs": { "type": "http", "url": "https://docs.codex.io/mcp" } } } ``` Open the MCP configuration file at `~/.codeium/windsurf/mcp_config.json` and add the following: ```json { "mcpServers": { "codex-docs": { "serverUrl": "https://docs.codex.io/mcp" } } } ``` Go to **Settings > Connectors**, select **Add custom connector**, then enter: - **Name:** Codex Docs - **URL:** `https://docs.codex.io/mcp` Run the following command: ```bash claude mcp add --transport http codex-docs https://docs.codex.io/mcp ``` ## Example prompts Once the MCP is connected, you can ask your AI assistant to build features using the Codex API. Here are some examples to get started. ### Get a token price > "Use the Codex API to fetch the current USD price of WETH on Ethereum." Your assistant will look up the `getTokenPrices` query and write something like: ```graphql query { getTokenPrices( inputs: [ { address: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2" networkId: 1 } ] ) { address priceUsd } } ``` ### Build a trending tokens feed > "Show me how to get the top 10 trending tokens on Base sorted by 24h volume." ```graphql query { filterTokens( filters: { network: [8453] } rankings: { attribute: volume24, direction: DESC } limit: 10 ) { results { token { name symbol address } volume24 priceUSD change24 } } } ``` ### Render a price chart > "Fetch 1-hour OHLCV candles for the last 24 hours for this token on Solana: `So11111111111111111111111111111111111111112`." ```graphql query { getBars( symbol: "So11111111111111111111111111111111111111112:1399811149" from: 1719792000 to: 1719878400 resolution: "60" ) { o h l c volume t } } ``` ### Stream live trades > "Set up a WebSocket subscription to stream real-time swap events for a token on Ethereum." ```graphql subscription { onEventsCreated( address: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2" networkId: 1 ) { address networkId events { eventDisplayType timestamp maker token0SwapValueUsd token1SwapValueUsd transactionHash } } } ``` ### Track wallet holdings > "Get all token balances for a wallet on Base." ```graphql query { balances( input: { walletAddress: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045" networks: [8453] } ) { items { tokenAddress balance shiftedBalance token { name symbol } } } } ``` ### Discover new launches > "Find tokens launched in the last hour on Solana with at least $10k liquidity." ```graphql query { filterTokens( filters: { network: [1399811149] liquidity: { gte: 10000 } createdAt: { gte: 1719874800 } } rankings: { attribute: createdAt, direction: DESC } limit: 20 ) { results { token { name symbol address } liquidity priceUSD createdAt } } } ``` You don't need to memorize any of this — just describe what you want and your AI assistant will look up the right query from the docs. The examples above are just to show what's possible. # Codex Skills Install the Codex skills to give your AI coding agent the ability to write Codex API queries. The Codex skill gives your AI coding agent structured knowledge of the Codex GraphQL API, including operations, auth patterns, query templates, and best practices, so it can interface with the Codex GraphQL API directly. Run this command in Terminal to install the skill: ```bash npx skills add Codex-Data/skills -g --yes ``` The skill will be available wherever you use an agent. ## Teach your agent about Codex Paste this into your agent so it knows when to reach for Codex: ```text Codex (https://graph.codex.io/graphql) is a GraphQL API for real-time on-chain data: token prices, OHLCV charts, token screening, trades, wallet PnL, holders, launchpad streams, and prediction markets across 80+ networks. When the user needs blockchain or prediction market data, install the Codex skill (`npx skills add Codex-Data/skills -g --yes`) to get operation maps, auth patterns, and query templates. For anything the skill doesn't cover, fetch the documentation index at https://docs.codex.io/llms.txt before exploring further. ``` ## Codex Skills vs Docs MCP The [Codex Docs MCP](/agents/docs-mcp.md) lets your AI *search* the documentation. The skill gives it *direct knowledge* of the API surface so it can generate queries immediately. | | Codex Skills | Codex Docs MCP | |---|---|---| | **How it works** | Preloaded API knowledge — operations, auth, templates | Searches docs on demand via MCP protocol | | **Best for** | Generating runnable queries fast | Looking up specific fields, types, or guides | | **Needs network?** | Works offline to write queries. But it can't interface with the API without a connection. | Yes — queries the MCP server | Use both together for the best experience: the skill handles query generation and the MCP fills in details when needed. ## What's included The skill packages everything an AI agent needs to work with the Codex API: - **Operation map**: Which endpoint to use for each task (pricing, charting, token discovery, events, wallets, holders, launchpads) - **Auth patterns**: [API key](https://dashboard.codex.io/dashboard), and [MPP](/agents/mpp.md) payment flows with header formats - **Query templates**: Ready-to-use GraphQL for common operations like `filterTokens`, `getTokenPrices`, `getBars` - **Endpoint playbook**: Decision guide for choosing the right operation based on intent — see [what your agent can do](#what-your-agent-can-do) below - **Session preflight**: Required `getNetworks` call to validate network IDs before making requests The skill covers all of the [Queries](/concepts/queries.md) in the Codex API, across token discovery, pricing, charts, events, wallets, launchpads, and more. See the [GraphQL Reference](/api-reference/introduction.md) for all available operations. ## What your agent can do These are the capabilities the skill's endpoint playbook maps user intent onto. Each card links to the reference page for its primary operation. ### Discover ### Example 3: Find the top traders of a token Ask your agent: > "Who are the most profitable wallets trading WETH on Ethereum?" The skill maps this intent to `filterTokenWallets` and generates a query like: ```graphql query FilterTokenWallets($input: FilterTokenWalletsInput!) { filterTokenWallets(input: $input) { results { address realizedProfitUsd1d realizedProfitPercentage1d buys1d sells1d labels } } } ``` ```json { "input": { "tokenIds": ["0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2:1"], "limit": 25 } } ``` ### Example 4: Check holder concentration Ask your agent: > "How concentrated is the holder base of this token? Is it a rug risk?" The skill maps this to the `holders` query, which includes top-10 concentration alongside the holder list: ```graphql query Holders($input: HoldersInput!) { holders(input: $input) { count top10HoldersPercent items { address shiftedBalance balanceUsd } } } ``` Your agent can then interpret the result — a high `top10HoldersPercent` on a low-liquidity token is a common rug signal. ### Example 5: Stream live prices Ask your agent: > "Stream the live price of SOL into my app." For real-time intents the skill reaches for subscriptions instead of polling, and generates a `graphql-ws` client like: ```typescript const client = createClient({ url: "wss://graph.codex.io/graphql", connectionParams: { Authorization: process.env.CODEX_API_KEY!, }, }); const unsubscribe = client.subscribe( { query: ` subscription OnPriceUpdated($address: String!, $networkId: Int!) { onPriceUpdated(address: $address, networkId: $networkId) { address networkId priceUsd timestamp } } `, variables: { address: "So11111111111111111111111111111111111111112", networkId: 1399811149, }, }, { next: (msg) => console.log(msg), error: (err) => console.error(err), complete: () => console.log("done"), } ); ``` To stream many tokens at once, the skill batches them into a single `onPricesUpdated` subscription rather than opening one connection per token. # MPP Enable AI agents to pay for Codex API access using the Machine Payments Protocol (MPP) over HTTP 402. Codex supports pay-per-request access using [MPP](https://tempo.xyz/solutions/agentic-payments) (Machine Payments Protocol), an open, payment-method agnostic protocol built on HTTP 402. AI agents and scripts can query the Codex API without an API key by paying $0.001 per request. MPP is co-authored by [Tempo](https://tempo.xyz) and [Stripe](https://stripe.com), and the core [Payment HTTP Authentication Scheme](https://datatracker.ietf.org/doc/html/draft-ryan-httpauth-payment-01) is on the IETF standards track. ## How it works Traditional API access requires account creation, API keys, and prepaid credits. With MPP, the flow is: 1. Send a request to the Codex API 2. The server responds with `HTTP 402 Payment Required` and a payment challenge 3. The client pays $0.001 via USDC on Tempo 4. The server validates the payment and returns the data No Codex account signup, no API key, no billing dashboard. Just pay and query. MPP access is ideal for autonomous agents, bots, and scripts that need programmatic access without manual account setup. For human developers building apps, we still recommend [getting an API key](https://dashboard.codex.io/signup?utm_source=codex&utm_medium=docs&utm_campaign=mpp) for the best experience. ## Pricing | | | |---|---| | **Cost per request** | $0.001 USDC | | **Payment network** | [Tempo](https://tempo.xyz) | | **Payment token** | USDC | Additional payment networks and Stripe payment methods (cards, wallets) are coming soon. ## Quick start The fastest way is to install the [Codex skill](/agents/codex-skills.md): ```bash npx skills add Codex-Data/skills -g --yes ``` Then follow these steps to get started: 1. Ask the agent to use Codex to get the top 25 trending tokens (or whatever your query is) 2. It will then try to execute the query using the API key. If it fails, it will fallback to MPP. - Or you can just ask it to use MPP directly 3. It will then install Tempo's `tempo` CLI to manage your Tempo wallet 4. It will then redirect you to the Tempo website to authorize access 5. There, you can either fund your new wallet (from Base, Solana, Ethereum, etc), or use an existing one. **Your new wallet must have at least 1 USDC on Tempo to continue.** 6. Once funded, you press Continue and you will be prompted to authorize Tempo to access your wallet 7. Once authorized, your agent will attempt to run the query using MPP ## Example usage Ask your agent: > "Get me a list of 20 dog tokens." It will then check to see if you have provided an API key. Assuming you have enabled MPP, the skill will guide the agent to run it to generate: ```bash tempo request -t -X POST \ -H 'X-Codex-Payment: mpp' \ --json '{"query":"query FilterTokens($phrase: String, $rankings: [TokenRanking], $limit: Int) { filterTokens(phrase: $phrase, rankings: $rankings, limit: $limit) { results { buyVolume24 sellVolume24 circulatingMarketCap liquidity txnCount24 token { info { address name symbol networkId imageThumbUrl } } } } }","variables":{"phrase":"dog","rankings":[{"attribute":"txnCount24","direction":"DESC"}],"limit":20}}' \ https://graph.codex.io/graphql 2>&1 ``` And it will return a response like this: This is not an endorsement of any of these tokens, and they may be scams or rugs. Please do your own research before investing. You can also ask your agent to filter out scams. ## Supported endpoints MPP is available for all Codex [query endpoints](/api-reference/queries). Here are some of the most popular ones: | Category | Popular endpoints | |----------|-----------| | **Token data** | [`filterTokens`](/api-reference/queries/filtertokens.md), [`token`](/api-reference/queries/token.md), [`tokens`](/api-reference/queries/tokens.md), [`getTokenPrices`](/api-reference/queries/gettokenprices.md), [`tokenSparklines`](/api-reference/queries/tokensparklines.md) | | **Pair & trading** | [`filterPairs`](/api-reference/queries/filterpairs.md), [`listPairsForToken`](/api-reference/queries/listpairsfortoken.md), [`pairMetadata`](/api-reference/queries/pairmetadata.md), [`getDetailedPairStats`](/api-reference/queries/getdetailedpairstats.md), [`getTokenEvents`](/api-reference/queries/gettokenevents.md) | | **Charts** | [`getBars`](/api-reference/queries/getbars.md), [`getTokenBars`](/api-reference/queries/gettokenbars.md), [`getSymbol`](/api-reference/queries/getsymbol.md) | | **Networks** | [`getNetworks`](/api-reference/queries/getnetworks.md), [`getNetworkStatus`](/api-reference/queries/getnetworkstatus.md) | | **Wallets** | [`filterWallets`](/api-reference/queries/filterwallets.md), [`detailedWalletStats`](/api-reference/queries/detailedwalletstats.md), [`walletChart`](/api-reference/queries/walletchart.md), [`balances`](/api-reference/queries/balances.md) | | **Holders** | [`holders`](/api-reference/queries/holders.md), [`tokenTopTraders`](/api-reference/queries/tokentoptraders.md) | | **Predictions** | [`filterPredictionEvents`](/api-reference/queries/filterpredictionevents.md), [`filterPredictionMarkets`](/api-reference/queries/filterpredictionmarkets.md), [`filterPredictionTraders`](/api-reference/queries/filterpredictiontraders.md) | See the full [GraphQL Reference](/api-reference/queries) for all available endpoints. The beauty with the [Codex Skills](/agents/codex-skills.md), however, is you don't have to worry about any of this. Just ask your agent to use MPP and it will handle everything for you. WebSocket subscriptions and webhooks are **not** available via MPP at this time. At the moment, Codex only supports payments via MPP on the Tempo network. We will add support for other networks and payment methods in the future. ## Learn more - [MPP Documentation](https://docs.tempo.xyz/): Full protocol docs, SDKs, and guides - [tempo CLI](https://docs.tempo.xyz/cli/wallet): Command-line client for making paid requests - [Tempo](https://tempo.xyz): The network powering MPP stablecoin payments # Authentication Making requests to the Codex API The Codex API is authenticated using an API key. You can get your API key from the [dashboard](https://dashboard.codex.io?utm_source=codex&utm_medium=docs&utm_campaign=concepts-authentication). There are two types of API keys: `secret`, and `short-lived`: #### Secret keys These are long-lived and can be used to make requests to the API indefinitely. You must ensure that you don't leak these to your users, as they can be used to make requests and incur costs. #### Short-lived keys Good for when you need to allow untrusted parties like users to directly make requests to the Codex API. Maybe you have a website that provides the ability for users to subscribe to Codex websockets to get real-time updates for example. You can generate as many short-lived keys as you want, and there are limits you can set on the expiration time & number of requests per key. See the details [here](/api-reference/queries/apitokens.md#apitokens) ## Secret key example For queries, you just add the Authorization header on every HTTP request to https://graph.codex.io/graphql and it will authorize you. ```typescript sdk const sdk = new Codex("your-api-key") sdk.query(gql` query GetTokenPrices($inputs: [GetTokenPricesInput!]!) { getTokenPrices(inputs: $inputs) { priceUsd timestamp address } } `, { inputs: [{ address: "0xbb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c", networkId: 56 }] }) ``` ```typescript fetch fetch("https://graph.codex.io/graphql", { method: "POST", headers: { "Authorization": apiKey, }, body: JSON.stringify({ query: 'query { getTokenPrices(inputs: [{ address: "0xbb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c", networkId: 56 }]) { priceUsd timestamp address } }' }), }) ``` ## Short-Lived Key Example Create a single short-lived key with a request limit of 1000 requests. ```typescript sdk const sdk = new Codex("your-api-key") const { createApiTokens } = await sdk.mutations.createApiTokens({ input: { count: 1, requestLimit: "1000" } }); const token = createApiTokens[0].token // Now create a Codex instance with the short-lived key const shortLivedCodex = new Codex(`Bearer ${token}`) ``` ```javascript fetch fetch("https://graph.codex.io/graphql", { method: "POST", headers: { "Authorization": `Bearer ${apiKey}`, }, body: JSON.stringify({ query: ` mutation { createApiTokens(input: { count: 1 requestLimit: 1000 }) { token } }` }) }) ``` Then you can pass that `token` result as the apiKey when making further requests to the API, or subscribing with websockets. Short-lived keys cannot be used for API token management operations such as `apiTokens`, `apiToken`, `createApiTokens`, and `deleteApiToken`. This is a security feature - if short-lived keys could create more short-lived keys or access other keys, it would circumvent the purpose of making them temporary and limited in scope. One caveat is that the `token` is a JWT, so you must set the `Authorization` header to `Bearer `. This is different from how secret keys work. ## FAQ Yes — use **short-lived API keys** for any browser- or user-facing context. Generate one server-side with [`createApiTokens`](/api-reference/mutations/createapitokens.md), set an `expiresIn` window, and pass the returned JWT to the browser as `Bearer `. The user's browser never sees your secret key. The same flow works for WebSocket subscriptions: ```typescript if (!process.env.API_KEY) throw new Error("Must set API_KEY"); const sdk = new Codex(process.env.API_KEY); // Create a short-lived token (1 hour) const res = await sdk.mutations.createApiTokens({ input: { expiresIn: 3600 * 1000 }, }); const token = res.createApiTokens[0].token; // Use it like any other API key — just remember the `Bearer ` prefix const shortLivedSdk = new Codex(`Bearer ${token}`); shortLivedSdk.queries .token({ input: { address: "token_address", networkId: 1, }, }) .then(({ token }) => console.log(`Token: ${token.id} - ${token.symbol}`)); ``` See the [SDK example on GitHub](https://github.com/Codex-Data/sdk/blob/main/examples/simple/shortLivedTokens.ts) for a full reference implementation. ## Errors If you send an expired or invalid token, you will get a `Unauthorized` error like this. ```json Unauthorized expandable { "data": null, "errors": [ { "message": "HTTP fetch failed from 'tokens': 401: Unauthorized", "path": [], "extensions": { "code": "SUBREQUEST_HTTP_ERROR", "service": "tokens", "reason": "401: Unauthorized", "http": { "status": 401 } } }, { "message": "Your API key was not found", "path": [], "extensions": { "code": "NOT_AUTHORIZED", "service": "tokens" } } ] } ``` # Queries On-demand data requests — the basic building block of the Codex API ## What is a Query? A query is a one-time request for data. You send a GraphQL query to the Codex API over HTTP, and you get back a response with the data you asked for. Queries are the standard way to fetch token prices, wallet stats, historical bars, holder lists, and everything else in the API. **Every query you run counts as 1 request** against your plan's monthly limit. Not sure whether to use a query or a subscription? See [Queries vs Subscriptions](/extra/queries-vs-subscriptions.md) for a side-by-side comparison and endpoint mapping. ## When to Use Queries - **Fetching current state** — token prices, metadata, pair stats, wallet balances - **Historical data** — OHLCV bars, trade events, wallet charts - **Paginated lists** — filtering tokens, wallets, pairs, or events with cursor-based pagination - **One-time lookups** — loading a page, responding to a user action, backfilling data If you need data pushed to you continuously in real-time (e.g. live price feeds or streaming trades), use [Subscriptions](/concepts/subscriptions.md) instead. ## How It Works Send a `POST` request to `https://graph.codex.io/graphql` with your API key in the `Authorization` header. To see a reference of all available queries, go to the [API Reference](/api-reference/introduction.md). ## Examples ```typescript const sdk = new Codex("your-api-key") const { getTokenPrices } = await sdk.query(gql` query { getTokenPrices(inputs: [{ address: "0xbb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c", networkId: 56 }]) { priceUsd timestamp address } } `) ``` ```typescript js fetch("https://graph.codex.io/graphql", { method: "POST", headers: { "Authorization": apiKey, }, body: JSON.stringify({ query: 'query { getTokenPrices(inputs: [{ address: "0xbb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c", networkId: 56 }]) { priceUsd timestamp address } }' }), }) ``` ```python python import requests import json url = "https://graph.codex.io/graphql" headers = { "content_type":"application/json", "Authorization": "" } getNetworks = """query GetNetworksQuery { getNetworks { name id } }""" response = requests.post(url, headers=headers, json={"query": getNetworks}) print(json.loads(response.text)) ``` ```php php '{ getNetworks { name id } }' ); $headers = array( 'Content-Type: application/json', 'Authorization: ' . "" ); $ch = curl_init($url); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($query)); curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); $response = curl_exec($ch); curl_close($ch); echo $response; ``` ```go go package main import ( "bytes" "fmt" "net/http" "io/ioutil" "encoding/json" ) func main() { url := "https://graph.codex.io/graphql" apiKey := "" query := `query GetNetworksQuery { getNetworks { name id } }` payload := map[string]string{"query": query} payloadBytes, _ := json.Marshal(payload) req, _ := http.NewRequest("POST", url, bytes.NewBuffer(payloadBytes)) req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", apiKey) client := &http.Client{} res, _ := client.Do(req) defer res.Body.Close() body, _ := ioutil.ReadAll(res.Body) var response map[string]interface{} json.Unmarshal(body, &response) fmt.Println(response) } ``` ```ruby ruby require 'net/http' require 'json' uri = URI('https://graph.codex.io/graphql') http = Net::HTTP.new(uri.host, uri.port) http.use_ssl = true headers = { 'Content-Type' => 'application/json', 'Authorization' => '' } query = { query: '{ getNetworks { name id } }' } request = Net::HTTP::Post.new(uri.path, headers) request.body = query.to_json response = http.request(request) puts response.body ``` # Subscriptions Real-time data streams via GraphQL subscriptions over WebSockets Subscriptions (WebSockets) require a Growth or Enterprise plan. [Learn more](https://dashboard.codex.io/dashboard/billing?utm_source=codex&utm_medium=docs&utm_campaign=billing). ## What is a Subscription? A subscription is a persistent connection that pushes data to you in real-time. Instead of repeatedly polling the API for updates, you open a WebSocket connection and tell Codex what data you want to watch. Whenever that data changes, Codex sends you the update automatically. **Every message the subscription sends you counts as 1 request** against your plan's monthly limit. For example, if you subscribe to price updates for a token and receive 1,000 updates in an hour, that's 1,000 requests. Not sure whether to use a query or a subscription? See [Queries vs Subscriptions](/extra/queries-vs-subscriptions.md) for a side-by-side comparison and endpoint mapping. ## When to Use Subscriptions - **Live price feeds** — streaming token or pair prices as they change - **Real-time trade notifications** — watching for new swaps, mints, or burns as they happen - **Streaming chart updates** — keeping OHLCV bars current without polling - **Launchpad monitoring** — detecting new token launches and graduation events instantly - **Live holder/balance tracking** — watching holder counts or wallet balances update in real-time If you only need data once (e.g. loading a page, fetching historical data, or responding to a user action), use [Queries](/concepts/queries.md) instead. ## How It Works Subscriptions use the WebSocket protocol. You open a connection to `wss://graph.codex.io/graphql`, authenticate via the `connection_init` payload, and then send `subscribe` messages for the data you want. ### Example ```typescript graphql-ws const client = createClient({ url: "wss://graph.codex.io/graphql", connectionParams: { Authorization: apiKey, }, }); client.subscribe({ query: ` subscription($address: String!, $networkId: Int!) { onPriceUpdated(address: $address, networkId: $networkId) { priceUsd timestamp address } } `, variables: { address: "0xbb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c", networkId: 56, }, sink: { next: (data) => { console.log(data); }, error: (error) => { console.error(error); }, complete: () => { console.log("complete"); }, } }) ``` ```javascript browser socket const CODEX_API_KEY = ""; const webSocket = new WebSocket( `wss://graph.codex.io/graphql`, "graphql-transport-ws" ); webSocket.onopen = () => { console.log("opened"); webSocket.send( JSON.stringify({ "type": "connection_init", "payload": { "Authorization": CODEX_API_KEY } }) ); }; webSocket.onmessage = (event) => { const data = JSON.parse(event.data) if (data.type === "connection_ack") { webSocket.send( JSON.stringify( { id: "my_id", type: "subscribe", payload: { "variables": { "address": "0xbb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c", "networkId": 56 }, "extensions": {}, "operationName": "onPriceUpdated", "query": `subscription($address: String!, $networkId: Int!) { onPriceUpdated(address: $address, networkId: $networkId) { priceUsd timestamp address } }` } } ) ); } else { console.log("message", data); } }; // You can send the `complete` message to the server to unsubscribe from the subscription. setTimeout(() => { webSocket.send(JSON.stringify({ id: "my_id", type: "complete", })); }, 10000); // unsubscribe after 10 seconds for demo purposes ``` You can refer to this [datafeed](https://gist.github.com/bradens/bfe449f8ea88fca8a1952cfe242b5e21) example using the SDK for `onTokenBarsUpdated` to get you started with a chart rendering subscription. ## Multiple Subscriptions You can subscribe multiple times in the same connection, just send additional `subscribe` messages. There is no hard limit on subscriptions per connection, but the practical capacity depends less on the number of subscriptions and more on the **total number of tokens being watched** and the message throughput from those tokens. As a starting point, plan for **up to ~100 tokens watched per connection**. If your token set is mostly low-volume, you can pack more onto a single connection. If it includes very active tokens (e.g. SOL, top trending tokens, high-volume pairs), open more connections with fewer tokens each. Randomize tokens across connections so you don't end up with one connection carrying all the high-volume tokens while others sit idle. If you start to see message drops, lower the density and add more connections. Reliability depends on: - Throughput of the subscriptions. If you are subscribed to [`onTokenEventsCreated`](/api-reference/subscriptions/ontokeneventscreated.md) to the SOL token, then you're going to get a lot more messages than a token with no volume. - The number of tokens you're watching across all subscriptions on a connection. `onPricesUpdated` accepts up to 25 tokens per subscription as a hard input cap — that's an API limit on each call, not a recommendation on connection density. You can run several `onPricesUpdated` subscriptions on the same connection, just account for the combined token count when sizing the connection. - Your internet connection, the amount of network capacity matters if you're making a lot of subscriptions. - Geography (how close is your application to Western US) Low-throughput subscriptions like [`onPairMetadataUpdated`](/api-reference/subscriptions/onpairmetadataupdated.md) can be packed considerably denser than this baseline. The ~100-token guideline assumes a typical mix of price/event streams — adjust upward for quiet streams and downward when watching high-volume tokens. ## Connection Management **Connection Persistence:** - WebSocket connections remain open indefinitely with no automatic server-side disconnection - Connections require heartbeat messages to stay alive (handled automatically if using our SDK) - Growth plans are limited to 300 connections. For Enterprise accounts, there isn’t a defined “hard-limit”, however, please contact our team if you have questions about your number of required connections. As each connection can handle multiple subscriptions, our “soft-limit” of connections is rarely reached by our customers. - No time-based limits on how long subscriptions can run **Reducing Usage for Idle Users:** Implement client-side idle detection to pause subscriptions when users are inactive: - Use idle detection hooks (e.g.[https://usehooks.com/useidle](https://usehooks.com/useidle) ) - Pause subscriptions after 'N' minutes of inactivity - Resume when user becomes active again - This prevents burning through API usage from idle browser tabs **Best Practices:** - Implement multiple subscriptions per connection (more reliable, easier to manage) - Exception: High-volume subscriptions like launchpad events should use dedicated connections - Proxy data through your backend to serve multiple users from a single subscription - Example: 10 users viewing the same chart = 1 subscription, instead of 10 Ensure you close subscriptions if you no longer want them to run. Deactivating an API key will still permit active subscriptions to remain open until they are disconnected or otherwise require reconnection. ## Commitment Levels Event subscriptions accept an optional commitment level that controls how early an event is delivered, trading latency against accuracy. It is set with the [`EventCommitmentLevel`](/api-reference/enums/eventcommitmentlevel.md) enum: - **`Confirmed`** — the most accurate stage; the event has been confirmed. The default, and the only level available on networks other than Solana and Base. Use when correctness matters more than latency. - **`Processed`** — delivered earlier than `Confirmed`, at the cost of some accuracy. On Solana, processed events may later be reorged out; on Base, `Processed` streams unconfirmed Flashblocks events ahead of confirmation. - **`Preprocessed`** — Solana only. The earliest possible signal, surfacing events *before* routing is finalized. **`Preprocessed` trades accuracy for speed.** Because preprocessed events are evaluated before transaction routing is finalized, they may differ from what later stages report, and **many preprocessed events will error and never be processed**. Use it only when the earliest-possible signal matters more than reliability — for example a latency-sensitive trading or sniper pipeline that can tolerate dropped or revised events. For anything that needs to be correct, prefer `Processed` or `Confirmed`. Levels other than `Confirmed` are supported on Solana and Base only, and `Preprocessed` is Solana-only. Bars subscriptions use a separate [`BarCommitmentLevel`](/api-reference/enums/barcommitmentlevel.md) — see [Confirmed vs. Unconfirmed data](/recipes/charts.md#confirmed-vs-unconfirmed-data) in the Charts recipe. ## FAQ - **Volume**: subscribe to [`onPairMetadataUpdated`](/api-reference/subscriptions/onpairmetadataupdated.md) — `volume*` fields update in real time. - **Holders**: [`onHoldersUpdated`](/api-reference/subscriptions/onholdersupdated.md) is available on Growth and Enterprise plans. If you don't need a real-time stream, you can also poll the [`holders`](/api-reference/queries/holders.md) query. # Webhooks Use your own HTTP endpoints to receive real-time updates export const AuthBanner = () => { const [visible, setVisible] = useState(true); useEffect(() => { if (typeof window === "undefined") return; try { if (sessionStorage.getItem("codex-banner-dismissed") === "true") { setVisible(false); return; } } catch (e) {} try { if (localStorage.getItem("codex-authenticated") === "true") { setVisible(false); return; } if (localStorage.getItem("d-explorer-key")) { setVisible(false); return; } } catch (e) {} fetch("https://dashboard.codex.io/api/api-keys/shared", { method: "GET", credentials: "include" }).then(res => res.ok ? res.json() : null).then(result => { const authed = result?.success && Array.isArray(result.data) && result.data.length > 0; try { localStorage.setItem("codex-authenticated", authed ? "true" : "false"); } catch (e) {} if (authed) setVisible(false); }).catch(() => {}); const handleKeyEntered = () => setVisible(false); window.addEventListener("codex-api-key-entered", handleKeyEntered); return () => window.removeEventListener("codex-api-key-entered", handleKeyEntered); }, []); const handleDismiss = () => { setVisible(false); try { sessionStorage.setItem("codex-banner-dismissed", "true"); } catch (e) {} }; return Don't have an API key yet? ; }; Webhooks deliver real-time updates from the Codex API directly to an HTTP endpoint you control. Unlike [subscriptions](/concepts/subscriptions.md), which require a persistent connection, webhooks fit naturally into event-driven architectures, alerting systems, and background services. ## Delivery and retries Codex sends webhook messages via HTTP POST and expects a 2xx response within 3 seconds. If your endpoint responds slowly or fails, the message will be retried up to two additional times with gradually increasing delays. If your service is down for long enough, some messages will be lost. Webhook usage breakdown: * **Processed** means an event matched at a basic level (for example, maker, pair, or token address) * **Triggered** means all conditions passed and Codex will try to publish * **Success** means publishing was attempted and succeeded * **Failed** means publishing was attempted and failed (usually a bad URL) ## Creating a webhook The fastest way to create and test a webhook is through the [Codex Explorer](https://explorer.codex.io), which provides a visual builder for every supported event type. To create one programmatically, use the [`createWebhooks`](/api-reference/mutations/createwebhooks.md) mutation. Each event type has its own input field on the mutation (for example, `tokenPairEventWebhooksInput` or `predictionTradeWebhooksInput`). See the [event type sections](#event-types) below for a ready-to-use creation example for each. ### Organizing webhooks with `bucketKey` You can optionally group and query your webhooks using a `bucketKey`. This is useful when you are managing many webhooks per user or per token, such as a price alert system. ```graphql theme={null} bucketKey: { bucketId: "price-alert-${user.id}" bucketSortKey: "price-alert-${token.id}" } ``` Both `bucketId` and `bucketSortKey` must be provided together. If you don't need to query webhooks by bucket, you can omit `bucketKey` entirely. Using both fields lets you independently query all alerts for a specific user and all alerts for a specific token. The individual `bucketId` and `bucketSortKey` fields on `createWebhook` are deprecated. Use the `bucketKey` object instead. ## Message structure Every webhook message shares a common envelope. The `type` field tells you which event fired, and the `data` field contains the event-specific payload described in the [event type sections](#event-types). - **type** (WebhookPublisherMessageType) The event type. One of `TOKEN_PAIR_EVENT`, `TOKEN_PAIR_EVENT_BATCH`, `TOKEN_PRICE_EVENT`, `TOKEN_PRICE_EVENT_BATCH`, `TOKEN_TRANSFER_EVENT`, `TOKEN_TRANSFER_EVENT_BATCH`, `MARKET_CAP_EVENT`, `MARKET_CAP_EVENT_BATCH`, `PREDICTION_TRADE_EVENT`, `PREDICTION_TRADE_EVENT_BATCH`, `PREDICTION_MARKET_METRICS_EVENT`, or `PREDICTION_MARKET_METRICS_EVENT_BATCH`. - **deduplicationId** (String) A unique identifier for this message. Use it to deduplicate retries on your side. - **webhookId** (String) The ID of the webhook that triggered this message. - **groupId** (String) The ID used to group related messages for ordered delivery. - **hash** (String) Deprecated. SHA256 hash of the `securityToken` and `deduplicationId`. Prefer the `X-Webhook-Timestamp` and `X-Webhook-Signature` headers described in [Verifying webhooks](#verifying-webhooks). The legacy `hash` does not cover the request body and is still emitted only for backwards compatibility. - **data** (WebhookPublisherDataModels) The event-specific payload. The shape depends on `type`. See the [event type sections](#event-types) for each payload structure. ## Verifying webhooks Every webhook delivery includes two signature headers that authenticate the exact request body bytes and provide a freshness signal for replay protection: * `X-Webhook-Timestamp`: Unix timestamp in seconds. * `X-Webhook-Signature`: lowercase hex HMAC-SHA256 over `{timestamp}.{rawBody}`, using your webhook's `securityToken` as the key. To verify a delivery: 1. Read the raw request body bytes as received, before any JSON parsing. 2. Build the signed payload string `{timestamp}.{rawBody}`. 3. Compute `hex(hmac_sha256(securityToken, signedPayload))` and compare it to `X-Webhook-Signature` using a constant-time comparison. 4. Reject the request if `abs(now - X-Webhook-Timestamp) > 300` seconds, or if either header is missing or malformed. ```js theme={null} const crypto = require("crypto"); function verifyWebhook ({ rawBody, timestamp, signature, securityToken }) { const age = Math.abs(Math.floor(Date.now() / 1000) - Number(timestamp)); if (!Number.isFinite(age) || age > 300) { return false; } const expected = crypto .createHmac("sha256", securityToken) .update(`${timestamp}.${rawBody}`) .digest("hex"); const expectedBuffer = Buffer.from(expected, "hex"); const signatureBuffer = Buffer.from(signature, "hex"); return ( expectedBuffer.length === signatureBuffer.length && crypto.timingSafeEqual(expectedBuffer, signatureBuffer) ); } ``` Verify the **original raw request body bytes**, then parse JSON only after the signature check passes. Re-serializing the parsed body will change whitespace and key order and produce a different signature. **Capturing the raw body** * Next.js App Router: call `await request.text()` once and use that exact string. * Next.js API routes: set `export const config = { api: { bodyParser: false } }`, then read the request stream into a buffer. * Express: mount `express.raw({ type: "application/json" })` on the webhook route, or capture the buffer in `express.json({ verify })`. * AWS Lambda + API Gateway: use `event.body` directly, base64-decoding when `event.isBase64Encoded` is true. * Rails/Rack: use `request.raw_post`. Flask/Django: `request.get_data()` / `request.body`. **Common mistakes** * Parsing and re-serializing JSON before verification. * Verifying only `data` or the legacy `hash` field instead of `{timestamp}.{rawBody}`. * Comparing signatures with `==` instead of a constant-time helper. * Treating the timestamp as milliseconds. `X-Webhook-Timestamp` is Unix seconds. * Skipping the freshness check, which removes replay protection. ## Hash verification (deprecated) The body `hash` field is deprecated. Use [Verifying webhooks](#verifying-webhooks) with `X-Webhook-Timestamp` and `X-Webhook-Signature`. The legacy `hash` is computed from `sha256(securityToken + deduplicationId)`, so it only identifies the delivery id pair and does not authenticate the delivered body. Both the headers and the legacy `hash` are emitted during the migration period. The legacy `hash` is a SHA256 digest of your webhook's `securityToken` concatenated with the message's `deduplicationId`: ```js theme={null} const crypto = require('crypto'); const calculatedHash = crypto .createHash('sha256') .update(securityToken) .update(deduplicationId) .digest('hex'); ``` ## Source IP addresses Codex sends webhook deliveries from a fixed set of IP addresses. If you want to gate inbound webhook traffic at the network layer in addition to verifying the signature on each payload, allowlist these IPs at your firewall or load balancer: ``` 35.155.50.173 52.25.29.13 44.235.164.143 52.32.112.191 ``` Network-level allowlisting complements signature verification, it doesn't replace it. Always verify the signature on incoming payloads to confirm authenticity. ## Event types Each event type fires on a different kind of on-chain or market activity. Pick the one that matches what you want to react to, then jump to its section below for filter conditions, the input field, a copy-paste mutation, and a sample payload. | Event | Fires when | | --- | --- | | [Token Pair Event](#token_pair_event) | Swaps, mints, burns, and other liquidity events on a trading pair. The most common type. | | [Token Price Event](#token_price_event) | A token's price crosses a threshold you set. | | [Token Transfer Event](#token_transfer_event) | Tokens move to or from a wallet you're watching. | | [Market Cap Event](#market_cap_event) | A token's market cap — fully diluted or circulating — crosses a threshold. | | [Prediction Trade Event](#prediction_trade_event) | Individual Polymarket or Kalshi trades, filterable by trader, market, event, side, or size. | | [Prediction Market Metrics Event](#prediction_market_metrics_event) | A prediction market's rolling windowed stats — volume, price, or trade count — cross a threshold, at the market or per-outcome level. | ### `TOKEN_PAIR_EVENT` The `TOKEN_PAIR_EVENT` webhook fires when a swap, mint, burn, or other liquidity event occurs on a trading pair. It delivers the full `Pair` and `Event` objects so you can reconstruct exactly what happened on-chain. This is the most commonly used webhook. **When to use it** * Tracking every trade made by a specific wallet * Monitoring high-value swaps on a single token or pair * Feeding a live trade tape for a specific pool * Alerting on liquidity events (mints, burns) for a pair **Filter conditions** * `tokenAddress`: fire for events involving this token * `networkId`: one or more network IDs to listen on * `swapValue`: filter by USD value of the swap * `maker`: fire for events made by a specific wallet * `pairAddress`: fire for events on a specific pair * `exchangeAddress`: fire for events on a specific exchange * `eventType`: filter to specific event types (`SWAP`, `MINT`, `BURN`, `SYNC`, `BUY`, `SELL`, `COLLECT`, `COLLECT_PROTOCOL`) See the full input type at [`tokenPairEventWebhookConditionInput`](/api-reference/input-objects/tokenpaireventwebhookconditioninput.md). ```graphql theme={null} mutation CreateTokenPairWebhook { createWebhooks( input: { tokenPairEventWebhooksInput: { webhooks: { name: "Big swaps on WETH/USDC" callbackUrl: "https://your-endpoint.com/webhook" securityToken: "your-security-token" alertRecurrence: INDEFINITE conditions: { pairAddress: { eq: "0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640" } networkId: { oneOf: [1] } swapValue: { gte: "10000" } } } } } ) { tokenPairEventWebhooks { id name } } } ``` ```json expandable theme={null} { "deduplicationId": "aa4cd403-b54e-4e2d-825c-f70a69f6fd9f-DbyEjKTHE76qgb8niQ4zDCoaU4CK7Si9PJkD6Ckjtugo:1399811149-0000000256717997#00000000#00000002#00000009", "groupId": "bc9345e6-b0da-4935-9b31-47b34cc5f628", "hash": "e39fa0bfcef1bfcd12d6707c29ac752899228d026338e594e04a398255962022", "type": "TOKEN_PAIR_EVENT", "webhook": { "bucketId": "bc9345e6-b0da-4935-9b31-47b34cc5f628", "bucketSortkey": "GSE6vfr6vws493G22jfwCU6Zawh3dfvSYXYQqKhFsBwe", "id": "aa4cd403-b54e-4e2d-825c-f70a69f6fd9f", "name": "traderpow:GSE6vfr6vws493G22jfwCU6Zawh3dfvSYXYQqKhFsBwe:TOKEN_PAIR_EVENT" }, "webhookId": "aa4cd403-b54e-4e2d-825c-f70a69f6fd9f", "data": { "event": { "address": "DbyEjKTHE76qgb8niQ4zDCoaU4CK7Si9PJkD6Ckjtugo", "baseTokenPrice": "18651403264.31996", "blockHash": "DGXgkMSxovvjdA5iKgrsFL7duNyXRPsW86Ztvg2Mc1hL", "blockNumber": 256717997, "data": { "amount0": "7020307392", "amount1": "-2899270904078", "liquidity": "51478999661589", "liquidity0": "1140722809229", "liquidity1": "457845387792910", "protocol": "Orca", "sqrtPriceX64": "376244087403309877755", "tick": "60310", "type": "Swap" }, "eventDisplayType": "Buy", "eventType": "Swap", "eventType2": "Token1Buy", "id": "DbyEjKTHE76qgb8niQ4zDCoaU4CK7Si9PJkD6Ckjtugo:1399811149", "labels": {}, "liquidityToken": "So11111111111111111111111111111111111111112", "logIndex": 2, "maker": "GSE6vfr6vws493G22jfwCU6Zawh3dfvSYXYQqKhFsBwe", "makerHashKey": "GSE6vfr6vws493G22jfwCU6Zawh3dfvSYXYQqKhFsBwe:DbyEjKTHE76qgb8niQ4zDCoaU4CK7Si9PJkD6Ckjtugo:1399811149", "networkId": 1399811149, "quoteToken": "token1", "sortKey": "0000000256717997#00000000#00000002#00000009", "supplementalIndex": 9, "timestamp": 1711526246, "token0PoolValueUsd": "186.5140326431996", "token0SwapValueUsd": "186.51403264319955960744460600963952776157867455426636901017", "token0ValueBase": "1", "token0ValueUsd": "186.5140326431996", "token1PoolValueUsd": "0.4483442346979745", "token1SwapValueUsd": "0.45162590368325119780671230665069180443756818839541768", "token1ValueBase": "0.0024038096669951625071971972244735217782", "token1ValueUsd": "0.4483442346979745", "transactionHash": "2dd5jDHbVYZpXgYbEQzpv47Z7jwimmHeDxKytiM9FB4GzBA5QeYh5xHTzkQvCScQksvTrHqgq9k7q84MBcGbaFfJ", "transactionIndex": 0, "ttl": 1716710246 }, "pair": { "address": "DbyEjKTHE76qgb8niQ4zDCoaU4CK7Si9PJkD6Ckjtugo", "exchangeHash": "whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc", "fee": null, "id": "DbyEjKTHE76qgb8niQ4zDCoaU4CK7Si9PJkD6Ckjtugo:1399811149", "networkId": 1399811149, "tickSpacing": null, "token0": "So11111111111111111111111111111111111111112", "token1": "FU1q8vJpZNUrmqsciSjp8bAKKidGsLmouB8CBdf8TKQv" } } } ## BATCH VERSION { "deduplicationId": "aa4cd403-b54e-4e2d-825c-f70a69f6fd9f-batch-0000000256717997", "groupId": "bc9345e6-b0da-4935-9b31-47b34cc5f628", "hash": "e39fa0bfcef1bfcd12d6707c29ac752899228d026338e594e04a398255962022", "type": "TOKEN_PAIR_EVENT_BATCH", "webhookId": "aa4cd403-b54e-4e2d-825c-f70a69f6fd9f", "data": [ { "event": { "...": "same shape as single event above" }, "pair": { "...": "same shape as single event above" } }, { "event": { "...": "second event in batch" }, "pair": { "...": "second pair in batch" } } ] } ``` ### `TOKEN_PRICE_EVENT` The `TOKEN_PRICE_EVENT` webhook fires when the price of a specific token crosses a threshold you define. Use it for price alerts on tokens you care about. **When to use it** * Price alerts for a single token (for example, "WETH above $4000") * Watchlist-style notifications for a small set of tokens * Dashboard price tickers where polling is not acceptable **Filter conditions** * `address`: the token contract address (required) * `networkId`: the network ID (required) * `priceUsd`: price condition that must be met (required). Supports `gt`, `gte`, `lt`, `lte`, `eq` See the full input type at [`tokenPriceEventWebhookConditionInput`](/api-reference/input-objects/tokenpriceeventwebhookconditioninput.md). ```graphql theme={null} mutation CreateTokenPriceWebhook { createWebhooks( input: { priceWebhooksInput: { webhooks: { name: "WETH above $4000" callbackUrl: "https://your-endpoint.com/webhook" securityToken: "your-security-token" alertRecurrence: INDEFINITE conditions: { address: { eq: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2" } networkId: { eq: 1 } priceUsd: { gte: "4000" } } } } } ) { priceWebhooks { id name } } } ``` ```json expandable theme={null} { "type": "TOKEN_PRICE_EVENT", "deduplicationId": "5e0fe797-c795-451f-af87-0257847b8c3b-0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2:1-0000000024000071#00000117#00000353", "webhookId": "5e0fe797-c795-451f-af87-0257847b8c3b", "groupId": "5e0fe797-c795-451f-af87-0257847b8c3b", "hash": "c296a760c563c9a4115146af75945d1b35d0741835309fa44d393ef3c2b044dd", "webhook": { "id": "5e0fe797-c795-451f-af87-0257847b8c3b", "name": "token-price-webhook" }, "data": { "id": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2:1", "priceUsd": "3.0898058248076381750556910457327e+3", "address": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", "networkId": 1, "timestamp": 1765585223, "blockNumber": 24000071 } } ## BATCH VERSION { "type": "TOKEN_PRICE_EVENT_BATCH", "deduplicationId": "5e0fe797-c795-451f-af87-0257847b8c3b-batch-0000000024000071", "webhookId": "5e0fe797-c795-451f-af87-0257847b8c3b", "groupId": "5e0fe797-c795-451f-af87-0257847b8c3b", "hash": "c296a760c563c9a4115146af75945d1b35d0741835309fa44d393ef3c2b044dd", "data": [ { "...": "same shape as the single message above" }, { "...": "additional messages in the batch" } ] } ``` ### `TOKEN_TRANSFER_EVENT` The `TOKEN_TRANSFER_EVENT` webhook fires when a token is transferred to or from a wallet you are monitoring. Use it to track wallet inflows and outflows in real time. **When to use it** * Monitoring whale wallet movements * Triggering on-chain alerts for your own wallets * Watching known exchange hot wallets or bridge addresses **Filter conditions** * `tokenAddress`: the token contract to track * `networkId`: one or more network IDs * `address`: the wallet address to monitor * `direction`: `TO` (receiving), `FROM` (sending), or both See the full input type at [`tokenTransferEventWebhookConditionInput`](/api-reference/input-objects/tokentransfereventwebhookconditioninput.md). ```graphql theme={null} mutation CreateTokenTransferWebhook { createWebhooks( input: { tokenTransferEventWebhooksInput: { webhooks: { name: "Inflows to whale wallet" callbackUrl: "https://your-endpoint.com/webhook" securityToken: "your-security-token" alertRecurrence: INDEFINITE conditions: { address: { eq: "0x1abde2088657de84ad6239f3d445dd07d6fa1033" } networkId: { oneOf: [8453] } direction: { oneOf: [TO] } } } } } ) { tokenTransferEventWebhooks { id name } } } ``` ```json expandable theme={null} { "type": "TOKEN_TRANSFER_EVENT", "deduplicationId": "6af3a260-7e2c-4615-babe-c239786ec9fd-0x84df029b0fc5d81ec8d65fa49568bc509e8ce0caa3de05c8de51acea8aafdb6b-740", "webhookId": "6af3a260-7e2c-4615-babe-c239786ec9fd", "groupId": "762acfb9-872d-4fa7-96a0-e45236759c55", "hash": "360adec82a811049a7374b087c40789e716c2329cb5dfc9b6dffc182fb77cd2b", "webhook": { "id": "6af3a260-7e2c-4615-babe-c239786ec9fd", "name": "Track transfers to wallet" }, "data": { "tokenAddress": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913", "networkId": 8453, "fromAddress": "0x4767bf3619bb493d129c864a023512fa1dce9da4", "toAddress": "0x1abde2088657de84ad6239f3d445dd07d6fa1033", "amount": "20000", "shiftedAmount": "0.02", "direction": "TO", "timestamp": 1767657659, "blockNumber": 40434156, "transactionHash": "0x84df029b0fc5d81ec8d65fa49568bc509e8ce0caa3de05c8de51acea8aafdb6b", "transactionIndex": 171, "logIndex": 740 } } ## BATCH VERSION { "type": "TOKEN_TRANSFER_EVENT_BATCH", "deduplicationId": "6af3a260-7e2c-4615-babe-c239786ec9fd-batch-40434156", "webhookId": "6af3a260-7e2c-4615-babe-c239786ec9fd", "groupId": "762acfb9-872d-4fa7-96a0-e45236759c55", "hash": "360adec82a811049a7374b087c40789e716c2329cb5dfc9b6dffc182fb77cd2b", "webhook": { "id": "6af3a260-7e2c-4615-babe-c239786ec9fd", "name": "Track all transfers" }, "data": [ { "...": "same shape as the single message above" }, { "...": "additional messages in the batch" } ] } ``` ### `MARKET_CAP_EVENT` The `MARKET_CAP_EVENT` webhook fires when a token's market cap crosses a threshold you specify. Unlike token price webhooks, it factors in supply (both fully diluted and circulating). **When to use it** * Market cap milestone alerts (for example, "VIRTUAL crosses $1B FDV") * Large-cap filtering logic for automated tools * Portfolio-level risk dashboards **Filter conditions** * `tokenAddress`: the token contract (required) * `networkId`: the network ID (required) * `fdvMarketCapUsd`: fully diluted market cap threshold * `circulatingMarketCapUsd`: circulating market cap threshold * `pairAddress`: optional source pair constraint * `liquidityUsd`, `volumeUsd`: optional pair-level liquidity and volume constraints See the full input type at [`marketCapEventWebhookConditionInput`](/api-reference/input-objects/marketcapeventwebhookconditioninput.md). ```graphql theme={null} mutation CreateMarketCapWebhook { createWebhooks( input: { marketCapEventWebhooksInput: { webhooks: { name: "VIRTUAL hits $1B FDV" callbackUrl: "https://your-endpoint.com/webhook" securityToken: "your-security-token" alertRecurrence: ONCE conditions: { tokenAddress: { eq: "0x0b3e328455c4059eeb9e3f84b5543f74e24e7e1b" } networkId: { eq: 8453 } fdvMarketCapUsd: { gte: "1000000000" } } } } } ) { marketCapEventWebhooks { id name } } } ``` ```json expandable theme={null} { "type": "MARKET_CAP_EVENT", "deduplicationId": "5e0fe797-c795-451f-af87-0257847b8c3b-0x0b3e328455c4059eeb9e3f84b5543f74e24e7e1b:8453-0000000030917440#00000117#00000353", "webhookId": "5e0fe797-c795-451f-af87-0257847b8c3b", "groupId": "test-group", "hash": "c296a760c563c9a4115146af75945d1b35d0741835309fa44d393ef3c2b044dd", "webhook": { "id": "5e0fe797-c795-451f-af87-0257847b8c3b", "name": "mcap-virt" }, "data": { "priceModel": { "id": "0x0b3e328455c4059eeb9e3f84b5543f74e24e7e1b:8453", "priceUsd": "2.014904011469267", "address": "0x0b3e328455c4059eeb9e3f84b5543f74e24e7e1b", "networkId": 8453, "timestamp": 1748624227, "blockNumber": 30917440, "absoluteDeviation": 0, "derivedSwapPrice": "2.008965143151456", "targetTokenAddress": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913", "pairMetadata": { "liquidityUsd": "67747", "pairId": "0x2aeee741fa1e21120a21e57db9ee545428e683c9:1", "volume24Usd": "162206" } }, "tokenModel": { "id": "0x0b3e328455c4059eeb9e3f84b5543f74e24e7e1b:8453", "address": "0x0b3e328455c4059eeb9e3f84b5543f74e24e7e1b", "name": "Virtual Protocol", "symbol": "VIRTUAL", "decimals": 18, "shiftedTotalSupply": "495620931.12225886540849246", "shiftedCirculatingSupply": "495620930.505466750752879695" }, "fdvMarketCapUsd": "998628602.29", "circulatingMarketCapUsd": "998628601.04" } } ## BATCH VERSION { "type": "MARKET_CAP_EVENT_BATCH", "deduplicationId": "5e0fe797-c795-451f-af87-0257847b8c3b-batch-0000000030917440", "webhookId": "5e0fe797-c795-451f-af87-0257847b8c3b", "groupId": "test-group", "hash": "c296a760c563c9a4115146af75945d1b35d0741835309fa44d393ef3c2b044dd", "data": [ { "priceModel": { "...": "same shape as single event above" }, "tokenModel": { "...": "same shape as single event above" }, "fdvMarketCapUsd": "998628602.29", "circulatingMarketCapUsd": "998628601.04" } ] } ``` ### `PREDICTION_TRADE_EVENT` The `PREDICTION_TRADE_EVENT` webhook fires when a prediction market trade occurs. Use it to track trades by a specific trader, on a specific market or event, or matching conditions like trade value or volume. **When to use it** * Tracking copy-traded wallets on Polymarket or Kalshi * Notifying on large trades in a specific market or event * Feeding a live trade tape for a prediction market dashboard **Filter conditions** * `traderId`: fire for a specific trader * `marketId`: fire for a specific market * `eventId`: fire for a specific event * `eventType`: filter by trade event type (`TRADE`, `BUY`, `SELL`, `BUY_COUNTERPARTY`, `SELL_COUNTERPARTY`, `POSITION_REDEEMED`). `PAYOUT_REDEMPTION` is deprecated; use `POSITION_REDEEMED` instead. * `tradeValueUsd`: filter by trade value in USD * `amountToken`: filter by number of tokens or shares traded See the full input type at [`predictionTradeWebhookConditionInput`](/api-reference/input-objects/predictiontradewebhookconditioninput.md). ```graphql theme={null} mutation CreatePredictionTradeWebhook { createWebhooks( input: { predictionTradeWebhooksInput: { webhooks: { name: "Track trader activity" callbackUrl: "https://your-endpoint.com/webhook" securityToken: "your-security-token" alertRecurrence: INDEFINITE conditions: { traderId: { eq: "your-trader-id" } tradeValueUsd: { gte: "100" } } bucketKey: { bucketId: "trader-alerts-${user.id}" bucketSortKey: "trader-${traderId}" } } } } ) { predictionTradeWebhooks { id name } } } ``` ```json expandable theme={null} { "type": "PREDICTION_TRADE_EVENT", "deduplicationId": "abc123-...", "webhookId": "6af3a260-7e2c-4615-babe-c239786ec9fd", "groupId": "762acfb9-872d-4fa7-96a0-e45236759c55", "hash": "360adec82a811049a7374b087c40789e716c2329cb5dfc9b6dffc182fb77cd2b", "webhook": { "id": "6af3a260-7e2c-4615-babe-c239786ec9fd", "name": "Track trader activity" }, "data": { "marketId": "0x1234...abcd", "sortKey": "0000000040434156#00000171#00000740", "outcomeId": "outcome-1", "outcomeLabel": "Yes", "protocol": "POLYMARKET", "tradeType": "BUY", "maker": "0x4767bf3619bb493d129c864a023512fa1dce9da4", "timestamp": 1767657659, "outcomeIndex": 0, "priceUsd": "0.72", "priceCollateral": "0.72", "amount": "100", "amountCollateral": "72", "amountUsd": "72.00", "transactionHash": "0x84df029b0fc5d81ec8d65fa49568bc509e8ce0caa3de05c8de51acea8aafdb6b", "blockNumber": 40434156, "networkId": 137, "exchangeAddress": "0xe55b5ceba4dc0d4e26261f3dcd468faaf7d0cdb8", "transactionId": "txn-abc123", "traderId": "trader-xyz", "eventId": "event-456" } } ## BATCH VERSION { "type": "PREDICTION_TRADE_EVENT_BATCH", "deduplicationId": "abc123-batch-40434156", "webhookId": "6af3a260-7e2c-4615-babe-c239786ec9fd", "groupId": "762acfb9-872d-4fa7-96a0-e45236759c55", "hash": "360adec82a811049a7374b087c40789e716c2329cb5dfc9b6dffc182fb77cd2b", "data": [ { "...": "same shape as the single message above" }, { "...": "additional messages in the batch" } ] } ``` ### `PREDICTION_MARKET_METRICS_EVENT` The `PREDICTION_MARKET_METRICS_EVENT` webhook fires when rolling, windowed statistics for a prediction market cross a threshold you define — at the whole-market level or for an individual outcome. Use it to react to volume spikes, price moves, or bursts of trading activity on a Polymarket or Kalshi market without polling. **When to use it** * Alerting on volume or trade-count spikes on a specific market * Tracking a price move on a single outcome (for example, "Yes" climbing past 60%) * Driving a live activity feed for a prediction market dashboard **Filter conditions** Metrics are evaluated over rolling time windows. Each window is its own field: `min5`, `hour1`, `hour4`, `hour12`, `day1`, and `week1`. Set a condition on one or more windows, and within a window match on: * **Market-level metrics** (the `market` field): `volumeUsd`, `trades`, `volumeChange`, `tradesChange` * **Per-outcome metrics** (`outcome0`, `outcome1`, or `anyOutcome`): everything available at the market level plus `price` and `priceChange` `marketId` is required — each webhook is pinned to a single market. Every metric supports the standard `gt`, `gte`, `lt`, `lte`, and `eq` operators. See the full input type at [`predictionMarketMetricsEventWebhookConditionInput`](/api-reference/input-objects/predictionmarketmetricseventwebhookconditioninput.md). ```graphql theme={null} mutation CreatePredictionMarketMetricsWebhook { createWebhooks( input: { predictionMarketMetricsEventWebhooksInput: { webhooks: { name: "Volume spike on a prediction market" callbackUrl: "https://your-endpoint.com/webhook" securityToken: "your-security-token" alertRecurrence: INDEFINITE conditions: { marketId: { eq: "your-market-id" } market: { hour1: { volumeUsd: { gte: "50000" } tradesChange: { gte: "2" } } } anyOutcome: { hour1: { priceChange: { gte: "0.1" } } } } } } } ) { predictionMarketMetricsEventWebhooks { id name } } } ``` Deliveries use the standard [message envelope](#message-structure). The `data` object carries the market, its `lifecycle`, one block per rolling window (`statsMin5`, `statsHour1`, `statsHour4`, `statsHour12`, `statsDay1`, `statsWeek1`), and `trending` / `relevance` / `competitive` score series. Each window holds market-level `core` totals, `outcome0Stats` / `outcome1Stats` (price, volume, orderbook), and a `statsChange` block whose `volumeChange`, `tradesChange`, and `priceChange` ratios are what your filter conditions compare against. The example shows the `min5` window in full; the other windows share the same shape. ```json expandable theme={null} { "type": "PREDICTION_MARKET_METRICS_EVENT", "deduplicationId": "103cb449-9165-442c-a94e-4e2394191096:KXATPMATCH-26JUN01TIAARN-ARN:Kalshi:1780339453", "webhookId": "103cb449-9165-442c-a94e-4e2394191096", "groupId": "default", "hash": "5f8fe57bd7ed36aa04237112cdaba68b04568b734a4f0cf7dd43ed26a5b934bd", "webhook": { "id": "103cb449-9165-442c-a94e-4e2394191096", "name": "Any" }, "data": { "marketId": "KXATPMATCH-26JUN01TIAARN-ARN:Kalshi", "lastTransactionAt": 1780339453, "lifecycle": { "ageSeconds": 158894, "expectedLifespanSeconds": 1333440, "timeToResolutionSeconds": 1174546, "isResolved": false }, "statsMin5": { "start": 1780339140, "end": 1780339500, "lastTransactionAt": 1780339453, "core": { "volume": { "usd": "214790", "ct": "214790" }, "trades": 344 }, "uniqueTraders": null, "liquidity": null, "openInterest": { "openInterest": { "open": { "usd": "2511700", "ct": "2511700" }, "close": { "usd": "2880725", "ct": "2880725" }, "low": { "usd": "2511700", "ct": "2511700" }, "high": { "usd": "2880725", "ct": "2880725" } } }, "outcome0Stats": { "core": { "venueOutcomeId": "yes#KXATPMATCH-26JUN01TIAARN-ARN:Kalshi", "trades": 282, "volume": { "usd": "205033", "ct": "205033", "shares": "398508360000" }, "price": { "open": { "usd": "0.52", "ct": "0.52" }, "close": { "usd": "0.6", "ct": "0.6" }, "low": { "usd": "0.5", "ct": "0.5" }, "high": { "usd": "0.6", "ct": "0.6" } } }, "buySell": null, "liquidity": null, "orderbook": { "bid": { "open": { "usd": "0", "ct": "0" }, "close": { "usd": "0", "ct": "0" }, "low": { "usd": "0", "ct": "0" }, "high": { "usd": "0", "ct": "0" } }, "ask": { "open": { "usd": "0.56", "ct": "0.56" }, "close": { "usd": "0.6", "ct": "0.6" }, "low": { "usd": "0.5", "ct": "0.5" }, "high": { "usd": "0.6", "ct": "0.6" } } }, "depth": null, "statsChange": { "volumeChange": 1.1353822760552819, "volumeSharesChange": 1.1420015657514841, "priceChange": 0.17647058823529413, "tradesChange": 0.128, "priceRange": 0.18181818181818182 } }, "outcome1Stats": { "...": "same shape as outcome0Stats, for the 'no' outcome" }, "allTimeStats": { "volume": { "usd": "1862439", "ct": "1862439" }, "venueVolume": { "usd": "3478959", "ct": "3478959" } }, "statsChange": { "volumeChange": 0.7787107886978701, "tradesChange": -0.13784461152882205, "openInterestChange": 0.10838253428676413 }, "scores": { "trending": 0.5129627015063644, "relevance": 0.9802115747563149, "competitive": 0.7098313166349453 } }, "statsHour1": { "...": "same shape as statsMin5, for the 1h window" }, "statsHour4": { "...": "same shape as statsMin5, for the 4h window" }, "statsHour12": { "...": "same shape as statsMin5, for the 12h window" }, "statsDay1": { "...": "same shape as statsMin5, for the 24h window" }, "statsWeek1": { "...": "same shape as statsMin5, for the 1w window" }, "trendingScores": { "score5m": 0.5129627015063644, "score1": 0.5526402920243513, "score4": 0.6330336268045679, "score12": 0.6056772499786226, "score24": 0.7080148356575182, "score1w": 0.49798763786971245 }, "relevanceScores": { "score5m": 0.9802115747563149, "score1": 0.9802115747563149, "score4": 0.9802115747563149, "score12": 0.9802115747563149, "score24": 0.9802115747563149, "score1w": 0.9506551892542483 }, "competitiveScores": { "score5m": 0.7098313166349453, "score1": 0.8411082705350258, "score4": 0.8190846252610525, "score12": 0.8288629254817336, "score24": 0.8254551774444506, "score1w": 0.8253162265343478 }, "allTimeStats": { "volume": { "usd": "1862439", "ct": "1862439" }, "venueVolume": { "usd": "3478959", "ct": "3478959" } }, "eventId": "KXATPMATCH-26JUN01TIAARN:Kalshi" } } ``` ## FAQ Webhooks monitor one token at a time, but there is no limit on how many webhooks you can create. To watch many tokens, create one webhook per token (the [`bucketKey`](#organizing-webhooks-with-bucketkey) field makes managing large sets straightforward). Create a `TOKEN_PAIR_EVENT` webhook for the token and filter `eventType` to `BUY`. The webhook payload includes the full event object, so you don't need a follow-up call to enrich the data. See the [`TOKEN_PAIR_EVENT` section](#token_pair_event) above for the full creation example. `securityToken` is your secret. Codex uses it as the HMAC key for the `X-Webhook-Signature` header on every delivery, which authenticates the exact raw request body. See [Verifying webhooks](#verifying-webhooks) for the full verification flow. The legacy body `hash` field is also derived from `securityToken` but is deprecated because it doesn't cover the body. Each webhook delivery counts as 1 request. If you also fire a follow-up API call to enrich the event (e.g. fetching token metadata), that's a second request. So if you're processing 10k buys per day with one enrichment call each, that's 20k requests/day or roughly 600k/month. # Rate Limits & Connection Limits Understand how requests are counted, connection limits, and how to optimize your usage ## Rate Limits Each plan has a per-second rate limit on API requests: | Plan | Rate Limit | |---|---| | **Almost free** | 5 requests/second | | **Growth** | 300 requests/second | | **Enterprise** | Custom — [contact our team](mailto:hello@codex.io) | Requests that exceed your rate limit will be rejected. If you consistently hit your rate limit, consider [optimizing your usage](/concepts/optimization.md) or upgrading your plan. ## When You're Rate Limited Requests over your limit are rejected with **HTTP 429** and an error coded `TOO_MANY_REQUESTS`: ```json { "errors": [ { "message": "Your account has been rate limited, please upgrade your plan", "extensions": { "code": "TOO_MANY_REQUESTS", "retryAfterSeconds": 10 } } ] } ``` When we can tell exactly when your limit lifts, `extensions.retryAfterSeconds` says how many seconds to wait — and the same value is sent as the standard `Retry-After` header, so most HTTP client libraries will honor it without any code from you. A 429 without that field means your request budget is momentarily empty rather than your account being throttled; capacity typically returns within a second, so back off briefly on your own schedule. Fixed retry intervals are the most common cause of prolonged rate limiting. If your interval is shorter than the penalty window, every retry lands inside it and extends the problem. See [Errors & Retries](/concepts/errors.md) for the full retry contract, including capacity errors that arrive as HTTP 200 and need the same backoff. ## How Requests Are Counted Every interaction with the Codex API counts against your plan's monthly request limit. Usage is counted per query response, not per HTTP request. Here's how each type of interaction is measured: | Interaction | What counts as 1 request | |---|---| | **Query** | Each query response (aliased queries in one request count separately) | | **Subscription** | Each message the subscription sends you | | **Webhook** | Each delivered message (when your condition is met) | A few details to keep in mind: - **Aliased queries each count separately.** If you combine multiple queries in one request using aliases, each query response is counted on its own, so `filterTokens` plus `getDetailedTokenStats` in one request counts as two. - **Open subscriptions are free when idle.** Subscriptions count one request per delivered message. An open subscription with no messages flowing does not consume usage. - **Webhooks count only on delivery.** A webhook counts only when a message is delivered, meaning when your condition is met. Codex's internal evaluation of events against your conditions is not counted. Subscriptions can generate a high volume of requests depending on what you're subscribed to. For example, subscribing to trade events on a high-volume token could produce thousands of messages per hour — each one counts as a request. ## Overages Overages are charged at the same per-million rate as your subscribed plan. For example, if you exceed your 1M Growth plan limit by 500K requests, you'll be charged for an additional 1M requests at the same per-million rate. For high-volume usage (10M+ requests/month), contact us on [Discord](https://discord.gg/9ZB7zcWuBY) or [via email](mailto:support@codex.io) for custom plan pricing with volume discounts. ## WebSocket Connection Limits WebSocket connections are the transport layer for [subscriptions](/concepts/subscriptions.md). Each connection can carry multiple subscriptions. | Plan | Connection Limit | |---|---| | **Growth** | 300 connections | | **Enterprise** | Soft limit — contact our team for your specific needs | Connection limits are per API key. Each connection can handle multiple subscriptions, so the connection limit is rarely a bottleneck. ## Capacity Per Connection There is no hard limit on subscriptions per connection. What matters in practice is the **total number of tokens being watched on that connection** and how active those tokens are — not the count of `subscribe` messages. As a starting point, plan for **up to ~100 tokens watched per connection**. Spread tokens across connections rather than packing them onto one — if a single connection ends up carrying all your high-volume tokens (SOL, top trending tokens, busy pairs), it'll drop messages while your other connections sit idle. If you see drops, reduce the per-connection token count and add more connections. The actual ceiling depends on: - **Throughput** — subscribing to [`onTokenEventsCreated`](/api-reference/subscriptions/ontokeneventscreated.md) for SOL generates far more messages than a low-volume token - **Network capacity** — your internet connection and proximity to Western US affect throughput - **Subscription type** — low-throughput streams like metadata updates can be packed considerably denser, while high-volume streams (launchpad events, busy chains) warrant dedicated connections Note: `onPricesUpdated` accepts up to 25 tokens per `input` array — that's a hard cap on a single subscription request, not a recommended connection density. You can run multiple `onPricesUpdated` subscriptions on the same connection, just count their combined tokens against the per-connection budget. ## Reducing Usage For best practices on batching requests, caching, managing subscription lifecycle, and other ways to minimize your request count, see [Optimization](/concepts/optimization.md). Deactivating an API key does **not** automatically close active WebSocket connections. You must explicitly close your connections or they will continue to consume requests until they disconnect. # Errors & Retries How Codex reports errors, which ones are safe to retry, and how long to wait ## How Errors Arrive Codex is a GraphQL API, so most errors come back in the `errors` array of an otherwise normal response — **HTTP 200 with an `errors` key**, not an HTTP error status. Only authentication and rate limiting fail at the HTTP layer. ```json { "errors": [ { "message": "The requested resources are over capacity. Retry in about 30 seconds — retrying sooner will be throttled.", "extensions": { "code": "OVER_CAPACITY", "retryAfterSeconds": 30 } } ] } ``` If your error handling only inspects HTTP status codes, it will treat these as successful responses. Always check whether the body contains an `errors` array. ## Which Errors to Retry There is one field to watch: **if an error carries `extensions.retryAfterSeconds`, wait that many seconds before retrying that request.** On rate-limited responses the same value is also sent as the standard `Retry-After` header, so most HTTP client libraries will honor it without any code from you. | Code | HTTP status | Meaning | Retry | |---|---|---|---| | `TOO_MANY_REQUESTS` | 429 | You exceeded your plan's rate limit | After `retryAfterSeconds`, or your own backoff if absent | | `OVER_CAPACITY` | 200 | We're briefly over capacity and scaling up | After `retryAfterSeconds` | | `UNAUTHENTICATED` / `FORBIDDEN` | 401 / 403 | Bad or missing credentials | No — fix the credentials | | Everything else | 200 | Unexpected failure on our side | Not automatically — see below | ### Rate limited A `TOO_MANY_REQUESTS` error means you exceeded your plan's per-second rate limit. When we can tell you exactly when the limit lifts, we do, via `retryAfterSeconds` and the `Retry-After` header. A 429 **without** `retryAfterSeconds` means your request budget is momentarily empty rather than your account being throttled — capacity typically returns within a second. Back off on your own schedule and retry; don't retry immediately in a tight loop. See [Rate Limits & Connection Limits](/concepts/rate-limits.md) for the limits themselves. ### Over capacity An `OVER_CAPACITY` error means the data your query needs is temporarily under more load than it can serve, and we're scaling up. It is not caused by anything wrong with your request — the same query will succeed once you retry. These arrive as HTTP 200 with the error in the body. Retry after `retryAfterSeconds` (currently 30). Retrying sooner is likely to be throttled and slows the recovery for everyone. ### Everything else Unexpected errors return a generic message with an error code: ```json { "errors": [ { "message": "Something went wrong. Error Code: 1a2b3c4d5e6f7890" } ] } ``` Don't retry these automatically — the same request will usually fail the same way. Include that error code when you [contact support](mailto:support@codex.io) or ask on [Discord](https://discord.gg/9ZB7zcWuBY); it lets us find the exact failure in our logs. ## Retry Strategy Fixed retry intervals are the most common cause of prolonged rate limiting. If your interval is shorter than the penalty window, every retry lands inside it and extends the problem. A retry policy that works well against Codex: 1. **Honor `retryAfterSeconds` whenever it's present.** It's computed from the actual condition — it isn't a guess, and it overrides whatever interval you'd otherwise use. 2. **Otherwise use exponential backoff with jitter.** Jitter matters if you run multiple workers: without it they synchronize and retry in a thundering herd. 3. **Cap your retries.** Three to five attempts is plenty; past that the condition needs attention rather than another request. 4. **Don't retry non-retriable errors.** Auth failures and malformed queries will fail identically every time. If you're hitting rate limits often enough that retry behavior matters, [Optimization](/concepts/optimization.md) covers how to reduce request volume — usually the better fix. # Optimization Best practices for reducing API usage, improving performance, and architecting efficient integrations ## Batch Queries Where Possible Several Codex **query** endpoints accept multiple inputs in a single request. Each batched call counts as one request, so use these instead of making separate calls: | Endpoint | Batching Support | |---|---| | [`getTokenPrices`](/api-reference/queries/gettokenprices.md) | Up to 25 tokens per request | | [`filterTokens`](/api-reference/queries/filtertokens.md) | Up to 200 results per page | | [`tokens`](/api-reference/queries/tokens.md) | Multiple token inputs in one call | | [`getDetailedPairsStats`](/api-reference/queries/getdetailedpairsstats.md) | Multiple pair inputs in one call | One [`getTokenPrices`](/api-reference/queries/gettokenprices.md) call with 25 tokens = **1 request**. Twenty-five individual calls = **25 requests**. **Batching a subscription does *not* reduce usage.** [`onPricesUpdated`](/api-reference/subscriptions/onpricesupdated.md) lets you watch up to 25 tokens in a single subscription (and [`onTokenBarsUpdated`](/api-reference/subscriptions/ontokenbarsupdated.md) batches similarly), but each message still delivers a *single* token's update and [bills per message](/concepts/rate-limits.md#how-requests-are-counted) — and updates fire per swap no matter how you group tokens. Watching 25 tokens in one subscription costs the same in requests as watching them across 25 subscriptions. The benefit is **operational, not billing**: fewer subscriptions and connections to open and manage. To actually cut subscription usage, see [Manage Subscription Lifecycle](#manage-subscription-lifecycle) and [Use Subscriptions Instead of Polling](#use-subscriptions-instead-of-polling). ## 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. ```graphql # 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`](/api-reference/queries/gettokenprices.md) every 5 seconds for 1 hour | **720 requests** | | Subscribing to [`onPriceUpdated`](/api-reference/subscriptions/onpriceupdated.md) for 1 hour (assuming ~1 update/sec) | **~3,600 requests** | | Subscribing to [`onPriceUpdated`](/api-reference/subscriptions/onpriceupdated.md) 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](/extra/queries-vs-subscriptions.md) 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](/networks.md) ([`getNetworks`](/api-reference/queries/getnetworks.md)) | 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: ```graphql # 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](https://usehooks.com/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 ```typescript // 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`](/api-reference/queries/gettokenprices.md) | [`filterTokens`](/api-reference/queries/filtertokens.md) (returns much more data) | | Top pair for a token | [`listPairsForToken`](/api-reference/queries/listpairsfortoken.md) | [`filterPairs`](/api-reference/queries/filterpairs.md) with phrase search | | Historical OHLCV | [`getBars`](/api-reference/queries/getbars.md) / [`getTokenBars`](/api-reference/queries/gettokenbars.md) | Reconstructing from [`getTokenEvents`](/api-reference/queries/gettokenevents.md) | | Token discovery | [`filterTokens`](/api-reference/queries/filtertokens.md) with filters + rankings | Fetching all tokens and filtering client-side | | Multiple token metadata | [`tokens`](/api-reference/queries/tokens.md) (batch) | Individual [`token`](/api-reference/queries/token.md) calls | ## Paginate Large Result Sets For endpoints that return many results, use cursor-based pagination rather than trying to fetch everything at once. Endpoints like [`getTokenEvents`](/api-reference/queries/gettokenevents.md) return a `cursor` alongside the results; pass it back on the next call to fetch the following page. A non-null `cursor` means more results are available. ```graphql # First page query { getTokenEvents(query: { address: "...", networkId: 1399811149 }, limit: 100) { items { timestamp eventType token0SwapValueUsd } cursor } } # Next page — pass the cursor from the previous response query { getTokenEvents(query: { address: "...", networkId: 1399811149 }, limit: 100, cursor: "eyJ...") { items { timestamp eventType token0SwapValueUsd } cursor } } ``` Fetch only the pages you need. Don't paginate through the entire dataset if you only need the first few pages. # Wallet PnL Understand how Codex calculates wallet profit and loss, including cost basis methodology, fee handling, and data freshness. Codex provides wallet-level profit and loss data across several endpoints, including [`filterWallets`](/api-reference/queries/filterwallets.md), [`filterTokenWallets`](/api-reference/queries/filtertokenwallets.md), [`getDetailedWalletStats`](/api-reference/queries/detailedwalletstats.md), and [`walletChart`](/api-reference/queries/walletchart.md). This page covers how those PnL numbers are calculated so you can interpret them correctly and layer on additional adjustments where needed. ## Cost basis methodology Codex calculates realized PnL using **weighted average cost basis**. Each buy adds to a cumulative acquisition cost, and each sell reduces that cost proportionally based on the share of holdings sold. The formula for `realizedPnl`: ``` sellAmountUsd - (acquisitionCostUsd × tokensSold / tokensHeld) ``` Which simplifies to: ``` sellAmountUsd - (averageCostPerToken × tokensSold) ``` ### Worked example A wallet trades a token called PENGU: | Action | Running acquisition cost | Tokens held | Realized PnL | |---|---|---|---| | Buy 10 PENGU for $10 | $10 | 10 | $0 | | Sell 4 PENGU for $8 | $6 | 6 | +$4 | | Buy 10 PENGU for $20 | $26 | 16 | +$4 (unchanged) | | Sell 16 PENGU for $32 | $0 | 0 | +$10 | After the second buy, the 16 tokens held carry a blended cost basis of about \$1.63 per token (\$26 acquisition cost spread across 16 tokens). Selling all 16 for \$32 produces \$6 of additional realized PnL, bringing the running total to \$10. ## Transferred and received tokens Codex only calculates PnL for tokens it can attribute a cost basis to. If a wallet acquires tokens through a swap, that swap establishes the acquisition cost used in the [weighted average cost basis](#cost-basis-methodology) calculation. If a wallet receives tokens through a transfer (with no corresponding buy), Codex has no cost basis for those tokens and **excludes them from PnL**. Concretely, when a wallet later sells tokens it received via transfer: - The sale is **not** counted in `realizedPnl` or other PnL figures, because the profit or loss can't be known without a cost basis. - The sale **is** still tracked in `all`-suffixed stats such as `volumeUsdAll1w`, `swapsAll1w`, and `amountSoldUsdAll1w`. Any field with `all` in its name reflects total activity, including sells of transferred tokens. We deliberately avoid assuming a zero cost basis for transferred tokens. Doing so would treat every such sale as pure profit, which skews PnL for transfer-recipient wallets and makes the data less accurate overall. ## Fee handling Wallet PnL and volume figures handle fees in two different ways depending on the fee type. ### Pool and swap fees Pool fees are already reflected in `realizedProfitUsd` and `volumeUsd`. Codex reads swap values post-execution, so the amounts and prices recorded are net of what the pool took. No additional adjustment is needed on your end. ### Gas fees Gas fees (base fees, priority fees, L1 data fees, and builder tips) are tracked separately from wallet PnL. Gas is paid outside the swap itself, so wallet aggregation does not fold it into `realizedProfitUsd` or `volumeUsd`. If you want PnL net of gas, subtract gas fees yourself using the [Global Fees Paid](/concepts/global-fees-paid.md) (GFP) fields. GFP exposes per-event and per-window fee components across most endpoints that return swap or bar data. ## Excluding native-token exposure By default, realized PnL includes a wallet's trades in the network's native token (SOL, ETH, and so on) alongside its other tokens. A trader who did well on memecoins but is down on their native-token bag can look worse than their token trading actually was, and vice versa. To see performance with native-token exposure stripped out, use the `ExNative` variants: `realizedProfitUsdExNative` and `realizedProfitPercentageExNative`, each available over rolling `1d`, `1w`, and `30d` windows (for example `realizedProfitUsdExNative30d`). These are exposed on [`getDetailedWalletStats`](/api-reference/queries/detailedwalletstats.md), on [`filterWallets`](/api-reference/queries/filterwallets.md) as both filters and ranking attributes, and on [`walletChart`](/api-reference/queries/walletchart.md) for time-series views. Ex-native tracking began July 2, 2026. Year windows are null (there isn't yet a full year of data), and chart buckets before that date read as 0. The standard `realizedProfitUsd*` fields continue to include native-token trades. ## Data freshness Wallet stats are indexed and updated on a rolling basis. To keep frequently viewed wallets fresh, querying [`filterWallets`](/api-reference/queries/filterwallets.md) or [`filterTokenWallets`](/api-reference/queries/filtertokenwallets.md) with one or more specific wallet addresses triggers a background refresh of the 500 most recently traded tokens for each wallet. Refreshes are capped at once every 6 hours per wallet. The triggering request returns the current indexed data immediately. Refreshed values become available shortly after, so a follow-up query will reflect the updated stats. ## Related - [Global Fees Paid](/concepts/global-fees-paid.md) — fee components available across endpoints - [Wallets recipe](/recipes/wallets/discover-traders.md) — practical examples of querying wallet data - [`filterWallets`](/api-reference/queries/filterwallets.md), [`filterTokenWallets`](/api-reference/queries/filtertokenwallets.md), [`getDetailedWalletStats`](/api-reference/queries/detailedwalletstats.md), [`walletChart`](/api-reference/queries/walletchart.md) # Global Fees Paid Understand how Codex measures and surfaces fee data across the API. Global Fees Paid (GFP) is a unified view of every dollar of fees a token, pool, or trader generates, broken into five components, summed into a single total, and combined into a handful of derived metrics. The same five components appear across bar queries, token filters, event feeds, and launchpad subscriptions, so once you understand them here you'll recognize them everywhere they're surfaced. ## What Global Fees Paid measures GFP captures the full economic cost of activity on-chain. Not just the trading fee a pool charges, but everything paid to network validators, block builders, and (on rollups) the L1 chain that posts the data. Every field is denominated in **USD** and summed at the start of the query window. The five components are the same on every endpoint. What changes is the **shape** of the data: a single scalar per event, an array per bar, a pre-computed value over a time window, or a single-window snapshot. The [endpoint coverage map](#endpoint-coverage) below shows which shape you get where. ## Endpoint coverage This is the master map. Find the endpoint you're using and follow the link to see the fields it exposes. | Endpoint | Type returned | Field set | |---|---|---| | [`getTokenEvents`](/api-reference/queries/gettokenevents.md) | `Event` (with `feeData`) | [Per-transaction fee detail](#per-transaction-fee-detail) | | [`getTokenEventsForMaker`](/api-reference/queries/gettokeneventsformaker.md) | `Event` (with `feeData`) | [Per-transaction fee detail](#per-transaction-fee-detail) | | [`onEventsCreatedByMaker`](/api-reference/subscriptions/oneventscreatedbymaker.md) | `Event` (with `feeData`) | [Per-transaction fee detail](#per-transaction-fee-detail) | | [`getBars`](/api-reference/queries/getbars.md) | `BarsResponse` | [Chart queries](#chart-queries) | | [`getTokenBars`](/api-reference/queries/gettokenbars.md) | `TokenBarsResponse` | [Chart queries](#chart-queries) | | [`onBarsUpdated`](/api-reference/subscriptions/onbarsupdated.md) | `IndividualBarData` | [Chart subscriptions](#chart-subscriptions) | | [`onTokenBarsUpdated`](/api-reference/subscriptions/ontokenbarsupdated.md) | `IndividualBarData` | [Chart subscriptions](#chart-subscriptions) | | [`filterTokens`](/api-reference/queries/filtertokens.md) | `TokenFilterResult` | [Token filtering](#token-filtering) | | [`onFilterTokensUpdated`](/api-reference/subscriptions/onfiltertokensupdated.md) | `TokenFilterResult` | [Token filtering](#token-filtering) | | [`onLaunchpadTokenEvent`](/api-reference/subscriptions/onlaunchpadtokenevent.md) | `LaunchpadTokenEventOutput` | [Launchpad subscriptions](#launchpad-subscriptions) | | [`onLaunchpadTokenEventBatch`](/api-reference/subscriptions/onlaunchpadtokeneventbatch.md) | `LaunchpadTokenEventOutput` | [Launchpad subscriptions](#launchpad-subscriptions) | ## The five components All five components are USD-denominated everywhere they appear in the schema. Some endpoints additionally expose the underlying native-unit values (wei on EVM, lamports on Solana). See [per-transaction fee detail](#per-transaction-fee-detail) for that. | Component | What it measures | |---|---| | `poolFees` | DEX/pool fees collected by the pool itself. The "trading fee," typically distributed to liquidity providers and/or the protocol. | | `baseFees` | Base fees paid to the network as gas. On EVM this is `baseFeePerGas × gasUsed`; on Solana it's roughly `5000 lamports × signature count`. | | `priorityFees` | Priority/tip portion of gas, paid to validators. EIP-1559 priority fee on EVM; tip portion of total fee on Solana. | | `builderTips` | Direct payments to block builders. The cleanest on-chain MEV signal: ETH transfers to `block.coinbase` on EVM, Jito tips on Solana. | | `l1DataFees` | Cost of posting rollup data to L1. **L2-only** (Base, Optimism, Arbitrum, etc.). Always `0` on L1s and Solana. | ## Derived metrics Built from the five components, these surface the most common questions developers ask about fee data without making them do the arithmetic. | Field | Formula | What it tells you | |---|---|---| | `totalFees` | `poolFees` + `baseFees` + `priorityFees` + `builderTips` + `l1DataFees` | Total economic cost over the window. | | `feeToVolumeRatio` | `totalFees` / `volume` | Normalized fee burden: how expensive activity is relative to volume. | | `mevToTotalFeesRatio` | `builderTips` / `totalFees` | Share of activity going to block builders. Your MEV-exposure indicator. | | `gasPerVolume` | (`baseFees` + `priorityFees` + `l1DataFees`) / `volume` | Pure gas cost per dollar of volume, excluding pool and builder fees. | | `averageCostPerTrade` | `totalFees` / `transactions` | Average user cost per trade in USD. | Ratios are `null` when their divisor is zero (no volume, no transactions, or no total fees). Always handle the null case in client code. ## Classifications Two categorical fields summarize the fee profile at a glance, useful for filtering, alerting, or labeling tokens in a UI without surfacing raw numbers. **`mevRiskLevel`**, based on `mevToTotalFeesRatio`: - `low`: builder tips are less than 3% of total fees - `medium`: between 3% and 30% - `high`: more than 30% - `null`: when `totalFees` is zero **`feeRegimeClassification`** describes which fee component dominates: - `gas-dominated`: gas (base + priority + L1 data) is more than 50% of fees - `mev-dominated`: builder tips are more than 20% of fees - `pool-fee-dominated`: pool fees dominate - `null`: when fees are zero ## Caveats and gotchas A few things that are easy to miss and worth flagging up front: - **Subscriptions only expose the five raw components.** `IndividualBarData` (used by [`onBarsUpdated`](/api-reference/subscriptions/onbarsupdated.md) and [`onTokenBarsUpdated`](/api-reference/subscriptions/ontokenbarsupdated.md)) does not include derived metrics or classifications. If you need `totalFees` or any ratio in a streaming context, compute it client-side or pull it from the corresponding query. - **Launchpad `feeToVolumeRatio1` is a `Float`, not a `String`.** Everywhere else in the schema, ratio fields are returned as strings (to preserve precision on very small or very large values). On `LaunchpadTokenEventOutput` it's a float. Your parsing layer needs to handle both. - **Filter inputs are a subset of result fields.** [`filterTokens`](/api-reference/queries/filtertokens.md) returns 35 fee fields, but only seven of them are usable as filter inputs. The full list is in the [token filtering](#token-filtering) section. - **Component-level filters at shorter windows aren't accepted as filter inputs.** You can filter on `totalFees5m` but not `builderTips5m`. Component-level filters are only available for the 24h window (`poolFees24`). - **Native-unit fields are chain-specific.** On `EventFeeData`, fields like `baseFeeNativeUnit` are wei on EVM and lamports on Solana: same field name, different units. The USD-denominated parent fields don't have this ambiguity. ## Per-transaction fee detail Every event returned by an event-feed endpoint carries a `feeData` object with both the **USD-denominated** GFP components and **native-unit** raw values. This is the most granular fee data the API exposes: one record per swap. **USD components** (same definitions as above): `poolFees`, `baseFees`, `priorityFees`, `builderTips`, `l1DataFees`, `totalFees`. **Pool-fee detail:** | Field | Type | Meaning | |---|---|---| | `poolFeeRateRaw` | `String` | Pool fee rate in the protocol's native encoding (e.g. raw uint24 for Uniswap V3). | | `poolFeeBps` | `Float` | Pool fee rate normalized to basis points (1 bps = 0.01%). | | `poolFeeAmountRaw` | `String` | Pool fee absolute amount in the fee token's smallest unit, when known per-swap. | | `dynamicFee` | `Boolean` | `true` when the pool fee is dynamic (Uniswap V4 hooks, AlgebraIntegral plugins). | | `estimatedPoolFee` | `Boolean` | `true` when `poolFeeBps` is a protocol-level estimate rather than an exact per-swap value. | **Native-unit gas detail** (wei on EVM, lamports on Solana): | Field | Type | Meaning | |---|---|---| | `baseFeeNativeUnit` | `String` | Base fee portion of gas. On EVM: `baseFeePerGas × gasUsed`. On Solana: `5000 × signatures`. | | `priorityFeeNativeUnit` | `String` | Priority fee. On EVM: `(effectiveGasPrice − baseFeePerGas) × gasUsed`. On Solana: `meta.fee − baseFee`. | | `gasUsed` | `String` | Gas units (EVM) or compute units (Solana) consumed by the transaction. | | `builderTipNativeUnit` | `String` | Direct payment to the block builder. ETH transfers to `block.coinbase` on EVM, Jito tip on Solana. | | `l1DataFeeNativeUnit` | `String` | L1 data posting fee (L2 rollups only). | | `txEventCount` | `Int` | Number of DEX events in the transaction. Use this as the divisor for pro-rating tx-level fees per event. | **Supplemental fee data:** `EventFeeData.supplementalFeeData` is a union type carrying protocol-specific fields. It currently has two variants, both for Pump.fun cashback: - **`PumpCashbackFeeData`** for Pump V1 swaps. Fields: `type` (always `"PumpCashback"`), `cashbackFeeBps`, `cashbackAmountLamports`. - **`PumpAmmCashbackFeeData`** for Pump AMM swaps. Same field shape, with `type = "PumpAmmCashback"`. The maker/wallet endpoints ([`getTokenEventsForMaker`](/api-reference/queries/gettokeneventsformaker.md), [`onEventsCreatedByMaker`](/api-reference/subscriptions/oneventscreatedbymaker.md)) are the simplest path to per-transaction fee detail for a specific wallet. Useful for trader analytics, MEV exposure tracking, or PnL accounting that includes gas costs. ## Chart queries [`getBars`](/api-reference/queries/getbars.md) and [`getTokenBars`](/api-reference/queries/gettokenbars.md) return parallel arrays. The value at index `i` corresponds to the bar starting at timestamp `t[i]`. All twelve fields are exposed: the five components, `totalFees`, the four ratios, and both classifications. | Field | Type | Notes | |---|---|---| | `poolFees` | `[String]` | USD per bar. | | `baseFees` | `[String]` | USD per bar. | | `priorityFees` | `[String]` | USD per bar. | | `builderTips` | `[String]` | USD per bar. | | `l1DataFees` | `[String]` | USD per bar. Always `0` outside L2 rollups. | | `totalFees` | `[String]` | USD per bar. | | `feeToVolumeRatio` | `[String]` | Null when bar volume is zero. | | `mevToTotalFeesRatio` | `[String]` | Null when `totalFees` is zero. | | `gasPerVolume` | `[String]` | Null when volume is zero. | | `averageCostPerTrade` | `[String]` | Null when no transactions. | | `mevRiskLevel` | `[String]` | Per-bar enum: `low` / `medium` / `high`, or null. | | `feeRegimeClassification` | `[String]` | Per-bar enum: `gas-dominated` / `mev-dominated` / `pool-fee-dominated`, or null. | ## Chart subscriptions [`onBarsUpdated`](/api-reference/subscriptions/onbarsupdated.md) and [`onTokenBarsUpdated`](/api-reference/subscriptions/ontokenbarsupdated.md) deliver one bar update at a time, so each fee field is a single scalar rather than an array. **Only the five raw components are exposed here.** Derived metrics and classifications are not. | Field | Type | Meaning | |---|---|---| | `poolFees` | `String` | USD for this bar. | | `baseFees` | `String` | USD for this bar. | | `priorityFees` | `String` | USD for this bar. | | `builderTips` | `String` | USD for this bar. | | `l1DataFees` | `String` | USD for this bar. | `IndividualBarData` is nested inside `OnBarsUpdatedResponse.aggregates.{r1, r5, r15, r60, …}.{usd, token}`. Resolution and currency are picked at the wrapper level, then the bar's fields are fetched. If you need `totalFees`, `mevRiskLevel`, or any ratio in a subscription context, compute it client-side from the five components or pull it from [`getBars`](/api-reference/queries/getbars.md) / [`getTokenBars`](/api-reference/queries/gettokenbars.md). ## Token filtering [`filterTokens`](/api-reference/queries/filtertokens.md) and [`onFilterTokensUpdated`](/api-reference/subscriptions/onfiltertokensupdated.md) return per-token results with fees pre-computed across **five rolling windows ending at "now"**: 5 minutes, 1 hour, 4 hours, 12 hours, and 24 hours. Each window suffix gives you a different field name. **Seven fields × five windows = 35 fee-related fields per token.** The base names are: - `poolFees{w}`, `baseFees{w}`, `priorityFees{w}`, `builderTips{w}`, `l1DataFees{w}`: the five components - `totalFees{w}`: the sum - `feeToVolumeRatio{w}`: the ratio Where `{w}` is one of `5m`, `1`, `4`, `12`, `24`. So the fully-enumerated set includes `poolFees5m`, `poolFees1`, `poolFees4`, `poolFees12`, `poolFees24`, `baseFees5m`, …, `feeToVolumeRatio24`. ### Filter inputs The `TokenFilters` input accepts a **subset** of the result fields. Only seven inputs are filterable: | Filter input | Type | Notes | |---|---|---| | `totalFees5m` | `NumberFilter` | `gt` / `lt` / `between` against summed fees over 5 minutes. | | `totalFees1` | `NumberFilter` | …over 1 hour. | | `totalFees4` | `NumberFilter` | …over 4 hours. | | `totalFees12` | `NumberFilter` | …over 12 hours. | | `totalFees24` | `NumberFilter` | …over 24 hours. | | `poolFees24` | `NumberFilter` | The only component-level filter: 24h pool fees. | | `feeToVolumeRatio24` | `NumberFilter` | 24h ratio filter. | Component-level filters at shorter windows (e.g. `builderTips24`, `baseFees1`) are **not** accepted as filter inputs. ### Ranking attributes All 35 result fields are usable as `ranking.attribute` values for sorting: ``` poolFees5m, poolFees1, poolFees4, poolFees12, poolFees24, baseFees5m, baseFees1, baseFees4, baseFees12, baseFees24, priorityFees5m, priorityFees1, priorityFees4, priorityFees12, priorityFees24, builderTips5m, builderTips1, builderTips4, builderTips12, builderTips24, l1DataFees5m, l1DataFees1, l1DataFees4, l1DataFees12, l1DataFees24, totalFees5m, totalFees1, totalFees4, totalFees12, totalFees24, feeToVolumeRatio5m, feeToVolumeRatio1, feeToVolumeRatio4, feeToVolumeRatio12, feeToVolumeRatio24 ``` ## Launchpad subscriptions [`onLaunchpadTokenEvent`](/api-reference/subscriptions/onlaunchpadtokenevent.md) and [`onLaunchpadTokenEventBatch`](/api-reference/subscriptions/onlaunchpadtokeneventbatch.md) deliver real-time updates on launchpad tokens (Pump.fun, Bonk, MeteoraDBC, Pump Mayhem, etc.). Fee fields populate on `eventType: "Updated"` events; other event types (`Deployed`, `Created`, `Migrated`, `Completed`, and the `Unconfirmed*` variants) carry the field shape but values may be null. **Only the 1-hour window is exposed:** | Field | Type | Notes | |---|---|---| | `poolFees1` | `String` | USD over the last hour. | | `baseFees1` | `String` | USD over the last hour. | | `priorityFees1` | `String` | USD over the last hour. | | `builderTips1` | `String` | USD over the last hour. | | `l1DataFees1` | `String` | USD over the last hour. Always `0` for non-L2 launchpad networks (most of them). | | `totalFees1` | `String` | Sum of the five components, USD. | | `feeToVolumeRatio1` | `Float` | `totalFees1 / volume1`. **Note: this is a `Float`, unlike the `String` ratios elsewhere in the schema.** | [`onLaunchpadTokenEventBatch`](/api-reference/subscriptions/onlaunchpadtokeneventbatch.md) is the more efficient choice when you don't need event-by-event delivery. It returns batched arrays in a single message. # Discover Tokens Learn how to build token dashboards with trending data, advanced filters, and token search In this recipe, we'll show you how to use the best endpoint in the industry, [`filterTokens`](/api-reference/queries/filtertokens.md), to populate "Discovery" pages where you can showcase tokens that fit specific criteria. From simple search queries to complex filtering and trending data, Codex has you covered across [80+ networks](https://docs.codex.io/networks) with data on over 70M+ tokens. Remember: You can always inspect queries on [Defined.fi](https://www.defined.fi?utm_source=codex&utm_medium=docs&utm_campaign=recipes-discover-tokens) for inspiration or to see how we use Codex to present data on our frontend. We recommend using the [Chrome GraphQL Network Inspector](https://chromewebstore.google.com/detail/graphql-network-inspector/ndlbedplllcgconngcnfmkadhokfaaln). ## Search by Name or Symbol Start with basic token discovery using phrase search to find tokens by name, symbol, or contract address. The [`filterTokens`](/api-reference/queries/filtertokens.md) endpoint supports improved symbol matching when using the phrase parameter with \$ prefix (eg: \$PEPE). Use the \$ prefix for results with improved token symbol matches, or without the \$ prefix to return partial token symbol matches. You can also use the token contract address to ensure an exact match. We also recommend utilizing ranking attributes such as `volume24` or `trendingScore24`, and filters such as `liquidity` to help ensure search results are relevant. `trendingIgnored` is adjusted regularly to ensure the best results are shown for trending tokens. Set to `true` if you want results to include stablecoins, wrapped base tokens, rugs/scams/low quality tokens etc.\ \ `statsType` filters MEV-related events from data to ensure you receive real user activity. - FILTERED: Removes MEV events. Shows "organic" volume. - UNFILTERED: Includes everything, even MEV events Most consumer-facing apps want `trendingIgnored: false` and `statsType: FILTERED` for the cleanest data. ## Global Fees Paid Codex exposes detailed fee breakdowns and MEV analytics across token data, so you can filter and rank tokens by the cost activity they generate rather than just price or volume. Fee fields cover pool fees, base fees, priority fees, builder tips, and L1 data fees, along with derived metrics like fee-to-volume ratio that surface tokens with genuine economic activity versus wash-traded volume. For the full breakdown of components, derived metrics, classifications, and which endpoints expose what, see the [Global Fees Paid](/concepts/global-fees-paid.md) concepts page. The example below ranks tokens by 1-hour total fees paid, filtered to tokens with meaningful fee activity and a minimum fee-to-volume ratio. ```graphql theme={null} { filterTokens( filters: { totalFees24: { gt: 500, lt: 1000000 } feeToVolumeRatio24: { gt: 0.005 } volume24: { gt: 50000 } network: [1, 1399811149, 8453, 137, 42161, 10, 56, 146] } limit: 20 rankings: [{ attribute: totalFees1, direction: DESC }] ) { count results { token { symbol name networkId address } volume1 totalFees1 poolFees1 baseFees1 priorityFees1 builderTips1 l1DataFees1 feeToVolumeRatio1 } } } ``` ## Bundlers/Snipers/Insiders Data New to filterTokens! You can now query for information on snipers, bundlers, and insiders on all newly launched tokens (since Nov 2025). This information is also included in our [launchpad subscriptions](/recipes/launchpads.md). - **Bundlers:** Identified as 4 or more buys of the same token/pair in the same block. Additionally, each swap must be more than \$5 with the total bundle being greater than 0.05% of the total supply. This data helps to detect coordinated buying activity from multiple wallets. - **Snipers:** Wallets that buy a token within 4 seconds of the first swap. Token creators are excluded. This helps identify bots and fast traders attempting to buy immediately at launch. - **Insiders:** Definitions and methodologies for insiders for EVM and SVM networks are outlined as follows: - **EVM Insider Definition** - Recipient funded by creator — wallet's firstFundedByAddress matches token creator - Direct transfer from creator — transfer.from is the creator address - Transfer from existing insider — transfer.from is already in the insider set Excluded: Zero address transfers, the token creation tx itself, and any DEX transactions. - **SVM (Solana) Insider Definition** - Recipient funded by creator — same as EVM - Creator is tx signer or sender — if creator is in txMakers or is the from address (when NOT a DEX event) - Any non-swap launchpad token transfer — if it's a launchpad token and the recipient doesn't send tokens back in the same tx, assumed insider Excluded: Transfers flagged with involvesDexProgram, and token swap patterns. **Label Persistence:** Wallets identified as any of the above will be labelled indefinitely once applied. For example: A wallet labeled as a "bundler" from one token keeps that label even for other tokens.\ \ **Suspicious:** A single roll-up of every wallet flagged as one or more of the above — `suspiciousCount` and `suspiciousHeldPercentage`. Because a wallet can be flagged as a sniper, bundler, and/or insider at once, these are the deduplicated union of those cohorts rather than a simple sum of the three counts. Use them when you want one overall risk number for a token instead of checking each cohort separately.\ \ **Data Fields:** ` sniperCount`, `bundlerCount`, `insiderCount`, `suspiciousCount`, `devHeldPercentage`, `sniperHeldPercentage`, `bundlerHeldPercentage`, `insiderHeldPercentage`, `suspiciousHeldPercentage`.\ \ You can also filter by these data types with `gt` `lt` `gte` `lte`. [Test this query in the Explorer →](/explore.md) ```graphql theme={null} query { filterTokens( filters: { launchpadName: "Pump.fun" } rankings: { direction: DESC, attribute: createdAt } ) { results { token { name address symbol } sniperCount bundlerCount insiderCount suspiciousCount devHeldPercentage sniperHeldPercentage bundlerHeldPercentage insiderHeldPercentage suspiciousHeldPercentage } } } ``` Different platforms use varying definitions and methodologies for identifying this type of data, with some assigning labels that persist too broadly. For this reason, you can expect that there will be data discrepancies between our data and other platforms. Codex's approach focuses on accuracy over catching every single edge case. We will continue to adjust our labelling thresholds as necessary to ensure the most accurate and reliable identification of this data. **With our robust set of filtering options**, you can design queries for almost any use-case, or combine them as needed: - **Multi-Network discovery:** Search across multiple networks simultaneously - **Time-based analysis:** Filter by creation date, recent activity, or specific timestamps - **Exchange-Specific:** Focus on tokens from specific exchanges or launchpad protocols - **Behavioral Filtering:** Use wallet age and trading pattern metrics to assess token quality - **Risk Management:** Combine scam detection with low liquidity, volume, and our new bundlers/snipers/insiders data filters Check out related endpoints in their respective API reference pages: - [filterTokens](/api-reference/queries/filtertokens.md) - [filterPairs](/api-reference/queries/filterpairs.md) - [getTokenPrices](/api-reference/queries/gettokenprices.md) - [getDetailedPairStats](/api-reference/queries/getdetailedpairstats.md) - [holders](/api-reference/queries/holders.md) # Detailed Token Page Learn how to build a comprehensive token detail page with price, holders, safety, trades, and real-time updates In this recipe we'll walk through building a token detail page — the kind of page your users land on after clicking a token from a discovery feed. We'll combine multiple Codex endpoints to populate every section: metadata, price, holders, top traders, safety signals, trade history, and real-time updates. This data powers the token pages on [Defined.fi](https://www.defined.fi/sol/9wK8yN6iz1ie5kEJkvZCTxyN1x5sTdNfx8yeMY8Ebonk?utm_source=codex&utm_medium=docs&utm_campaign=recipes-token-dashboard): ## Step 1: Token Metadata & Safety Start by fetching the token's core info — name, symbol, images, social links, and safety signals. This single query gives you everything for the header of your token page. [Test this query in the Explorer →](/explore.md) ```graphql theme={null} query TokenMetadata { token( input: { address: "9wK8yN6iz1ie5kEJkvZCTxyN1x5sTdNfx8yeMY8Ebonk" networkId: 1399811149 } ) { name symbol decimals address networkId isScam creatorAddress creator { address displayName category identityLabels tokensCreatedCount tokensMigratedCount } createdAt mintable freezable socialLinks { twitter telegram discord website } top10HoldersPercent info { circulatingSupply totalSupply imageSmallUrl imageLargeUrl description } launchpad { launchpadName graduationPercent poolAddress completed migrated migratedAt migratedPoolAddress } } } ``` **Token verification:** Codex uses `isScam` rather than `isVerified` for token safety. `isScam: false` is the equivalent of a token being "verified." For Solana tokens, also check `mintable` and `freezable` — if these return an address, the token's supply can be increased or holdings can be frozen. **Screen for suspicious-wallet concentration.** Alongside `isScam`, Codex reports how much of a token's supply sits in risky wallet cohorts. [`filterTokens`](/api-reference/queries/filtertokens.md) exposes `suspiciousHeldPercentage` and `suspiciousCount` — the deduplicated union of snipers, bundlers, and insiders — plus the per-cohort `sniperHeldPercentage`, `bundlerHeldPercentage`, `insiderHeldPercentage`, and `devHeldPercentage`. These are available as result fields, filter inputs, and ranking attributes, so you can both display them and screen risky tokens out at discovery time. For launchpad tokens, the same breakdown is available inline on [`pairMetadata`](/api-reference/queries/pairmetadata.md) via `walletActivity` (shown in Step 2). **Resolve the creator inline.** Alongside the raw `creatorAddress`, `EnhancedToken` exposes `creator`, a fully resolved [`Wallet`](/api-reference/types/wallet.md). Select it to pull the creator's display name, identity labels, [category](/api-reference/enums/walletcategory.md), and `tokensCreatedCount` / `tokensMigratedCount` in the same call — a useful trust signal (e.g. a serial deployer) without a follow-up query. ## Step 2: Price & Pair Data Fetch the token's current price, volume, and liquidity from its top trading pair using `pairMetadata`. This gives you the price stats panel for your dashboard. [Test this query in the Explorer →](/explore.md) ```graphql theme={null} query TokenPrice { pairMetadata( pairId: "7GtLUbEStB1xjqVcGHqpAo4hW8uFNbLKDMcoHb7QSEXY:1399811149" ) { price liquidity volume5m volume1 volume4 volume12 volume24 priceChange5m priceChange1 priceChange4 priceChange12 priceChange24 highPrice24 lowPrice24 enhancedToken0 { name symbol isScam } enhancedToken1 { name symbol isScam } walletActivity { suspiciousCount suspiciousHeldPercentage sniperHeldPercentage bundlerHeldPercentage insiderHeldPercentage } } } ``` If you don't already have the pair ID, use `listPairsWithMetadataForToken` to find it. Results are sorted by liquidity so the first result is the most active pair. [Test this query in the Explorer →](/explore.md) ```graphql theme={null} query FindPair { listPairsWithMetadataForToken( tokenAddress: "9wK8yN6iz1ie5kEJkvZCTxyN1x5sTdNfx8yeMY8Ebonk" networkId: 1399811149 limit: 5 ) { results { pair { address id token0 token1 fee protocol } backingToken { address symbol } volume liquidity } } } ``` Use `enhancedToken0` and `enhancedToken1` to get enriched token metadata directly from the pair query — this saves you an extra call to `token`. The `walletActivity` block returns suspicious-wallet concentration (snipers, bundlers, insiders) and is populated for launchpad tokens — it's `null` for others, where you should read these stats from [`filterTokens`](/api-reference/queries/filtertokens.md) instead. ## Step 3: Holders & Top Traders Build the holders tab. Use `holders` for the top holder list and `tokenTopTraders` for the most active traders with PnL data. [Test this query in the Explorer →](/explore.md) ```graphql theme={null} query TopHolders { holders( input: { tokenId: "9wK8yN6iz1ie5kEJkvZCTxyN1x5sTdNfx8yeMY8Ebonk:1399811149" } ) { count top10HoldersPercent items { address balance shiftedBalance balanceUsd tokenPriceUsd } } } ``` [Test this query in the Explorer →](/explore.md) ```graphql theme={null} query TopTraders { tokenTopTraders( input: { tokenAddress: "9wK8yN6iz1ie5kEJkvZCTxyN1x5sTdNfx8yeMY8Ebonk" networkId: 1399811149 tradingPeriod: WEEK limit: 20 } ) { items { walletAddress realizedProfitUsd realizedProfitPercentage amountBoughtUsd amountSoldUsd volumeUsd buys sells tokenBalance lastTransactionAt } } } ``` ## Step 4: Trade History Show recent buys and sells. Use `getTokenEvents` for the initial load and paginate with `cursor` for older trades. [Test this query in the Explorer →](/explore.md) ```graphql theme={null} query RecentTrades { getTokenEvents( query: { address: "9wK8yN6iz1ie5kEJkvZCTxyN1x5sTdNfx8yeMY8Ebonk" networkId: 1399811149 } limit: 25 ) { cursor items { eventDisplayType timestamp maker token0SwapValueUsd token1SwapValueUsd token0Address token1Address transactionHash blockNumber walletAge walletLabels } } } ``` [Test this query in the Explorer →](/explore.md) ```graphql theme={null} query LargeBuys { getTokenEvents( query: { address: "9wK8yN6iz1ie5kEJkvZCTxyN1x5sTdNfx8yeMY8Ebonk" networkId: 1399811149 eventDisplayType: [Buy] priceUsdTotal: { gt: 1000 } } limit: 25 ) { cursor items { eventDisplayType timestamp maker token0SwapValueUsd token1SwapValueUsd transactionHash walletLabels } } } ``` ## Step 5: Chart Data Fetch OHLCV bars for rendering a price chart. See the [Charts recipe](/recipes/charts.md) for full details on rendering with TradingView. [Test this query in the Explorer →](/explore.md) ```graphql theme={null} query TokenChart { getTokenBars( symbol: "9wK8yN6iz1ie5kEJkvZCTxyN1x5sTdNfx8yeMY8Ebonk:1399811149" from: 1740000000 to: 1740604800 resolution: "60" ) { o h l c t volume } } ``` ## Step 6: Real-Time Updates Once the page is loaded, open subscriptions to keep it live. Here are the key subscriptions for a token dashboard: Subscribe to `onPairMetadataUpdated` to keep price, volume, and liquidity current without polling. [Test this query in the Explorer →](/explore.md) ```graphql theme={null} subscription LivePrice { onPairMetadataUpdated( id: "7GtLUbEStB1xjqVcGHqpAo4hW8uFNbLKDMcoHb7QSEXY:1399811149" ) { price liquidity volume5m volume1 volume24 priceChange5m priceChange1 priceChange24 } } ``` Subscribe to `onTokenEventsCreated` to stream new trades as they happen. [Test this query in the Explorer →](/explore.md) ```graphql theme={null} subscription LiveTrades { onTokenEventsCreated( input: { tokenAddress: "9wK8yN6iz1ie5kEJkvZCTxyN1x5sTdNfx8yeMY8Ebonk" networkId: 1399811149 } ) { events { eventDisplayType timestamp maker token0SwapValueUsd token1SwapValueUsd transactionHash walletLabels } } } ``` Subscribe to `onTokenBarsUpdated` to keep the chart updating in real time. [Test this query in the Explorer →](/explore.md) ```graphql theme={null} subscription LiveChart { onTokenBarsUpdated( tokenId: "9wK8yN6iz1ie5kEJkvZCTxyN1x5sTdNfx8yeMY8Ebonk:1399811149" ) { aggregates { r1 { usd { o h l c t volume } } } } } ``` Subscribe to `onHoldersUpdated` to keep the holders list current. [Test this query in the Explorer →](/explore.md) ```graphql theme={null} subscription LiveHolders { onHoldersUpdated( tokenId: "9wK8yN6iz1ie5kEJkvZCTxyN1x5sTdNfx8yeMY8Ebonk:1399811149" ) { holders balances { address balance shiftedBalance balanceUsd } } } ``` Each subscription uses one connection toward your plan's limit (300 for Growth plans). A single token dashboard page with all four subscriptions above uses 4 connections. Share connections across subscriptions where possible — see [Subscriptions › Multiple Subscriptions](/concepts/subscriptions.md#multiple-subscriptions) for guidance on how to size each connection. ## Putting It All Together Here's the recommended data flow for a token dashboard: **On page load (queries):** 1. `token` — metadata, safety, social links 2. `pairMetadata` — price, volume, liquidity 3. `holders` + `tokenTopTraders` — holder and trader tabs 4. `getTokenEvents` — recent trade history 5. `getTokenBars` — chart OHLCV data **After load (subscriptions):** 1. `onPairMetadataUpdated` — live price and volume 2. `onTokenEventsCreated` — live trade feed 3. `onTokenBarsUpdated` — live chart updates 4. `onHoldersUpdated` — live holder changes **Optimizing calls:** You can reduce initial load by running queries 1-5 in parallel — they're all independent. For the subscriptions, open them on a single WebSocket connection to minimize connection usage. ### Subscriptions vs Queries Quick Reference | Data | Query (historical) | Subscription (real-time) | |------|-------------------|-------------------------| | Price & volume | `pairMetadata` | `onPairMetadataUpdated` | | Trades | `getTokenEvents` | `onTokenEventsCreated` | | Chart bars | `getBars` / `getTokenBars` | `onBarsUpdated` / `onTokenBarsUpdated` | | Holders | `holders` | `onHoldersUpdated` | | Token prices | `getTokenPrices` | `onPricesUpdated` | Check out the related endpoints in their respective pages: - [token](/api-reference/queries/token.md) - [pairMetadata](/api-reference/queries/pairmetadata.md) - [listPairsWithMetadataForToken](/api-reference/queries/listpairswithmetadatafortoken.md) - [holders](/api-reference/queries/holders.md) - [tokenTopTraders](/api-reference/queries/tokentoptraders.md) - [getTokenEvents](/api-reference/queries/gettokenevents.md) - [getBars](/api-reference/queries/getbars.md) / [getTokenBars](/api-reference/queries/gettokenbars.md) - [onPairMetadataUpdated](/api-reference/subscriptions/onpairmetadataupdated.md) - [onTokenEventsCreated](/api-reference/subscriptions/ontokeneventscreated.md) - [onTokenBarsUpdated](/api-reference/subscriptions/ontokenbarsupdated.md) - [onHoldersUpdated](/api-reference/subscriptions/onholdersupdated.md) # Events Learn how to get a list of token events In this recipe we'll show you how to use the Codex api to fetch a list of swaps for a token, complete with filtering, sorting, and realtime updates. This data powers the transactions tables view on [Defined.fi](https://www.defined.fi/base/0x6cdcb1c4a4d1c3c6d054b27ac5b77e89eafb971d?quoteToken=token1). ## Queries To get a list of swaps for a token, you can use the [`getTokenEvents`](/api-reference/queries/gettokenevents.md) query. For example, for the WBNB token on the BNB chain (networkId 56), you can use the following query: [Test this query in the Explorer →](/explore.md) ```graphql theme={null} query Events { getTokenEvents( query: { address: "0xbb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c" networkId: 56 } ) { items { id token0Address token1Address token0SwapValueUsd token1SwapValueUsd transactionHash } } } ``` To get more (paginate), you can use the `cursor` argument to get the next page of results. Modify the query to include the cursor like this: [Test this query in the Explorer →](/explore.md) ```graphql theme={null} query PaginatedEvents { getTokenEvents( cursor: "eyJpdiI6ImNhYmI1Y2UzMmUyYjRmYzRhOGU4NWM5Njc1NzFjZGEzIiwiY29udGVudCI6ImU4ZGNiNTI3YTRiZDJmYTRlNDNjNzE0ZTM3MGU1MDdjZjE5YjhiNTM4ZWE0NjUxM2MxMzlmYjA0OTQ4YTZjNTA1MmVmM2M1ZTdiNTNhOWIyZDczY2Q2NjYxYjM2MTllZTQ1ZDE2ZTJkMDJmYjllZTU2N2YwOTQ3OWNhYmY1NzRjZDNhZTJlM2IwMDQ3ZjZkZGQzODE1Mzk0YTNkMWExMzBiZTNkZTQ4MGUyOWQyNjRiYmFlNjMwMjQ1NzgwOGFhM2RhZDU1YmY0ZjNhZGFlNWYyNDM2NGJhMjY1OWZlZjI0MzQifQ==" query: { address: "0xbb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c" networkId: 56 } ) { cursor items { id token0Address token1Address token0SwapValueUsd token1SwapValueUsd transactionHash timestamp blockNumber } } } ``` Now you know how to use a paginated cursor to fetch more results. This pattern is used in many places in the Codex API, see [`getTokenEventsForMaker`](/api-reference/queries/gettokeneventsformaker.md), [holders](/api-reference/queries/holders.md) for an example. ## Realtime Updates To get realtime updates, you can use the [`onTokenEventsCreated`](/api-reference/subscriptions/ontokeneventscreated.md) subscription. [Test this query in the Explorer →](/explore.md) ```graphql theme={null} subscription TokenEventsCreated { onTokenEventsCreated( input: { tokenAddress: "0xbb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c" networkId: 56 } ) { events { id token0Address token1Address token0SwapValueUsd token1SwapValueUsd transactionHash timestamp blockNumber } } } ``` Now you've got all you need to build a fully functional swap list view. See more event queries in the [api reference](/api-reference/queries), like [getTokenEventsForMaker](/api-reference/queries/gettokeneventsformaker.md), [getEventLabels](/api-reference/queries/geteventlabels.md) for an example. And more subscriptions like - [getTokenEvents](/api-reference/queries/gettokenevents.md) - [onTokenEventsCreated](/api-reference/subscriptions/ontokeneventscreated.md) - [onEventsCreatedByMaker](/api-reference/subscriptions/oneventscreatedbymaker.md) - [onEventsCreated](/api-reference/subscriptions/oneventscreated.md) - [onUnconfirmedEventsCreated](/api-reference/subscriptions/onunconfirmedeventscreated.md) - [onUnconfirmedEventsCreatedByMaker](/api-reference/subscriptions/onunconfirmedeventscreatedbymaker.md) # Charts Learn how to render a token chart with the Codex API In this recipe we'll show you how to use the Codex api to render a token chart, complete with OHLCV data, and more. This data powers the charts on [defined.fi](https://defined.fi/bsc/0xbb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c). ## Fetch Start by implementing the following query to fetch the OHLCV data for a given token. [Test this query in the Explorer →](/explore.md) ```graphql theme={null} query ChartData { getBars( symbol: "0xbb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c:56" from: 1750353512 to: 1750439882 resolution: "1" ) { o h l c t volume } } ``` ## Render ```json Sample Response expandable { "data": { "getBars": { "o": [ 639.962297445, 639.948026102, ], "h": [ 640.343357275, 640.081660228, ], "l": [ 639.795898952, 639.914833559, ], "c": [ 639.948026102, 639.983598149, ], "t": [ 1750353300, 1750353600, ], "volume": [ "577495.066850454", "302268.420570785", ] } } } ``` The result uses the "response-as-a-table" pattern, which means you can use the `o`, `h`, `l`, `c`, `t`, and `volume` fields to render the chart. This is adapted from the TradingView documentation, and is intended to be used with their charting library [here](https://www.tradingview.com/charting-library-docs/latest/connecting_data/UDF/#response-as-a-table-concept). ## Realtime updates Now that we have all the data we need to render a chart, we can use the [onTokenBarsUpdated](/api-reference/subscriptions/ontokenbarsupdated.md) subscription to get realtime updates for all pairs of a given token. Note, you can also use [onBarsUpdated](/api-reference/subscriptions/onbarsupdated.md) to get realtime updates for a specific pair. [Test this query in the Explorer →](/explore.md) ```graphql theme={null} subscription OnTokenBarsUpdated { onTokenBarsUpdated(tokenId: "0xbb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c:56") { aggregates { r1 { usd { o h l c t volume } } } } } ``` After receiving the realtime updates, you can use the `o`, `h`, `l`, `c`, `t`, and `volume` fields to update the chart. ## Confirmed vs. Unconfirmed data The bars subscriptions accept an optional `commitmentLevel` argument. Omitting it gives you `confirmed` behavior by default. - **`confirmed`** (default): best for charts where accuracy matters more than latency, like historical analysis or finalized trade data. - **`processed`**: best for live trading UIs and sniper bots, where the lowest possible latency matters more than perfect accuracy. Processed events may later be reorged out. **`processed` is currently available on Solana only.** Event subscriptions (not bars) use a separate [`EventCommitmentLevel`](/api-reference/enums/eventcommitmentlevel.md) with its own values — `Confirmed`, `Processed`, and the even-earlier `Preprocessed`. See [Commitment levels](/concepts/subscriptions.md#commitment-levels) on the Subscriptions page for the tradeoffs. ## Aggregated Charts Create token charts with aggregated data across all valid pairs with [getTokenBars](/api-reference/queries/gettokenbars.md). Note that this data has limited historical data, back to timestamp `1753121580`. For real-time aggregate charts, subscribe to [onTokenBarsUpdated](/api-reference/subscriptions/ontokenbarsupdated.md). You can refer to this [datafeed](https://gist.github.com/bradens/bfe449f8ea88fca8a1952cfe242b5e21) example using the SDK for `onTokenBarsUpdated` to get you started with a chart rendering subscription. Check out all of our charting queries and subscriptions in the [api reference](/api-reference) - [onTokenBarsUpdated](/api-reference/subscriptions/ontokenbarsupdated.md) - [onBarsUpdated](/api-reference/subscriptions/onbarsupdated.md) - [getBars](/api-reference/queries/getbars.md) - [getTokenBars](/api-reference/queries/gettokenbars.md) # Launchpads Filter through launchpad data and get realtime updates. In this recipe we'll show you how to use the Codex api to create a 3 column launchpad view, complete with filtering, sorting, and realtime updates. This data directly powers the launchpads view on [defined.fi](https://defined.fi/tokens/launchpads). The websocket pushes data on every new token creation and event through the lifetime of the bonding curve and for an additional 6 hours post-migration (graduation). New to launchpad tokens? See the [Launchpad Lifecycle](/recipes/launchpad-lifecycle.md) guide for how tokens progress through stages — from bonding curve to graduation to migration. The supported launchpads include Pump.fun, Four.meme, Launchlab, Meteora, and more. See [Supported Launchpads](https://docs.codex.io/launchpads) for the complete list with each launchpad's `launchpadName` and `protocol` filter values. It is a requirement to proxy launchpad data through your backend to serve multiple users from a single subscription. Failure to do so may result in additional charges and termination of your connection. ## Initial Fetch Start by implementing the following query to fetch the launchpad data, one query for each column. [Test this query in the Explorer →](/explore.md) ```graphql theme={null} query LaunchpadsNew { filterTokens( filters: { launchpadCompleted: false, launchpadMigrated: false } rankings: { attribute: createdAt, direction: DESC } ) { results { createdAt marketCap priceUSD token { name symbol address networkId launchpad { graduationPercent launchpadName launchpadProtocol migrated completed completedAt } } } } } ``` [Test this query in the Explorer →](/explore.md) ```graphql theme={null} query LaunchpadsCompleting { filterTokens( filters: { launchpadCompleted: false launchpadMigrated: false launchpadGraduationPercent: { gt: 80, lt: 100 } } rankings: { attribute: graduationPercent, direction: DESC } ) { results { createdAt marketCap priceUSD token { name symbol address networkId launchpad { graduationPercent launchpadName launchpadProtocol migrated completed completedAt } } } } } ``` [Test this query in the Explorer →](/explore.md) ```graphql theme={null} query LaunchpadsCompleted { filterTokens( filters: { launchpadMigrated: true launchpadGraduationPercent: { gte: 100 } } rankings: { attribute: launchpadMigratedAt, direction: DESC } ) { results { createdAt marketCap priceUSD token { name symbol address networkId launchpad { graduationPercent launchpadName launchpadProtocol migrated completed completedAt } } } } } ``` Now you have the data for the initial render for each column. You can adjust the filters with the remaining of the arguments to the [filterTokens](/api-reference/queries/filtertokens.md) query. See the [TokenFilters](/api-reference/input-objects/tokenfilters.md) type for more details. By default `filterTokens` will return all tokens from a launchpad, across all networks, if you want to scope down to a specific launchpad, use the `launchpadName` or `launchpadProtocol` filters, if you want to narrow down just to specific network, use the `network` filter. ## Realtime Updates To get realtime updates, you can use the [onLaunchpadTokenEventBatch](/api-reference/subscriptions/onlaunchpadtokeneventbatch.md) subscription. This gives you every update for each launchpad token. [Test this query in the Explorer →](/explore.md) ```graphql theme={null} subscription OnLaunchpadTokenEventBatch( $input: OnLaunchpadTokenEventBatchInput ) { onLaunchpadTokenEventBatch(input: $input) { eventType marketCap price token { address decimals id name networkId symbol createdAt launchpad { graduationPercent poolAddress completedAt completed migratedAt migrated migratedPoolAddress } } devWallet { address displayName category tokensCreatedCount tokensMigratedCount } } } ``` Now you've got all you need to build a fully functional launchpad view. Each launchpad event carries `devWallet`, the token creator resolved to a full [`Wallet`](/api-reference/types/wallet.md). Select it to show the dev's display name, [category](/api-reference/enums/walletcategory.md), identity labels, and how many tokens they've created and migrated — inline, without a separate lookup per launch. Launchpad events are extremely high-frequency and will send a large number of requests. We offer a monthly flat-rate option with unlimited requests for this subscription. [Contact us](mailto:hello@codex.io?subject=Launchpad%20Events%20Subscription) for more information. ## Further Reading Launchpads that utilize bonding curves, like pump.fun, have structured token progression from 0-100% before "graduating." Some launchpad protocols like Zora, Base, and Clanker do not implement bonding curves, and lack the distinct graduation phases. Tokens on protocols without bonding curves will simply be 'New', without going through the further phases of completing, completed, or migrating. In these cases the associated attributes like `migrated`/`completed`, `migratedAt`/`completedAt`, `migratedSlot`, and `graduationPercent` will be absent. All the metadata in the launchpad subscription is available through the launchpad model in EnhancedToken (https://docs.codex.io/api-reference/types#launchpaddata). The volume/liquidity/etc metrics are then available through a variety of different endpoints for each token. Data for charts and trading events would need to be fetched separately, as those aren’t included in the subscription. Check out other similar queries and subscriptions in the GraphQL [reference](/api-reference/introduction.md) - [filterTokens](/api-reference/queries/filtertokens.md) - [onLaunchpadTokenEventBatch](/api-reference/subscriptions/onlaunchpadtokeneventbatch.md) - [onLaunchpadTokenEvent](/api-reference/subscriptions/onlaunchpadtokenevent.md) # Launchpad Lifecycle Understand how tokens progress through launchpad stages — from creation to bonding curve to graduation and migration. Launchpad tokens go through a series of stages before they become fully tradeable on a DEX. This guide explains each stage, how to detect them with the Codex API, and which events fire along the way. This complements the [Launchpads recipe](/recipes/launchpads.md) which covers building a launchpad discovery UI. ## The Stages Tokens on bonding-curve launchpads (Pump.fun, Four.meme, LaunchLab, etc.) progress through these stages: ``` Deployed → Created → Bonding (Updated) → Completed → Migrated ``` | Stage | What's happening | Key fields | |-------|-----------------|------------| | **Deployed** | Token contract discovered on-chain | `eventType: Deployed` | | **Created** | Metadata populated (name, symbol, image) | `eventType: Created` | | **Bonding** | Trading on bonding curve, `graduationPercent` rising from 0→100 | `eventType: Updated`, `graduationPercent < 100` | | **Completed** | Bonding curve filled — waiting for migration | `completed: true`, `migrated: false` | | **Migrated** | Liquidity moved to DEX pool — fully tradeable | `migrated: true`, `migratedPoolAddress` set | **Completed vs Migrated:** `completed` means the bonding curve is full. `migrated` means liquidity has actually moved to a DEX. For monitoring graduations via subscriptions, use `Migrated` events — `Completed` events are a legacy state and will be deprecated. **Not all launchpads have bonding curves.** Protocols like Zora, Clanker, and Baseapp skip the bonding curve entirely. Tokens on these protocols go straight to "Created" without progressing through completion or migration. Fields like `graduationPercent`, `completed`, `migrated`, and `migratedAt` will be absent for these tokens. Doppler-based protocols (like Bankr) are a related case. They manage liquidity along ticks on Uniswap pools rather than progressing through a traditional bonding curve, so `graduationPercent`, `completed`, and `completedAt` will be null. Migration is still tracked when it occurs, but is rare in practice. ## Detecting Token State Use the `launchpad` field on the `token` query to check a token's current lifecycle stage. [Test this query in the Explorer →](/explore.md) ```graphql theme={null} query LaunchpadState { token( input: { address: "9wK8yN6iz1ie5kEJkvZCTxyN1x5sTdNfx8yeMY8Ebonk" networkId: 1399811149 } ) { name symbol launchpad { launchpadName launchpadProtocol graduationPercent poolAddress completed completedAt migrated migratedAt migratedPoolAddress } } } ``` Use this logic to determine the current stage: ```js function getTokenStage(launchpad) { if (!launchpad) return 'not-a-launchpad-token' if (launchpad.migrated) return 'migrated' if (launchpad.completed) return 'completed' if (launchpad.graduationPercent > 0) return 'bonding' return 'new' } ``` ## Filtering by Stage Use `filterTokens` with launchpad filters to query tokens at each stage. These are the same filters that power the columns in the [Launchpads recipe](/recipes/launchpads.md). [Test this query in the Explorer →](/explore.md) ```graphql theme={null} query BondingTokens { filterTokens( filters: { launchpadCompleted: false launchpadMigrated: false launchpadGraduationPercent: { gt: 0 } } rankings: { attribute: graduationPercent, direction: DESC } limit: 10 ) { results { createdAt marketCap priceUSD token { name symbol address networkId launchpad { launchpadName graduationPercent completed migrated } } } } } ``` [Test this query in the Explorer →](/explore.md) ```graphql theme={null} query RecentlyMigrated { filterTokens( filters: { launchpadMigrated: true } rankings: { attribute: launchpadMigratedAt, direction: DESC } limit: 10 ) { results { createdAt marketCap priceUSD token { name symbol address networkId launchpad { launchpadName graduationPercent migrated migratedAt migratedPoolAddress } } } } } ``` Use `launchpadName` or `launchpadProtocol` to narrow down to a specific launchpad. [Test this query in the Explorer →](/explore.md) ```graphql theme={null} query PumpFunTokens { filterTokens( filters: { launchpadName: ["Pump.fun"] launchpadCompleted: false launchpadMigrated: false } rankings: { attribute: createdAt, direction: DESC } limit: 10 ) { results { createdAt marketCap priceUSD token { name symbol address networkId launchpad { launchpadName launchpadProtocol graduationPercent } } } } } ``` For the complete list of supported launchpads with their `launchpadName` and `protocol` filter values, see [Supported Launchpads](https://docs.codex.io/launchpads). ## Real-Time Lifecycle Events Subscribe to `onLaunchpadTokenEventBatch` to receive events as tokens progress through each stage. Filter by `eventType` to listen for specific transitions. [Test this query in the Explorer →](/explore.md) ```graphql theme={null} subscription NewLaunchpadTokens { onLaunchpadTokenEventBatch(input: { eventType: Created }) { eventType address networkId launchpadName marketCap price holders token { name symbol address networkId createdAt launchpad { graduationPercent poolAddress } } } } ``` `Updated` events fire as tokens trade on the bonding curve. These include statistics like price, volume, and holder counts — fields that aren't available on `Created` or `Migrated` events. [Test this query in the Explorer →](/explore.md) ```graphql theme={null} subscription BondingCurveUpdates { onLaunchpadTokenEventBatch(input: { eventType: Updated }) { eventType address networkId launchpadName marketCap price holders volume1 buyCount1 sellCount1 sniperCount sniperHeldPercentage bundlerCount bundlerHeldPercentage devHeldPercentage top10HoldersPercent token { name symbol launchpad { graduationPercent } } } } ``` Subscribe to `Migrated` events to detect when a token graduates from its bonding curve and moves to a DEX pool. [Test this query in the Explorer →](/explore.md) ```graphql theme={null} subscription TokenGraduations { onLaunchpadTokenEventBatch(input: { eventType: Migrated }) { eventType address networkId launchpadName marketCap price liquidity token { name symbol address networkId launchpad { graduationPercent migrated migratedAt migratedPoolAddress } } } } ``` Use `onLaunchpadTokenEvent` with an `address` to follow a single token through all its stages. [Test this query in the Explorer →](/explore.md) ```graphql theme={null} subscription TrackToken { onLaunchpadTokenEvent( input: { address: "9wK8yN6iz1ie5kEJkvZCTxyN1x5sTdNfx8yeMY8Ebonk" networkId: 1399811149 } ) { eventType marketCap price holders token { name symbol launchpad { graduationPercent completed migrated migratedAt migratedPoolAddress } } } } ``` Launchpad events are extremely high-frequency. You must proxy this data through your backend to serve multiple users from a single subscription. We offer a monthly flat-rate option with unlimited requests. [Contact us](mailto:hello@codex.io?subject=Launchpad%20Events%20Subscription) for more information. ## Event Types Reference | Event Type | When it fires | Stats available? | |-----------|---------------|-----------------| | `Deployed` | Token contract discovered on-chain | No | | `Created` | Token metadata populated | No | | `Updated` | Token stats change (trades, price, holders) | Yes — price, volume, holders, sniper/bundler counts | | `Completed` | Bonding curve filled (legacy — use `Migrated`) | No | | `Migrated` | Liquidity moved to DEX pool | No | | `UnconfirmedDeployed` | Token discovered before finalization | No | | `UnconfirmedMetadata` | Metadata processed before finalization | No | Statistics fields (`price`, `volume1`, `holders`, `sniperCount`, etc.) are only populated on `Updated` events. Other event types signal a state change but don't include these metrics. ## After Migration Once a token migrates, it behaves like any other DEX token. You can switch from launchpad subscriptions to the standard Codex endpoints: | Data | Endpoint | |------|----------| | Price & volume | [`pairMetadata`](/api-reference/queries/pairmetadata.md) using the `migratedPoolAddress` | | Live price | [`onPairMetadataUpdated`](/api-reference/subscriptions/onpairmetadataupdated.md) | | Trades | [`getTokenEvents`](/api-reference/queries/gettokenevents.md) | | Live trades | [`onTokenEventsCreated`](/api-reference/subscriptions/ontokeneventscreated.md) | | Chart | [`getTokenBars`](/api-reference/queries/gettokenbars.md) | | Live chart | [`onTokenBarsUpdated`](/api-reference/subscriptions/ontokenbarsupdated.md) | See the [Detailed Token Page recipe](/recipes/detailed-token-page.md) for the full post-migration data flow. ## Protocols Without Bonding Curves Some launchpad protocols don't use bonding curves. Tokens on these protocols are created and immediately tradeable — they skip the bonding, completed, and migrated stages entirely. | Protocol | Has bonding curve? | |----------|-------------------| | Pump.fun | Yes | | Four.meme | Yes | | LaunchLab | Yes | | Meteora DBC | Yes | | boop.fun | Yes | | Zora | No | | Clanker | No | | Baseapp | No | | Virtuals | No | For tokens without bonding curves, the `launchpad` fields `graduationPercent`, `completed`, `completedAt`, `migrated`, `migratedAt`, and `migratedPoolAddress` will be absent. These tokens will only emit `Created` and `Updated` events. Check out the related endpoints and types: - [filterTokens](/api-reference/queries/filtertokens.md) — query tokens by launchpad stage - [token](/api-reference/queries/token.md) — get a token's launchpad data - [onLaunchpadTokenEvent](/api-reference/subscriptions/onlaunchpadtokenevent.md) — single token lifecycle events - [onLaunchpadTokenEventBatch](/api-reference/subscriptions/onlaunchpadtokeneventbatch.md) — batched lifecycle events - [LaunchpadTokenProtocol](/api-reference/enums/launchpadtokenprotocol.md) — supported protocols - [Launchpads recipe](/recipes/launchpads.md) — building a launchpad discovery UI # Discover Traders Understand how to find and filter high-performing wallets with the Codex API In this recipe we'll show you how to filter for high-performing wallets, rank them by the metrics that matter to you, and narrow results by identity, socials, and labels. This data powers the trader discovery experience on [re.defined.fi](https://re.defined.fi): ![A table of trader wallets on re.defined.fi showing PnL, volume, average hold period, win rate, and reputation columns](/images/wallets/discover_traders_redefined.png) Wallet timeframes (such as PnL and volume windows) are rolling periods. 1D is the last 24 hours, 1W is the last 7 days, and so on. They do not start at a fixed date or time of day. ## Filter by Performance Use `filterWallets` to surface wallets with strong trading performance. Combine numeric filters with a ranking to sort the results. All performance metrics are available across four windows: `1d`, `1w`, `30d`, `1y`. Append the window to the metric name (for example `realizedProfitUsd30d`, `winRate1w`, `volumeUsd1y`). [Test this query in the Explorer →](/explore.md) ```graphql theme={null} { filterWallets(input: { filters: { realizedProfitUsd30d: { gte: 10000 } } rankings: [{ attribute: realizedProfitUsd30d, direction: DESC }] limit: 10 }) { count results { address wallet { displayName ethosScore twitterUsername } } } } ``` For a trade to count toward win rate, profit or loss must exceed $1 USD. `volumeUsd` only counts volume from tokens with a reliable USD price. Use `volumeUsdAll` to include volume from tokens without one. ## Filter by Identity and Socials Narrow discovery to wallets that have linked social accounts or a set display name. This is useful for surfacing public, identifiable traders rather than anonymous addresses. All identity filters accept `true` (must have) or `false` (must not have): | Filter | What it checks | |---|---| | `hasTwitter` | Wallet has a linked Twitter/X account | | `hasDiscord` | Wallet has a linked Discord account | | `hasTelegram` | Wallet has a linked Telegram account | | `hasFarcaster` | Wallet has a linked Farcaster account | | `hasGithub` | Wallet has a linked GitHub account | | `hasDisplayName` | Wallet has a display name set | | `hasSocials` | Wallet has any linked social account | [Test this query in the Explorer →](/explore.md) ```graphql theme={null} { filterWallets(input: { filters: { hasTwitter: true hasDisplayName: true } rankings: [{ attribute: ethosScore, direction: DESC }] limit: 10 }) { count results { address wallet { displayName ethosScore twitterUsername } } } } ``` ## Sort by Ethos Credibility `ethosScore` (0 to 2800) is a credibility score you can use as a ranking attribute to surface reputable traders first. Combine it with identity filters to focus on wallets with both a public identity and a strong reputation. [Test this query in the Explorer →](/explore.md) ```graphql theme={null} { filterWallets(input: { filters: { hasSocials: true } rankings: [{ attribute: ethosScore, direction: DESC }] limit: 10 }) { count results { address wallet { ethosScore ethosLevel displayName } } } } ``` ## Sort and Filter by Average Hold Period `avgHoldPeriodSec` estimates how long a trader tends to hold tokens, using average-cost accounting: sells realize the age of the sold cost basis, so unsold holdings do not affect the result. It is a lower bound, capped at the window length, and returns null when under $1 of cost basis was sold in the window. It is available in four windows (`1d`, `1w`, `30d`, `1y`) and can be used as either a ranking attribute or a filter input. **Long-term conviction holders** (longest hold period first): [Test this query in the Explorer →](/explore.md) ```graphql theme={null} { filterWallets(input: { filters: { volumeUsd30d: { gte: 50000 } } rankings: [{ attribute: avgHoldPeriodSec30d, direction: DESC }] limit: 10 }) { count results { address wallet { displayName } } } } ``` **Quick flippers** (shortest hold period first), filtered to active traders: [Test this query in the Explorer →](/explore.md) ```graphql theme={null} { filterWallets(input: { filters: { volumeUsd30d: { gte: 50000 } } rankings: [{ attribute: avgHoldPeriodSec1d, direction: ASC }] limit: 10 }) { count results { address wallet { displayName } } } } ``` **Hold period as a filter** (traders that hold positions for more than 24 hours, sorted by profit). 86400 seconds = 24 hours: [Test this query in the Explorer →](/explore.md) ```graphql theme={null} { filterWallets(input: { filters: { avgHoldPeriodSec30d: { gt: 86400 } } rankings: [{ attribute: realizedProfitUsd30d, direction: DESC }] limit: 10 }) { count results { address wallet { displayName } } } } ``` Hold period is most useful displayed alongside other stats on a single trader's profile. See the [Trader Dashboard](/recipes/wallets/trader-dashboard.md) recipe for showing it on one wallet. ## Find Token-Specific Traders To find profitable traders of a specific token, use `filterTokenWallets`. Watch for wallets that appear across multiple token queries, which can indicate consistent performance. [Test this query in the Explorer →](/explore.md) ```graphql theme={null} { filterTokenWallets(input: { tokenIds: ["0xTOKEN_ADDRESS:NETWORK_ID"] rankings: [{ attribute: realizedProfitUsd30d, direction: DESC }] limit: 10 }) { count results { walletAddress tokenBalance tokenBalanceLive purchasedTokenBalance } } } ``` `filterTokenWallets` accepts up to 50 token IDs per query. If you pass more than one token ID, you must also include at least one wallet address. Records only update on swaps, so wallets that received tokens via transfer (airdrops, direct sends) will not appear. Use [`holders`](/api-reference/queries/holders.md) if you need an accurate holder list updated on every transfer. `tokenBalance` reflects the wallet's balance as of its last swap, while `tokenBalanceLive` is the most up-to-date balance — prefer `tokenBalanceLive` when you need a wallet's current holding. To narrow results to wallets that still hold the token, filter for a `tokenBalance` greater than 0. This surfaces current holders rather than everyone who has ever traded it, and can return more relevant records in a single request. ## Filter by Wallet Labels Codex surfaces two separate label systems. They come from different sources and may overlap on individual wallets, so treat them as distinct. ![The Labels & Scores filter panel on re.defined.fi showing both behavioral and identity label buttons, plus risk score and reputation sliders](/images/wallets/wallet_label_score_filters.png) ### Codex behavioral labels These are assigned by Codex based on a wallet's on-chain trading activity. Apply them through `includeLabels` (only wallets matching) or `excludeLabels` (wallets to remove) on `filterWallets`. The `WalletLabel` enum values: - **`INTERESTING`** — Wallet is interesting based on a number of factors - **`MEDIUM_WEALTHY`** — Wallet holds $5M+ in assets - **`MEGA_WEALTHY`** — Wallet holds $10M+ in assets - **`SMART_TRADER_TOKENS_OVER_TWO_DAYS_OLD`** — Over $7.5K profit in the last 90 days from tokens older than 2 days - **`SMART_TRADER_TOKENS_UNDER_TWO_DAYS_OLD`** — Over $5K profit in the last 90 days from tokens between 1 hour and 2 days old - **`SNIPER`** — Over $3K profit in the last 90 days from tokens launched within their first hour - **`WEALTHY`** — Wallet holds $1M+ in assets See the [WalletLabel enum reference](/api-reference/enums/walletlabel.md) for the complete list (including bot and scammer values used to filter low-quality wallets out of results). [Test this query in the Explorer →](/explore.md) ```graphql theme={null} { filterWallets(input: { filters: { includeLabels: [SMART_TRADER_TOKENS_OVER_TWO_DAYS_OLD] } rankings: [{ attribute: realizedProfitUsd30d, direction: DESC }] limit: 10 }) { count results { address labels wallet { displayName } } } } ``` ### Identity labels Codex also surfaces a separate set of curated identity labels from third-party data. These describe what the wallet is (CEX, KOL, founder, whale) rather than how it trades. They appear on the `wallet.identityLabels` array, and the full current vocabulary is returned by the [`walletLabelTypes`](/api-reference/queries/walletlabeltypes.md) query. See the reference page for the complete list with display names and descriptions. Three labels appear conceptually in both systems: `SNIPER`, `BOT`, and `SCAMMER`. They are curated separately (Codex on-chain analysis vs. third-party sources) and may flag overlapping but not identical sets of wallets. A wallet may carry the behavioral `SNIPER` label without the identity `SNIPER` label, or vice versa. ### Wallet category Distinct from both label systems, `wallet.category` returns a single structural classification of what kind of address a wallet is — for example `NORMIE`, `TOKEN_CREATOR`, `EXCHANGE`, `PAIR`, or `POOL_AUTHORITY`. Where labels describe how a wallet trades or who it is, `category` answers "what kind of address is this." See the [WalletCategory enum reference](/api-reference/enums/walletcategory.md) for the full set of values. ## Related Endpoints - [filterWallets](/api-reference/queries/filterwallets.md) - [filterTokenWallets](/api-reference/queries/filtertokenwallets.md) - [walletLabelTypes](/api-reference/queries/walletlabeltypes.md) - [holders](/api-reference/queries/holders.md) - [WalletLabel enum reference](/api-reference/enums/walletlabel.md) - [WalletCategory enum reference](/api-reference/enums/walletcategory.md) Ready to dig into a single wallet? Continue to the [Trader Dashboard](/recipes/wallets/trader-dashboard.md) recipe. # Trader Dashboard Understand how to build a complete single-wallet view with the Codex API Once you have a wallet of interest, this recipe shows how to pull together everything you would display on a trader profile or dashboard: identity, performance stats, PnL, charts, trading history, and current holdings. ![A complete trader profile on re.defined.fi showing the identity header, network breakdown, realized PnL chart, volume calendar heatmap, per-token stats, and recent activity feed](/images/wallets/trader_wallet_dashboard_redefined.png) Wallet timeframes (such as PnL and volume windows) are rolling periods. 1D is the last 24 hours, 1W is the last 7 days, and so on. They do not start at a fixed date or time of day. ## Identity and Stats Overview `detailedWalletStats` is the core endpoint for a dashboard. A single call returns both the wallet's identity (display name, Ethos credibility, linked socials, identity labels) and its performance stats across rolling time windows, broken down by network. This is what you render as the header and primary metrics of a trader profile. [Test this query in the Explorer →](/explore.md) ```graphql theme={null} { detailedWalletStats(input: { walletAddress: "0xADDRESS" includeNetworkBreakdown: true }) { walletAddress lastTransactionAt labels scammerScore botScore statsDay30 { statsUsd { volumeUsd realizedProfitUsd realizedProfitPercentage soldTokenAcquisitionCostUsd heldTokenAcquisitionCostUsd averageProfitUsdPerTrade } statsNonCurrency { swaps uniqueTokens wins losses avgHoldPeriodSec } } wallet { address displayName avatarUrl description ethosScore ethosLevel ethosVerified twitterUsername discordUsername telegramUsername farcasterUsername githubUsername identityLabels tokensCreatedCount tokensMigratedCount } } } ``` Available time windows: `statsDay1`, `statsWeek1`, `statsDay30`, `statsYear1`. Each provides the same field set for that rolling window. Request only the windows you need to display. `tokensCreatedCount` and `tokensMigratedCount` are lifetime totals across all networks — how many tokens the wallet has deployed, and how many of those graduated/migrated. They're useful for flagging serial deployers on a profile header. Both are response fields only, not filter inputs. Setting `includeNetworkBreakdown: true` returns per-network volume and hold period alongside the aggregate stats, which is useful when a wallet is active on multiple chains: ![A per-network breakdown on re.defined.fi showing volume share and average hold period for Ethereum, HyperEVM, BNB Chain, and others](/images/wallets/wallet_network_activity_redefined.png) **Hold period vs. hold time.** `avgHoldPeriodSec` estimates how long tokens are held using average-cost accounting: sells realize the age of the sold cost basis, so unsold holdings do not affect the result. It is a lower bound, capped at the window length, and returns null when under $1 of cost basis was sold in the window. Display it as an indicator of conviction, not a precise duration. ## Calculate Wallet PnL PnL (profit and loss) is central to a trader dashboard. For the concepts behind how Codex calculates it, see the [Wallet PnL](/concepts/wallet-pnl.md) page. This section covers the practical queries. ### Overall realized PnL The `detailedWalletStats` example above already returns realized PnL through `statsUsd.realizedProfitUsd` and `statsUsd.realizedProfitPercentage` for each window. ### Per-token PnL To see how a wallet performed on a specific token, use `filterTokenWallets` filtered to that wallet: [Test this query in the Explorer →](/explore.md) ```graphql theme={null} { filterTokenWallets(input: { tokenIds: ["0xTOKEN_ADDRESS:NETWORK_ID"] filtersV2: { walletAddress: { eq: "0xADDRESS" } } }) { results { walletAddress realizedProfitUsd30d tokenBalance tokenBalanceLive purchasedTokenBalance } } } ``` ### Unrealized PnL To estimate PnL on tokens still held, compare current value against acquisition cost: ``` Unrealized PnL = Current Balance Value - Acquisition Cost ``` Two fields from `filterTokenWallets` give you the inputs: - `tokenAcquisitionCostUsd` — what the wallet paid for tokens it still holds - Current value — multiply `tokenBalanceLive` by the token's current price (from [`getTokenPrices`](/api-reference/queries/gettokenprices.md)) For an aggregate cost basis across all held tokens, `heldTokenAcquisitionCostUsd` from `detailedWalletStats.statsUsd` is the single-field equivalent. ### PnL over time For charting PnL progression, use `walletChart`. The `realizedProfitUsd` field in each data point gives time-series PnL at your chosen resolution. See the [Visualize Wallet Activity](#visualize-wallet-activity) section below. ## Visualize Wallet Activity `walletChart` returns time-series data for rendering performance charts and heatmaps: PnL progression, trading volume, and swap counts over time. [Test this query in the Explorer →](/explore.md) ```graphql theme={null} { walletChart(input: { walletAddress: "0xADDRESS" networkId: 1 range: { start: 1739404800 end: 1742083200 resolution: "1D" } }) { walletAddress backfillState range { start end resolution } data { timestamp volumeUsd realizedProfitUsd swaps } } } ``` ![A realized PnL line chart on the left and a daily volume calendar heatmap on the right, both for a single wallet on re.defined.fi](/images/wallets/wallet_chart_heatmap_redefined.png) Available resolutions: `60` (1-hour candles), `240` (4-hour candles), `1D` (daily), `7D` (weekly). Choose a resolution appropriate for your time range. Omit `networkId` for cross-chain aggregated data. ## If Stats Look Empty If `detailedWalletStats` or `walletChart` return empty or partial data, the wallet's historical stats may still be processing. `walletChart` exposes the state directly in its response through `backfillState`. To check it ahead of time on any wallet, use `walletAggregateBackfillState`: [Test this query in the Explorer →](/explore.md) ```graphql theme={null} { walletAggregateBackfillState(input: { walletAddress: "0xADDRESS" }) { walletAddress status } } ``` The `status` enum has six values: | Status | What it means | |---|---| | `BackfillComplete` | Historical stats are ready | | `BackfillInProgress` | Currently processing, retry shortly | | `BackfillRequestReceived` | Queued, will start soon | | `BackfillNotFound` | Has not been started — trigger one with [`backfillWalletAggregates`](/api-reference/mutations/backfillwalletaggregates.md) | | `BackfillCanceled` | Started, then canceled. Wallet may be flagged as a bot | | `BackfillBlocked` | Blocked. Wallet may be flagged as a bot | ## Trading History For a per-transaction view of everything a wallet has done, use `getTokenEventsForMaker`. It returns the wallet's individual swap events with full detail, including the global fees paid data on each event. [Test this query in the Explorer →](/explore.md) ```graphql theme={null} { getTokenEventsForMaker(query: { maker: "0xADDRESS" networkId: 1 }) { items { transactionHash timestamp eventType eventDisplayType token0SwapValueUsd token1SwapValueUsd maker } } } ``` ![A scrolling trading history feed on re.defined.fi showing individual swap events with token name, USD amount, and token amount per transaction](/images/wallets/trading_history_redefined.png) A single transaction may return multiple events for multi-hop swaps (Token A → B → C). Disambiguate client-side using the transaction hash if you only want the end-to-end swap. ## Current Holdings Complete the dashboard with a portfolio breakdown. `balances` returns the tokens a wallet currently holds with current balance and metadata. Select `balanceUsd` for each holding's value, plus `liquidityUsd` and `tokenLastTradedTimestamp` so you can value the portfolio accurately rather than trusting a raw price on every token. [Test this query in the Explorer →](/explore.md) ```graphql theme={null} { balances(input: { walletAddress: "0xADDRESS" networks: [1, 8453] removeScams: true }) { items { tokenAddress balance shiftedBalance balanceUsd liquidityUsd tokenLastTradedTimestamp token { name symbol } } } } ``` Summing `balanceUsd` alone can overstate a wallet's worth, because a token can carry a price with almost no liquidity behind it. `liquidityUsd` is the token's route-backed liquidity in USD — the real liquidity supporting its price, summed across pairs with valid routing — and `tokenLastTradedTimestamp` is when it last traded. Discount or drop holdings below a liquidity threshold or with no recent trades before totaling the portfolio, so a handful of dead or illiquid tokens don't make a wallet look far richer than it is. ![A holdings table on re.defined.fi listing each token a wallet holds with amount, price, and USD value columns](/images/wallets/Holdings_redefined.png) ENS names are not supported on `balances`. For EVM wallets, native token balances require a network with traces enabled. On networks without traces, a native balance may still update when the wallet makes a swap, but without trace data those values can be updated inconsistently — treat them as approximate. Set `removeScams: true` to filter out tokens flagged as scams. ## Bring It Together A complete trader dashboard pulls from several endpoints: - **Identity and performance:** `detailedWalletStats` for display name, Ethos score, socials, identity labels, PnL, win rate, and hold period in one call - **Per-token detail:** `filterTokenWallets` for token-specific performance - **Visualization:** `walletChart` for time-series PnL, volume, and swap counts - **History:** `getTokenEventsForMaker` for the per-transaction feed - **Holdings:** `balances` for the current portfolio ## Related Endpoints - [detailedWalletStats](/api-reference/queries/detailedwalletstats.md) - [filterTokenWallets](/api-reference/queries/filtertokenwallets.md) - [walletChart](/api-reference/queries/walletchart.md) - [balances](/api-reference/queries/balances.md) - [getTokenEventsForMaker](/api-reference/queries/gettokeneventsformaker.md) - [walletAggregateBackfillState](/api-reference/queries/walletaggregatebackfillstate.md) - [backfillWalletAggregates](/api-reference/mutations/backfillwalletaggregates.md) - [getTokenPrices](/api-reference/queries/gettokenprices.md) # Prediction Event Dashboard Learn how to build a comprehensive prediction event detail page with markets, charts, trades, and real-time pricing In this recipe we'll walk through building a prediction event detail page, the kind of page Polymarket and Kalshi center their UX around. An event like "2024 Presidential Election" contains multiple related markets ("Who wins?", "Popular vote margin?", "Which states flip?"). We'll combine multiple Codex endpoints to populate every section: event metadata, market probabilities, multi-market charts, aggregated activity, trade history, and market drill-downs. ## Step 1: Event Metadata & Market List For the examples below to work, you'll need an ID of an event. You can get that by using [filterPredictionEvents](/api-reference/queries/filterpredictionevents.md). Take the ouput of that and replace the `eventId` value in the queries below. Unforunately, Prediction Markets are ever changing so it's hard for us to hardcode an example like we can with tokens. Start by fetching the event details and all its markets in a single call with [`detailedPredictionEventStats`](/api-reference/queries/detailedpredictioneventstats.md). This returns event metadata, the full list of markets, aggregated stats across time windows, and lifecycle information. [Test this query in the Explorer →](/explore.md) ```graphql theme={null} query DetailedPredictionEventStats { detailedPredictionEventStats(input: { eventId: "yourEventId" }) { eventId lastTransactionAt predictionEvent { id protocol venueEventId status question url rulesPrimary rulesSecondary tags opensAt closesAt resolvesAt resolvedAt resolution { result source } imageLargeUrl imageThumbUrl createdAt updatedAt networkId marketIds categories { name slug subcategories { name slug } } } predictionMarkets { id protocol venueMarketId eventId question label eventLabel outcomeLabels outcomeIds resolution { result source } imageThumbUrl createdAt opensAt closesAt resolvesAt resolvedAt networkId } statsDay1 { start end statsCurrency { volumeUsd volumeCT openLiquidityUsd closeLiquidityUsd openOpenInterestUsd closeOpenInterestUsd } statsNonCurrency { trades uniqueTraders } statsChange { volumeChange openLiquidityChange openOpenInterestChange tradesChange uniqueTradersChange } scores { trending relevance } } allTimeStats { volumeUsd volumeCT venueVolumeUsd venueVolumeCT } lifecycle { ageSeconds expectedLifespanSeconds timeToResolutionSeconds isResolved } } } ``` This is the core call that populates the event header, market list sidebar, and summary stats. It gives you everything you need to render the top of the page in one request. Stats are available at multiple windows. `statsDay1` is shown above, but `statsHour1`, `statsHour4`, `statsHour12`, `statsWeek1` follow the same structure. ## Step 2: Market Outcome Pricing To show current probabilities (best ask price) for each market's outcomes (the core of any prediction market UI), use [`filterPredictionMarkets`](/api-reference/queries/filterpredictionmarkets.md) scoped to the event. This returns real-time bid/ask pricing, spread, liquidity depth, and volume for each outcome. [Test this query in the Explorer →](/explore.md) ```graphql theme={null} query EventMarketPricing { filterPredictionMarkets( eventIds: ["yourEventId"] rankings: [ { outcome: outcome0, outcomeAttribute: bestAskCT, direction: DESC } ] ) { count results { id status market { id label question imageThumbUrl } outcome0 { label bestAskCT bestBidCT spreadCT lastPriceCT liquidityCT volumeUsd24h priceChange24h } outcome1 { label bestAskCT bestBidCT spreadCT lastPriceCT liquidityCT volumeUsd24h priceChange24h } volumeUsdAll liquidityUsd openInterestUsd } } } ``` - The `bestAskCT` price (in collateral token, e.g., USDC) represents the current implied probability (0.65 = 65% chance) - Ranking by outcome0's `bestAskCT` in DESC order puts the highest-probability outcomes at the top - You can also rank by market-level attributes like `volumeUsd24h` or `trendingScore24h` - The spread (`bestAskCT - bestBidCT`) indicates market efficiency - This is the data that powers the market list showing "Yes 65¢ / No 35¢" for each market ## Step 3: Multi-Market Probability Chart The signature visualization: a multi-line chart showing how probabilities for the top markets in an event have moved over time. Use [`predictionEventTopMarketsBars`](/api-reference/queries/predictioneventtopmarketsbars.md). [Test this query in the Explorer →](/explore.md) ```graphql theme={null} query EventTopMarketsBars { predictionEventTopMarketsBars( input: { eventId: "yourEventId" from: 1773100000 to: 1773700000 resolution: hour1 limit: 5 rankBy: "volumeUsd1w" rankDirection: "DESC" } ) { eventId predictionEvent { id question } marketBars { marketId predictionMarket { id label outcomeLabels } bars { t outcome0 { priceCollateralToken { o h l c } volumeUsd trades } outcome1 { priceCollateralToken { o h l c } } openInterestUsd { o h l c } } } } } ``` - Returns OHLC bars for the top N markets (up to 10) in a single request - `rankBy` determines which markets are "top". Use `volumeUsd1w` for most active, or rank by outcome attribute with `rankByOutcome` + `rankByOutcomeAttribute` - Plot `outcome0.priceCollateralToken.c` (close price) for each market as a line to get the multi-candidate probability chart - You can also pass explicit `marketIds` instead of using ranking to choose which markets to chart ## Step 4: Event-Level Aggregated Charts For charts showing aggregated activity across the entire event (total volume, liquidity, open interest over time), use [`predictionEventBars`](/api-reference/queries/predictioneventbars.md). [Test this query in the Explorer →](/explore.md) ```graphql theme={null} query EventBars { predictionEventBars( input: { eventId: "yourEventId" from: 1773100000 to: 1773700000 resolution: hour1 } ) { eventId bars { t volumeUsd buyVolumeUsd sellVolumeUsd totalVolumeUsd trades uniqueTraders liquidityUsd { o h l c } openInterestUsd { o h l c } } } } ``` - This aggregates data across ALL markets in the event - Use for volume bar charts, liquidity area charts, and open interest line charts - `buyVolumeUsd` / `sellVolumeUsd` breakdown enables buy/sell pressure analysis - `uniqueTraders` shows participation growth over time ## Step 5: Trade Feed Show recent trades across all markets in the event with [`predictionTrades`](/api-reference/queries/predictiontrades.md). [Test this query in the Explorer →](/explore.md) ```graphql theme={null} query EventTrades { predictionTrades(input: { eventId: "yourEventId", limit: 50 }) { items { marketId outcomeId protocol tradeType maker traderId timestamp outcomeIndex outcomeLabel priceUsd priceCollateral amount amountUsd transactionHash networkId } cursor } } ``` - Returns trades across all markets in the event, sorted by most recent - Each trade includes the `outcomeLabel` and `outcomeIndex` so you can show "Bought Yes @ $0.65" - `cursor` enables pagination for loading more trades - `traderId` can be used to link to trader profiles - Can also query by `marketId` for single-market trade feeds ## Step 6: Single Market Drill-Down When a user clicks on a specific market within the event, fetch detailed chart data and token holder information. [Test this query in the Explorer →](/explore.md) ```graphql theme={null} query MarketDrillDown { predictionMarketBars( input: { marketId: "yourMarketId" from: 1773100000 to: 1773700000 resolution: hour1 } ) { marketId bars { t volumeUsd trades uniqueTraders openInterestUsd { o h l c } outcome0 { priceCollateralToken { o h l c } liquidityCollateralToken { o h l c } bidCollateralToken { o h l c } askCollateralToken { o h l c } volumeUsd trades buys sells } outcome1 { priceCollateralToken { o h l c } liquidityCollateralToken { o h l c } bidCollateralToken { o h l c } askCollateralToken { o h l c } volumeUsd trades buys sells } } } } ``` [Test this query in the Explorer →](/explore.md) ```graphql theme={null} query TokenHolders { predictionTokenHolders( input: { marketId: "yourMarketId", tokenId: "yourTokenId", limit: 25 } ) { items { walletAddress amount predictionTrader { alias } } total cursor } } ``` **Getting the `tokenId`:** The `tokenId` is extracted from the `outcomeIds` array on the market object (returned in Step 1). Each `outcomeId` is a compound string like `46553455570564517989191023458705371521436514261892866503067981558938998232024:Polymarket:0xc5d563a36ae78145c45a50134d48a1215220f80a:137`. The `tokenId` is just the first segment before the first colon (e.g., `46553455570564517989191023458705371521436514261892866503067981558938998232024`). For a binary market, `outcomeIds[0]` is the "Yes" token and `outcomeIds[1]` is the "No" token. Query once per outcome to get holders for each side. - Market bars give per-outcome OHLC data for price, liquidity, bid, and ask, enabling candlestick charts and bid/ask spread visualization - Token holders shows who holds the most of each outcome token, with their trader alias if available - Token holder data is only available for Polymarket (on-chain ERC-1155 tokens on Polygon). Kalshi does not expose holder data. ## Putting It All Together Here's the recommended data flow for an event dashboard: **On page load (parallel queries):** 1. `detailedPredictionEventStats`: event metadata, market list, aggregated stats 2. `filterPredictionMarkets(eventIds)`: current outcome pricing for all markets 3. `predictionEventTopMarketsBars`: multi-market probability chart data 4. `predictionEventBars`: event-level volume/liquidity charts **After initial load:** 5. `predictionTrades(eventId)`: trade feed **On user interaction (drill-down):** 6. `predictionMarketBars`: when user clicks a specific market 7. `predictionTokenHolders`: when user views holders tab To alert on a market without polling — volume spikes, price moves, or trade-count bursts over rolling windows — set up a prediction market metrics webhook. See [Webhooks](/concepts/webhooks.md#prediction_market_metrics_event) for setup. **Optimizing calls:** Queries 1-4 are independent and can be run in parallel to minimize page load time. **Probabilities:** The `bestAskCT` or `priceCollateralToken.c` values represent implied probability when the collateral token is a stablecoin. A price of 0.65 means the market implies a 65% chance. **Multi-outcome events:** Each market is binary (two outcomes), but an event can have many markets. For a "Who wins the election?" event, each candidate gets their own market. **CT vs USD:** Collateral token values are more precise for prediction markets since they avoid exchange rate fluctuations. **Sorting the market list:** Rank by `outcome0.bestAskCT DESC` to show the highest-probability outcome first, or by `volumeUsd24h DESC` for most active. **Status values:** Events and markets can be `OPEN`, `SUSPENDED`, or `RESOLVED`. Check out the related endpoints in their respective API reference pages: - [detailedPredictionEventStats](/api-reference/queries/detailedpredictioneventstats.md) - [filterPredictionMarkets](/api-reference/queries/filterpredictionmarkets.md) - [predictionEventTopMarketsBars](/api-reference/queries/predictioneventtopmarketsbars.md) - [predictionEventBars](/api-reference/queries/predictioneventbars.md) - [predictionTrades](/api-reference/queries/predictiontrades.md) - [predictionMarketBars](/api-reference/queries/predictionmarketbars.md) - [predictionTokenHolders](/api-reference/queries/predictiontokenholders.md) # Discover Prediction Markets Learn how to build prediction market discovery pages with filtering, ranking, and search In this recipe, we'll show you how to use [`filterPredictionEvents`](/api-reference/queries/filterpredictionevents.md) and [`filterPredictionMarkets`](/api-reference/queries/filterpredictionmarkets.md) to build discovery pages for prediction markets: filterable, sortable lists that let users browse what's trending, search by topic, and find markets that match specific criteria. There are two levels of discovery: **events** (containers that group related markets, like "2024 Presidential Election") and **markets** (individual binary questions, like "Will candidate X win?"). A third query, `filterPredictionTraders`, powers trader leaderboards and is covered in the [Prediction Traders](/recipes/predictions/traders.md) recipe. ## Step 1: Browse the Category Taxonomy Before building filters, fetch the full category tree so you can populate category dropdowns and sidebar navigation. Categories are nested up to 5 levels deep (e.g., Sports > Football > NFL). [Test this query in the Explorer →](/explore.md) ```graphql theme={null} query PredictionCategories { predictionCategories { name slug subcategories { name slug subcategories { name slug } } } } ``` This is a lightweight call. Fetch once and cache client-side. Use the `slug` values when filtering events or markets by passing them in the `categories` filter. ## Step 2: Discover Trending Events Events are the primary discovery unit. They group related markets together (e.g., "2024 Presidential Election" contains markets for each candidate, state, etc.). Use [`filterPredictionEvents`](/api-reference/queries/filterpredictionevents.md) to build sortable, filterable event lists. [Test this query in the Explorer →](/explore.md) ```graphql theme={null} query TrendingEvents { filterPredictionEvents( filters: { protocol: [POLYMARKET], status: [OPEN] } rankings: [{ attribute: trendingScore24h, direction: DESC }] limit: 20 ) { count page results { id event { id protocol status slug question description imageThumbUrl venueUrl closesAt resolvesAt } status markets { id label } marketCount categories trendingScore24h relevanceScore24h liquidityUsd openInterestUsd volumeUsd24h volumeChange24h trades24h uniqueTraders24h } } } ``` [Test this query in the Explorer →](/explore.md) ```graphql theme={null} query SportsEvents { filterPredictionEvents( filters: { protocol: [POLYMARKET], status: [OPEN], categories: ["sports"] } rankings: [{ attribute: volumeUsd24h, direction: DESC }] limit: 20 ) { count results { id event { question imageThumbUrl closesAt } marketCount categories volumeUsd24h volumeChange24h trades24h liquidityUsd openInterestUsd trendingScore24h } } } ``` [Test this query in the Explorer →](/explore.md) ```graphql theme={null} query SearchEvents { filterPredictionEvents( phrase: "election" filters: { status: [OPEN] } rankings: [{ attribute: relevanceScore24h, direction: DESC }] limit: 20 ) { count results { id event { question description imageThumbUrl } marketCount relevanceScore24h volumeUsd24h liquidityUsd } } } ``` **Scoring explained (events):** - `trendingScore24h` combines volume, trades, and momentum, making it best for "what's hot right now" - `relevanceScore24h` factors in liquidity and market maturity, making it best for "most important" - `competitiveScore24h` is only available on **markets** (via `filterPredictionMarkets`), not events, since competitiveness measures how close a market's outcomes are to each other. All score, volume, and trade metrics are available at 5m, 1h, 4h, 12h, 24h, and 1w windows. Use `relatedEventIds` to build "Related Events" sections. ## Step 3: Discover Individual Markets For more granular discovery, filter at the individual market level with [`filterPredictionMarkets`](/api-reference/queries/filterpredictionmarkets.md). This is useful for showing specific binary questions across events, or building market-level leaderboards. [Test this query in the Explorer →](/explore.md) ```graphql theme={null} query CompetitiveMarkets { filterPredictionMarkets( filters: { status: [OPEN] } rankings: [{ attribute: competitiveScore24h, direction: DESC }] limit: 20 ) { count results { id eventLabel market { id eventId protocol label question imageThumbUrl status closesAt } status outcome0 { label bestAskCT bestBidCT spreadCT lastPriceCT liquidityCT volumeUsd24h priceChange24h } outcome1 { label bestAskCT bestBidCT spreadCT lastPriceCT liquidityCT volumeUsd24h priceChange24h } competitiveScore24h trendingScore24h liquidityUsd openInterestUsd volumeUsd24h trades24h priceCompetitiveness } } } ``` Use event IDs from the events query above (e.g., `event.id`). [Test this query in the Explorer →](/explore.md) ```graphql theme={null} query EventMarkets { filterPredictionMarkets( eventIds: ["yourEventId"] rankings: [ { outcome: outcome0, outcomeAttribute: bestAskCT, direction: DESC } ] limit: 50 ) { count results { id market { id label question } outcome0 { label bestAskCT bestBidCT lastPriceCT volumeUsd24h } outcome1 { label bestAskCT bestBidCT lastPriceCT volumeUsd24h } volumeUsdAll liquidityUsd openInterestUsd } } } ``` [Test this query in the Explorer →](/explore.md) ```graphql theme={null} query ClosingSoon { filterPredictionMarkets( filters: { status: [OPEN], closesAt: { lte: 1773700000 } } rankings: [{ attribute: openInterestUsd, direction: DESC }] limit: 20 ) { count results { id market { id label question closesAt resolvesAt } outcome0 { label bestAskCT lastPriceCT } outcome1 { label bestAskCT lastPriceCT } openInterestUsd liquidityUsd volumeUsd24h } } } ``` **Market-level vs outcome-level ranking:** Use `attribute` for market-wide metrics (volume, trending score) or `outcome` + `outcomeAttribute` for outcome-specific metrics (best ask price, liquidity per outcome). - `bestAskCT` is the implied probability when collateral is a stablecoin (0.65 = 65% chance) - `spreadCT` = `bestAskCT - bestBidCT`. Tighter spreads indicate more efficient markets - `priceCompetitiveness` measures how close outcome prices are to each other - `volumeImbalance24h` shows buy/sell pressure asymmetry ## Step 4: Building Filter UIs Combine the above into a practical filter interface: **Category navigation:** Use `predictionCategories` to build sidebar/tabs, pass selected `slug` into `filters.categories` **Status tabs:** OPEN | RESOLVED | all. Map to `filters.status` using [`PredictionEventStatus`](/api-reference/enums/predictioneventstatus.md) values **Sort dropdown: map user-friendly labels to ranking attributes:** - "Trending" → `trendingScore24h` DESC - "Most Volume" → `volumeUsd24h` DESC - "Most Liquidity" → `liquidityUsd` DESC - "Newest" → `age` ASC - "Closing Soon" → `closesAt` ASC (with status OPEN filter) - "Most Competitive" → `competitiveScore24h` DESC (markets only, via `filterPredictionMarkets`) **Time window selector:** All metrics are available at 5m, 1h, 4h, 12h, 24h, 1w. Let users toggle the time window for scores and volume displays. **Pagination:** Use `offset` and `limit` for page-based navigation. `count` in the response gives the total matching results. ## Step 5: Combining Events and Markets The recommended pattern for a discovery page that shows events with their top markets inline: 1. Fetch events with `filterPredictionEvents` (gives you `marketCount` and basic market IDs) 2. For each displayed event, fetch market pricing with `filterPredictionMarkets(eventIds: [eventId])` ranked by `outcome0.bestAskCT` DESC 3. Display as: Event card → list of markets with Yes/No prices This two-query approach avoids over-fetching while giving complete pricing data. **Protocol filter:** Use `protocol: [POLYMARKET]` or `protocol: [KALSHI]` to scope to a specific venue. **Scores are pre-computed:** Trending, relevance, and competitive scores are calculated server-side, so there's no need to compute them yourself. **Events vs markets:** Start with events for the main discovery page, use markets for drill-down or when you need outcome-level data. **Performance:** `filterPredictionEvents` and `filterPredictionMarkets` are optimized for fast reads from a search index, making them safe to call on every filter change. Check out related endpoints in their respective API reference pages: - [filterPredictionEvents](/api-reference/queries/filterpredictionevents.md) - [filterPredictionMarkets](/api-reference/queries/filterpredictionmarkets.md) - [predictionCategories](/api-reference/queries/predictioncategories.md) - [filterPredictionTraders](/api-reference/queries/filterpredictiontraders.md) # Prediction Charts Learn how to render prediction market charts with outcome probabilities, event activity, and multi-market comparisons In this recipe we'll show you how to fetch and render prediction market chart data at three levels: individual market outcome charts, event-level aggregated charts, and multi-market comparison charts within an event. Prediction market charts differ from token charts: instead of a single price, you have dual outcome probabilities, bid/ask spreads, and market-level metrics like open interest. Three chart queries are available: - **`predictionMarketBars`**: single market, per-outcome OHLC - **`predictionEventBars`**: event-level aggregated volume/liquidity/OI - **`predictionEventTopMarketsBars`**: multi-market probability comparison All support resolutions: `min1`, `min5`, `min15`, `min30`, `hour1`, `hour4`, `hour12`, `day1`, `week1`. ## Step 1: Single Market Outcome Charts The core chart for any prediction market detail page. It shows how each outcome's probability has moved over time with full OHLC data. Use [`predictionMarketBars`](/api-reference/queries/predictionmarketbars.md) to fetch per-outcome price, liquidity, bid/ask, and volume data. You will need a market ID to fetch charts. Use [filterPredictionMarkets](/api-reference/queries/filterpredictionmarkets.md) to grab a market ID. [Test this query in the Explorer →](/explore.md) ```graphql theme={null} query PredictionMarketBars { predictionMarketBars( input: { marketId: "yourMarketId" from: 1773100000 to: 1773700000 resolution: hour1 } ) { marketId predictionMarket { id label question outcomeLabels eventLabel } bars { t volumeUsd volumeCollateralToken trades uniqueTraders openInterestUsd { o h l c } outcome0 { trades buys sells volumeUsd buyVolumeUsd sellVolumeUsd priceUsd { o h l c } priceCollateralToken { o h l c } liquidityCollateralToken { o h l c } bidCollateralToken { o h l c } askCollateralToken { o h l c } } outcome1 { trades buys sells volumeUsd buyVolumeUsd sellVolumeUsd priceUsd { o h l c } priceCollateralToken { o h l c } liquidityCollateralToken { o h l c } bidCollateralToken { o h l c } askCollateralToken { o h l c } } } } } ``` **Chart types you can render from this data:** **Outcome probability (candlestick or line):** - Use `outcome0.priceCollateralToken` / `outcome1.priceCollateralToken` for OHLC candlestick charts - The close price (`c`) represents the current implied probability (e.g., 0.65 = 65%) - For a simpler line chart, just plot the `c` (close) values over time **Bid/Ask spread (dual line):** - Plot `outcome0.bidCollateralToken.c` and `outcome0.askCollateralToken.c` as two lines - The gap between them is the spread. Tighter = more liquid/efficient market **Volume (bar chart):** - `volumeUsd` at the market level for total volume per bar - Per-outcome volume with `outcome0.volumeUsd` / `outcome1.volumeUsd` - Buy/sell breakdown with `buyVolumeUsd` / `sellVolumeUsd` **Open interest (OHLC):** - `openInterestUsd` at the market level shows total capital at risk **Activity (line):** - `trades` and `uniqueTraders` per bar **Additional parameters:** - `countback`: Instead of `from`, specify "give me the last N bars from `to`", which is useful for "show last 100 candles" - `removeEmptyBars`: Skip bars with no trading activity (useful for illiquid markets) ## Step 2: Multi-Market Probability Comparison The signature prediction market chart: multiple outcome lines on one chart, like Polymarket's multi-candidate probability view. Use [`predictionEventTopMarketsBars`](/api-reference/queries/predictioneventtopmarketsbars.md) to get bars for the top markets in an event in a single request. [Test this query in the Explorer →](/explore.md) ```graphql theme={null} query TopMarketsByVolume { predictionEventTopMarketsBars( input: { eventId: "yourEventId" from: 1773100000 to: 1773700000 resolution: hour1 limit: 5 rankBy: "volumeUsd1w" rankDirection: "DESC" } ) { eventId predictionEvent { id question } marketBars { marketId predictionMarket { id label outcomeLabels } bars { t outcome0 { priceCollateralToken { o h l c } volumeUsd trades } outcome1 { priceCollateralToken { o h l c } } openInterestUsd { o h l c } volumeUsd } } } } ``` **How to use this data:** - Returns OHLC bars for up to 10 markets in a single request - Each market in `marketBars` has its own `bars` array - Plot `outcome0.priceCollateralToken.c` for each market as a separate colored line, labeled with `predictionMarket.label` - **Ranking options:** `rankBy` for market-level attributes (`volumeUsd1w`, `trendingScore24h`, etc.) or `rankByOutcome` + `rankByOutcomeAttribute` for outcome-level (`bestAskCT`, `lastPriceUsd`, etc.) - `marketIds` overrides ranking. Use it when you want specific markets regardless of rank ## Step 3: Event-Level Aggregated Charts Shows aggregated activity across ALL markets in an event. This is useful for volume, liquidity, and open interest charts at the event level. Use [`predictionEventBars`](/api-reference/queries/predictioneventbars.md). [Test this query in the Explorer →](/explore.md) ```graphql theme={null} query PredictionEventBars { predictionEventBars( input: { eventId: "yourEventId" from: 1773100000 to: 1773700000 resolution: "day1" } ) { eventId predictionEvent { id question marketIds } bars { t volumeUsd buyVolumeUsd sellVolumeUsd totalVolumeUsd trades uniqueTraders liquidityUsd { o h l c } openInterestUsd { o h l c } } } } ``` **Chart types from this data:** - **Volume bar chart:** `volumeUsd` per bar, optionally split into `buyVolumeUsd` / `sellVolumeUsd` for buy/sell pressure visualization - **Liquidity area chart:** `liquidityUsd` OHLC. Use `c` for a simple line, or shade between `l` and `h` for a range - **Open interest line:** `openInterestUsd` OHLC. Shows total capital committed across all markets - **Activity line:** `trades` and `uniqueTraders` per bar - **Cumulative volume:** `totalVolumeUsd` is the running total This query does NOT include per-outcome price data. Use `predictionEventTopMarketsBars` for that. ## Step 4: Rendering with a Chart Library Transform the API response into chart-ready data. This approach works with any library (lightweight-charts, TradingView, Chart.js, etc.): **Candlestick data (for outcome prices):** ```typescript // Transform outcome OHLC bars for a candlestick chart const candlestickData = bars.map(bar => ({ time: bar.t, // unix timestamp open: parseFloat(bar.outcome0.priceCollateralToken.o), high: parseFloat(bar.outcome0.priceCollateralToken.h), low: parseFloat(bar.outcome0.priceCollateralToken.l), close: parseFloat(bar.outcome0.priceCollateralToken.c), })); ``` **Multi-line data (for probability comparison):** ```typescript // Transform multiple markets into line series const series = marketBars.map(mb => ({ label: mb.predictionMarket.label, data: mb.bars.map(bar => ({ time: bar.t, value: parseFloat(bar.outcome0.priceCollateralToken.c), })), })); ``` **Volume bars:** ```typescript // Transform volume data for bar chart overlay const volumeData = bars.map(bar => ({ time: bar.t, value: parseFloat(bar.volumeUsd), color: parseFloat(bar.buyVolumeUsd) > parseFloat(bar.sellVolumeUsd) ? '#22c55e' // green = net buying : '#ef4444', // red = net selling })); ``` ## Step 5: Resolution Selection | Time range displayed | Recommended resolution | |---|---| | Last hour | min1 | | Last 4 hours | min5 | | Last 24 hours | min15 or min30 | | Last week | hour1 | | Last month | hour4 or hour12 | | Last 3 months | day1 | | All time | day1 or week1 | - Higher resolutions (min1, min5) give more granular data but return more bars - For markets with low activity, use `removeEmptyBars: true` to skip empty candles - Use `countback` instead of `from` when you want a fixed number of bars regardless of time range ## Putting It All Together Recommended chart layout for a prediction event page: 1. **Top chart:** Multi-market probability lines via `predictionEventTopMarketsBars`, the hero visualization 2. **Below:** Volume bar chart via `predictionEventBars`, showing overall event activity 3. **Drill-down:** When user clicks a specific market, show its detailed OHLC chart via `predictionMarketBars` with metric selector (price, liquidity, bid/ask, volume, OI) All three queries accept the same resolution values, so a single resolution picker can control all charts on the page. **Collateral Token (CT) vs USD:** CT values (e.g., `priceCollateralToken`) are preferred for prediction markets since the collateral is typically a stablecoin. USD values include exchange rate fluctuations. **Implied probability:** For stablecoin-collateral markets, `priceCollateralToken.c = 0.65` means the market implies a 65% probability. **OHLC fields:** `o` = open, `h` = high, `l` = low, `c` = close. Standard candlestick format. All values are strings that should be parsed to floats. **Timestamps:** The `t` field is a unix timestamp in seconds. Chart libraries may need milliseconds, so multiply by 1000. **Empty bars:** Illiquid markets may have bars with no trades. Use `removeEmptyBars` to skip them, or fill forward with the previous bar's close value for continuous lines. Check out the related chart endpoints in their API reference pages: - [predictionMarketBars](/api-reference/queries/predictionmarketbars.md) - [predictionEventBars](/api-reference/queries/predictioneventbars.md) - [predictionEventTopMarketsBars](/api-reference/queries/predictioneventtopmarketsbars.md) # Prediction Traders Learn how to build trader leaderboards, profiles, and portfolio analytics for prediction market traders In this recipe we'll show you how to discover top prediction market traders, display detailed trader profiles with performance stats, show their market positions and P&L, and chart their trading activity over time. Prediction market traders have rich on-chain performance data: win rate, P&L, volume, position sizing, and per-market breakdowns. This recipe covers 5 queries that work together: discover traders → view profile → see positions → chart performance → trade history. ## Step 1: Trader Leaderboard Build a sortable trader leaderboard with performance metrics across configurable time windows using [`filterPredictionTraders`](/api-reference/queries/filterpredictiontraders.md). [Test this query in the Explorer →](/explore.md) ```graphql theme={null} query TopTraders { filterPredictionTraders( rankings: [{ attribute: TOTAL_PROFIT_CT_ALL, direction: DESC }] limit: 25 ) { count page results { id trader { id venueTraderId protocol alias primaryAddress profileImageUrl profileUrl labels } totalVolumeUsdAll totalProfitUsdAll totalProfitCTAll totalTradesAll activeMarketsCount pnlPerVolumeAll biggestWinUsd biggestLossUsd firstTradeTimestamp lastTradeTimestamp volumeUsd24h realizedPnlUsd24h realizedProfitPercentage24h trades24h winRate24h heldTokenAcquisitionCostUsd } } } ``` [Test this query in the Explorer →](/explore.md) ```graphql theme={null} query BestWinRate { filterPredictionTraders( filters: { totalVolumeUsdAll: { gte: 10000 } } rankings: [{ attribute: WIN_RATE_1W, direction: DESC }] limit: 25 ) { count results { id trader { alias primaryAddress profileImageUrl } totalVolumeUsdAll totalProfitCTAll winRate24h trades24h realizedPnlUsd24h } } } ``` [Test this query in the Explorer →](/explore.md) ```graphql theme={null} query HighestPnL { filterPredictionTraders( rankings: [{ attribute: REALIZED_PNL_USD_24H, direction: DESC }] limit: 25 ) { count results { id trader { alias primaryAddress profileImageUrl } realizedPnlUsd24h realizedPnlCT24h realizedProfitPercentage24h volumeUsd24h trades24h winRate24h } } } ``` [Test this query in the Explorer →](/explore.md) ```graphql theme={null} query SearchTrader { filterPredictionTraders(phrase: "HorizonSplendidView", limit: 10) { count results { id trader { alias primaryAddress profileImageUrl labels } totalVolumeUsdAll totalProfitCTAll winRate24h } } } ``` **Ranking attributes** are available at multiple windows: 12h, 24h, 1w, 1m, and "All" (all-time). **Volume filters** (e.g., `totalVolumeUsdAll: { gte: 10000 }`) are essential for meaningful leaderboards. Filter out low-activity traders. **P&L per volume** (`pnlPerVolumeAll`) normalizes profit by capital deployed, making it better for comparing traders of different sizes. **Search** matches against alias, primary address, or venue trader ID. ## Step 2: Trader Profile & Detailed Stats Fetch comprehensive stats for a single trader, broken down by time window, using [`detailedPredictionTraderStats`](/api-reference/queries/detailedpredictiontraderstats.md). Grab the trader ID from the previous queries to use here. We've used a sample trader ID for this example. Feel free to change it. [Test this query in the Explorer →](/explore.md) ```graphql theme={null} query TraderProfile { detailedPredictionTraderStats( input: { traderId: "0x02227b8f5a9636e895607edd3185ed6ee5598ff7:Polymarket" } ) { traderId lastTransactionAt trader { id protocol venueTraderId alias primaryAddress linkedAddresses profileImageUrl profileUrl labels totalVolumeUsd totalVolumeCT allTimeProfitUsd allTimeProfitCT biggestWinUsd biggestWinCT biggestLossUsd biggestLossCT totalTradesCount activeMarketsCount firstTradeTimestamp lastTradeTimestamp } allTimeStats { totalVolumeUsd totalVolumeCT totalProfitUsd totalProfitCT } statsDay1 { start end lastTransactionAt statsCurrency { volumeUsd volumeCT buyVolumeUsd sellVolumeUsd realizedPnlUsd realizedPnlCT averageSwapAmountUsd averageProfitUsdPerTrade realizedProfitPercentage heldTokenAcquisitionCostUsd soldTokenAcquisitionCostUsd } statsNonCurrency { trades buys sells wins losses uniqueMarkets } statsChange { volumeChange realizedPnlChange tradesChange winsChange lossesChange uniqueMarketsChange } } } } ``` - The `trader` object has the all-time aggregate profile: total volume, profit, biggest win/loss, trade count, active markets - Windowed stats (`statsHour1` through `statsDay30`) give recent performance breakdowns - `statsCurrency` has all the monetary metrics, `statsNonCurrency` has counts, `statsChange` has period-over-period changes - `heldTokenAcquisitionCostUsd` / `soldTokenAcquisitionCostUsd` show the cost basis of currently held and already-sold positions - `realizedProfitPercentage` is the return on capital for the window - Use the windowed stats to build a time-period toggle (1h, 4h, 12h, 24h, 1w, 30d) on the trader profile Each toggle interval maps to one exact field name: | Time window | Field name | | --- | --- | | 1 hour | `statsHour1` | | 4 hours | `statsHour4` | | 12 hours | `statsHour12` | | 24 hours | `statsDay1` | | 1 week | `statsWeek1` | | 30 days | `statsDay30` | | All-time | `allTimeStats` | Note the one-week window is `statsWeek1`, not `statsDay7`. Note the 30-day window is `statsDay30`, not `statsMonth1`. These six windowed fields plus `allTimeStats` are the complete set. There is no `statsDay7`, `statsDay3`, `statsDay14`, or `statsMonth1`. ## Step 3: Trader's Market Positions Show which markets a trader is active in, their positions, and per-market P&L with [`filterPredictionTraderMarkets`](/api-reference/queries/filterpredictiontradermarkets.md). Grab the trader ID, market ID, or event ID from the previous queries to use here. Or use the corresponding [`filterPredictionMarkets`](/api-reference/queries/filterpredictionmarkets.md), [`filterPredictionEvents`](/api-reference/queries/filterpredictionevents.md), or [`filterPredictionTraders`](/api-reference/queries/filterpredictiontraders.md) queries to find relevant IDs. [Test this query in the Explorer →](/explore.md) ```graphql theme={null} query OpenPositions { filterPredictionTraderMarkets( traderIds: ["0x02227b8f5a9636e895607edd3185ed6ee5598ff7:Polymarket"] filters: { hasOpenPosition: true } rankings: [{ attribute: totalVolumeUsd, direction: DESC }] limit: 25 ) { count results { id traderId marketId eventId market { id eventId label question eventLabel imageThumbUrl outcome0Label outcome1Label } hasOpenPosition totalRealizedPnlUsd totalRealizedPnlCT totalVolumeUsd totalTrades totalCostBasisUsd totalSharesHeld pnlPerVolumeMarket outcome0 { outcomeId isWinningOutcome sharesHeld avgEntryPriceUsd avgEntryPriceCT costBasisUsd buys sells buyVolumeUsd sellVolumeUsd realizedPnlUsd pnlStatus } outcome1 { outcomeId isWinningOutcome sharesHeld avgEntryPriceCT costBasisUsd buys sells realizedPnlUsd pnlStatus } } } } ``` [Test this query in the Explorer →](/explore.md) ```graphql theme={null} query MarketTopTraders { filterPredictionTraderMarkets( marketIds: ["yourMarketId"] rankings: [{ attribute: totalVolumeUsd, direction: DESC }] limit: 25 ) { count results { id traderId marketId market { id label question eventLabel } totalRealizedPnlUsd totalVolumeUsd totalTrades hasOpenPosition outcome0 { sharesHeld avgEntryPriceCT realizedPnlUsd pnlStatus } outcome1 { sharesHeld avgEntryPriceCT realizedPnlUsd pnlStatus } } } } ``` [Test this query in the Explorer →](/explore.md) ```graphql theme={null} query EventTopTraders { filterPredictionTraderMarkets( eventIds: ["yourEventId"] rankings: [{ attribute: totalVolumeUsd, direction: DESC }] limit: 25 ) { count results { id traderId marketId market { id label eventLabel } totalRealizedPnlUsd totalVolumeUsd hasOpenPosition } } } ``` **Per-outcome position data:** Each result has `outcome0` and `outcome1` with shares held, avg entry price, cost basis, realized P&L. **`hasOpenPosition`:** Filter to show only active positions vs historical ones. **`pnlStatus`:** Indicates the P&L state for the outcome (e.g., PROFIT, LOSS). **`avgEntryPriceCT`:** The average price the trader paid. Compare with current market price to estimate unrealized P&L. **Cross-referencing:** Use `marketIds` to find top traders for a market, or `eventIds` for an event. This powers "Top Traders" tabs on market/event pages. You can also use `predictionTraderMarketsStats` as a simpler alternative for fetching a trader's per-market stats without the filtering/ranking system: Grab the trader ID from the previous queries to use here. We've used a sample trader ID for this example. Feel free to change it. [Test this query in the Explorer →](/explore.md) ```graphql theme={null} query TraderPositions { predictionTraderMarketsStats( input: { traderId: "0x02227b8f5a9636e895607edd3185ed6ee5598ff7:Polymarket" limit: 25 } ) { items { traderId marketId hasOpenPosition predictionMarket { id label question eventLabel outcomeLabels winningOutcomeId resolution { result source } } outcome0Stats { sharesHeld avgEntryPriceCT costBasisCT realizedPnlCT pnlStatus buys sells buyVolumeCT sellVolumeCT } outcome1Stats { sharesHeld avgEntryPriceCT costBasisCT realizedPnlCT pnlStatus buys sells buyVolumeCT sellVolumeCT } } cursor } } ``` ## Step 4: Trader Performance Charts Chart a trader's performance over time (volume, P&L, trade activity, and cumulative profit) using [`predictionTraderBars`](/api-reference/queries/predictiontraderbars.md). Grab the trader ID from the previous queries to use here. We've used a sample trader ID for this example. Feel free to change it. [Test this query in the Explorer →](/explore.md) ```graphql theme={null} query TraderPerformance { predictionTraderBars( input: { traderId: "0x02227b8f5a9636e895607edd3185ed6ee5598ff7:Polymarket" from: 1773100000 to: 1773700000 resolution: day1 } ) { traderId trader { id alias primaryAddress profileImageUrl } bars { t trades buys sells uniqueMarkets volumeUsd volumeCT buyVolumeUsd sellVolumeUsd wins losses realizedPnlUsd realizedPnlCT cumulativeRealizedPnlUsd cumulativeRealizedPnlCT } } } ``` **Chart types from this data:** **Cumulative P&L (line chart, the hero chart):** - Plot `cumulativeRealizedPnlCT` over time. This is the signature trader performance chart - Shows the running total of realized profit/loss - Color the line green when positive, red when negative **Period P&L (bar chart):** - `realizedPnlUsd` per bar. Green bars for profit, red for loss - Pair with the cumulative line above for a combo chart **Volume (bar chart):** - `volumeUsd` or split into `buyVolumeUsd` / `sellVolumeUsd` **Win/Loss (stacked bar):** - `wins` and `losses` per bar. Stacked or grouped bars showing resolution outcomes **Activity (line):** - `trades`, `buys`, `sells`: trade frequency - `uniqueMarkets`: how diversified the trader is per period **Resolutions available:** HOUR1, HOUR4, DAY1, WEEK1 ## Step 5: Trader's Trade History Show the individual trade-by-trade history for a trader with [`predictionTrades`](/api-reference/queries/predictiontrades.md). Grab the trader ID from the previous queries to use here. We've used a sample trader ID for this example. Feel free to change it. [Test this query in the Explorer →](/explore.md) ```graphql theme={null} query TraderTrades { predictionTrades( input: { traderId: "0x02227b8f5a9636e895607edd3185ed6ee5598ff7:Polymarket" limit: 50 } ) { items { marketId outcomeId protocol tradeType maker traderId timestamp outcomeIndex outcomeLabel priceUsd priceCollateral amount amountCollateral amountUsd transactionHash blockNumber networkId predictionMarket { id label question eventLabel outcomeLabels } } cursor } } ``` - Returns individual trades with full context: market, outcome, price, size, direction - `tradeType` indicates BUY or SELL - `outcomeLabel` shows which outcome was traded (e.g., "Yes" or "No") - `priceCollateral` is the price paid per share - `amount` is the number of shares, `amountUsd` is the USD value - `cursor` enables pagination for loading more trades - Include `predictionMarket` to show market context alongside each trade ## Putting It All Together Here's the recommended data flow: **For a trader profile page, on page load (parallel queries):** 1. `detailedPredictionTraderStats`: profile header, summary stats, windowed performance 2. `filterPredictionTraderMarkets(traderIds, hasOpenPosition: true)`: active positions table 3. `predictionTraderBars`: cumulative P&L chart **On tab switch:** 4. `filterPredictionTraderMarkets(traderIds)`: all positions (when user switches to "All Positions" tab) 5. `predictionTrades(traderId)`: trade history (when user switches to "Trades" tab) **For a leaderboard page:** 1. `filterPredictionTraders`: the main ranked list with configurable sort 2. Click a trader → navigate to their profile using the data flow above **Trader IDs:** Format is typically `protocol:address` (e.g., `Polymarket:0x1234...`). **CT vs USD:** Collateral token (CT) values are preferred for prediction market P&L since the collateral is usually a stablecoin. **Unrealized P&L:** The API provides realized P&L. To estimate unrealized, compare `avgEntryPriceCT` from positions with current market price from `filterPredictionMarkets`. **Win rate context:** Always show win rate alongside trade count. A 100% win rate on 2 trades is less meaningful than 60% on 500 trades. **Position sizing:** `totalCostBasisUsd` shows how much capital a trader has deployed in a market, providing useful context alongside P&L. **Cross-query use:** `filterPredictionTraderMarkets` works bidirectionally. Filter by `traderIds` for a trader's portfolio, or by `marketIds`/`eventIds` to find top traders for a market/event. Check out the related endpoints in their respective API reference pages: - [filterPredictionTraders](/api-reference/queries/filterpredictiontraders.md) - [detailedPredictionTraderStats](/api-reference/queries/detailedpredictiontraderstats.md) - [filterPredictionTraderMarkets](/api-reference/queries/filterpredictiontradermarkets.md) - [predictionTraderMarketsStats](/api-reference/queries/predictiontradermarketsstats.md) - [predictionTraderBars](/api-reference/queries/predictiontraderbars.md) - [predictionTrades](/api-reference/queries/predictiontrades.md) # Migration Guides Move from another onchain data provider to Codex without rewriting your product Migration Guides help teams move from another onchain data API to Codex with as little friction as possible. Each guide is written for engineers who already have working code against a different provider and want a clear, endpoint-by-endpoint path to the Codex equivalent. Every guide in this section follows the same shape: - **Mental model**: how Codex's API differs from the source provider so you know what to expect. - **Endpoint mapping**: a table that pairs each source endpoint with the closest Codex query, subscription, or webhook. - **Side-by-side examples**: working request/response comparisons for the most common patterns. - **Gaps and gains**: things the source provider does that Codex doesn't (yet), plus the capabilities you pick up by moving. - **AI migration prompt**: a copy-paste prompt you can hand to an LLM to translate your existing integration. ## Available guides | Source | Status | Guide | | :-- | :-- | :-- | | Birdeye | Live | [Birdeye to Codex](/migrations/birdeye.md) | | Bitquery | Live | [Bitquery to Codex](/migrations/bitquery.md) | | CoinGecko | Live | [CoinGecko to Codex](/migrations/coingecko.md) | | Dune Sim | Live | [Dune Sim to Codex](/migrations/dune-sim.md) | **Note:** Looking for a migration guide for a different provider? Reach out to us [via email](mailto:hello@codex.io?subject=Migration%20Guide%20Request) and we'll add it to our queue. ## Before you start No matter which provider you're moving from, two things are worth doing first: 1. **Get an API key.** Codex authenticates every request with an API key from the [dashboard](https://dashboard.codex.io?utm_source=codex&utm_medium=docs&utm_campaign=migrations-overview). See [Authentication](/concepts/authentication.md) for the request format and short-lived key flow. 2. **Skim [Queries](/concepts/queries.md) and [Subscriptions](/concepts/subscriptions.md).** Codex is a GraphQL API with WebSocket subscriptions, not a REST API. If your existing integration is REST-only, the shape of requests will change even where the data is identical. If you hit something a guide doesn't cover, the [FAQ](/extra/faq.md), [Troubleshooting](/extra/troubleshooting.md) page, and our [community](https://t.me/codex_community) are the fastest paths to an answer. # Birdeye to Codex Move your Birdeye Data Services integration to Codex Birdeye Data Services (BDS) and Codex cover a similar problem space: real-time token, trade, and wallet data across Solana and EVM chains. This guide maps every Birdeye endpoint to its Codex equivalent, shows working side-by-side examples for the most common patterns, and ends with a copy-paste prompt you can hand to an LLM to migrate the rest of your codebase. ## Mental model Birdeye is a REST API where the network is a request header (`x-chain`, defaults to Solana) and most resources have separate v1, v2, and v3 endpoints with different field naming conventions (camelCase in v1/v2, snake_case in v3+). Codex is a single GraphQL Supergraph: one endpoint (`https://graph.codex.io/graphql`), one auth header, one query language, and the network is just a parameter (`networkId`) on each field. What that means in practice: - You stop maintaining version-specific code paths (the `/defi/v2/...` vs `/defi/v3/...` decision goes away). - You stop juggling `x-chain` headers. Network selection lives next to the data, so the same query covers Solana, Ethereum, Base, BNB, and [80+ networks](https://docs.codex.io/networks). - Multi-token requests stop needing separate batch endpoints. GraphQL aliases and array inputs handle that natively, and you only pay for the fields you request. - Real-time data has two delivery options instead of one. Birdeye is WebSocket-only; Codex gives you [WebSocket subscriptions](/concepts/subscriptions.md) and [webhooks](/concepts/webhooks.md), and you can mix them in the same app. If you've never used GraphQL, [Learn GraphQL](/learn-graphql.md) is a 10-minute primer that's enough to follow the rest of this guide. ## Authentication Birdeye uses an `X-API-KEY` header plus an `x-chain` header that defaults to `solana` (so it's effectively required for every non-Solana request). Codex uses an `Authorization` header with your API key from the [dashboard](https://dashboard.codex.io?utm_source=codex&utm_medium=docs&utm_campaign=migrations-birdeye), and network comes through as a field argument instead of a header. ```bash Birdeye curl "https://public-api.birdeye.so/defi/price?address=So11111111111111111111111111111111111111112" \ -H "X-API-KEY: $BIRDEYE_API_KEY" \ -H "x-chain: solana" ``` ```bash Codex curl https://graph.codex.io/graphql \ -H "Authorization: $CODEX_API_KEY" \ -H "Content-Type: application/json" \ -d '{"query":"{ getTokenPrices(inputs: [{ address: \"So11111111111111111111111111111111111111112\", networkId: 1399811149 }]) { priceUsd timestamp address } }"}' ``` For browser-facing apps, generate a short-lived JWT with [`createApiTokens`](/api-reference/mutations/createapitokens.md) and pass it as `Bearer `. See [Authentication](/concepts/authentication.md) for the full pattern. ## Endpoint mapping The table covers the Birdeye endpoints customers ask about most often, grouped by surface area. Where Birdeye splits a concept across v1, v2, and v3 endpoints, the Codex equivalent on the right replaces all of them. ### Prices and OHLCV | Birdeye | Codex equivalent | Notes | | :-- | :-- | :-- | | `GET /defi/price` | [`getTokenPrices`](/api-reference/queries/gettokenprices.md) | Pass a single-element array. | | `GET /defi/multi_price`, `POST /defi/multi_price` | [`getTokenPrices`](/api-reference/queries/gettokenprices.md) | Native batch input, max 25 tokens per call (anything over is truncated). Birdeye's `multi_price` allows up to 100, so chunk larger batches. | | `GET /defi/historical_price_unix` | [`getTokenPrices`](/api-reference/queries/gettokenprices.md) with a `timestamp` input | Pass the unix timestamp on the input to get the price at that moment. | | `GET /defi/history_price` | [`getTokenBars`](/api-reference/queries/gettokenbars.md) | Token-level OHLCV; pair-level via [`getBars`](/api-reference/queries/getbars.md). | | `GET /defi/ohlcv`, `/defi/v3/ohlcv` | [`getTokenBars`](/api-reference/queries/gettokenbars.md) | Codex supports 1-second up to weekly (`7D`) intervals. | | `GET /defi/ohlcv/pair`, `/defi/v3/ohlcv/pair` | [`getBars`](/api-reference/queries/getbars.md) | Pair-scoped OHLCV. | | `GET /defi/ohlcv/base_quote` | [`getBars`](/api-reference/queries/getbars.md) with `quoteToken` | Invert the pair to quote in the other token. | | `GET /defi/price_volume/single`, `POST /defi/price_volume/multi` | [`getDetailedTokenStats`](/api-reference/queries/getdetailedtokenstats.md) | Price and volume in one query, plus much more. | | `GET /defi/v3/price/stats/single`, `POST /defi/v3/price/stats/multiple` | [`getDetailedTokenStats`](/api-reference/queries/getdetailedtokenstats.md) | Stats over multiple timeframes. | ### Token data | Birdeye | Codex equivalent | Notes | | :-- | :-- | :-- | | `GET /defi/token_overview` | [`token`](/api-reference/queries/token.md) + [`getDetailedTokenStats`](/api-reference/queries/getdetailedtokenstats.md) | One GraphQL request returns metadata, stats, safety, and launchpad context. | | `GET /defi/v3/token/meta-data/single` | [`token`](/api-reference/queries/token.md) | Richer payload than Birdeye's, including social links and image URLs. | | `GET /defi/v3/token/meta-data/multiple` | [`tokens(ids: [{ address, networkId }])`](/api-reference/queries/tokens.md) | Batch token metadata. | | `GET /defi/v3/token/market-data` (single + multiple) | [`getDetailedTokenStats`](/api-reference/queries/getdetailedtokenstats.md) | | | `GET /defi/v3/token/trade-data/single`, `/defi/v3/token/trade-data/multiple` | [`getDetailedTokenStats`](/api-reference/queries/getdetailedtokenstats.md) | Trade stats are part of detailed token stats. | | `GET /defi/token_security` | [`token`](/api-reference/queries/token.md) | Safety fields (`isScam`, `mintable`, `freezable`, `creatorAddress`, top-holder concentration) are inline on the token object. | | `GET /defi/token_creation_info` | [`token`](/api-reference/queries/token.md) | `createdAt`, `creatorAddress`. | | `GET /defi/v3/token/exit-liquidity`, `/defi/v3/token/exit-liquidity/multiple` | [`liquidityMetadata`](/api-reference/queries/liquiditymetadata.md) + [`liquidityMetadataByToken`](/api-reference/queries/liquiditymetadatabytoken.md) | Plus [`liquidityLocks`](/api-reference/queries/liquiditylocks.md) for locked-LP context. | ### Discovery and search | Birdeye | Codex equivalent | Notes | | :-- | :-- | :-- | | `GET /defi/token_trending` | [`filterTokens`](/api-reference/queries/filtertokens.md) ranked by `trendingScore24` | See the [Discover Tokens recipe](/recipes/discover-tokens.md). | | `GET /defi/v3/token/list`, `GET /defi/v3/token/list/scroll`, `GET /defi/tokenlist` | [`filterTokens`](/api-reference/queries/filtertokens.md) | Filters and rankings collapse into one query. | | `GET /defi/v2/tokens/new_listing` | [`filterTokens`](/api-reference/queries/filtertokens.md) ranked by `createdAt` | Or subscribe to [`onTokenLifecycleEventsCreated`](/api-reference/subscriptions/ontokenlifecycleeventscreated.md) / [`onLatestTokens`](/api-reference/subscriptions/onlatesttokens.md). | | `GET /defi/v3/search` | [`filterTokens(phrase: ...)`](/api-reference/queries/filtertokens.md) | Use `$SYMBOL` for exact symbol matches. | | `GET /defi/v3/token/meme/detail/single`, `GET /defi/v3/token/meme/list` | [`filterTokens`](/api-reference/queries/filtertokens.md) + [launchpad context](/launchpads.md) | Codex models meme launches as launchpad lifecycle events (pump.fun, LetsBonk, etc.). | | `GET /smart-money/v1/token/list` | [`filterTokens`](/api-reference/queries/filtertokens.md) plus [`filterWallets`](/api-reference/queries/filterwallets.md) | See the [Wallets recipe](/recipes/wallets/discover-traders.md) for the smart-money pattern. | ### Pairs and markets | Birdeye | Codex equivalent | Notes | | :-- | :-- | :-- | | `GET /defi/v3/pair/overview/single` | [`getDetailedPairStats`](/api-reference/queries/getdetailedpairstats.md) | Pair-level trade stats (volume, buys/sells, price change) are included. Birdeye has no separate pair `trade-data` endpoint; those fields live on the overview response. | | `GET /defi/v3/pair/overview/multiple` | [`getDetailedPairsStats`](/api-reference/queries/getdetailedpairsstats.md) | | | `GET /defi/v2/markets` | [`listPairsForToken`](/api-reference/queries/listpairsfortoken.md) + [`listPairsWithMetadataForToken`](/api-reference/queries/listpairswithmetadatafortoken.md) | All venues for a token. | ### Trades | Birdeye | Codex equivalent | Notes | | :-- | :-- | :-- | | `GET /defi/txs/token`, `GET /defi/v3/token/txs`, `GET /defi/txs/token/seek_by_time` | [`getTokenEvents`](/api-reference/queries/gettokenevents.md) | Swap and lifecycle events for a token; pass a `timestamp` range for the "seek by time" variant. | | `GET /defi/txs/pair`, `GET /defi/txs/pair/seek_by_time` | [`getTokenEvents`](/api-reference/queries/gettokenevents.md) | Pass the pair address in `query: { address: ... }`. | | `GET /defi/v3/txs`, `GET /defi/v3/txs/recent` | [`getTokenEvents`](/api-reference/queries/gettokenevents.md) | | | `GET /defi/v3/token/txs-by-volume` | [`getTokenEvents`](/api-reference/queries/gettokenevents.md) with `priceUsdTotal` filter | Filter swaps by USD size. | | `GET /defi/v3/token/mint-burn-txs` (Solana) | [`getTokenEvents`](/api-reference/queries/gettokenevents.md) with `eventDisplayType: [Mint, Burn]` | Mint/burn lifecycle events. | | `GET /defi/v3/all-time/trades/single`, `POST /defi/v3/all-time/trades/multiple` | [`getDetailedTokenStats`](/api-reference/queries/getdetailedtokenstats.md) (`statsUsd.volume`, `statsNonCurrency.transactions`) | Aggregate metrics, not raw rows. | ### Holders | Birdeye | Codex equivalent | Notes | | :-- | :-- | :-- | | `GET /defi/v3/token/holder` | [`holders`](/api-reference/queries/holders.md) | Ranked holder list. | | `GET /holder/v1/distribution` | `top10HoldersPercent` field on [`token`](/api-reference/queries/token.md) | Concentration in one field. | | `GET /token/v1/holder-profile` | `top10HoldersPercent` on [`token`](/api-reference/queries/token.md) + [`holders`](/api-reference/queries/holders.md) | Combine concentration with the ranked holder list. | | `GET /token/v1/holder/chart` | Partial via [`onHoldersUpdated`](/api-reference/subscriptions/onholdersupdated.md) | Codex streams live holder counts; historical timeseries is not a first-class endpoint. | | `GET /token/v1/holder-positions` | [`filterTokenWallets`](/api-reference/queries/filtertokenwallets.md) | Wallets ranked by per-token PnL. | | `POST /token/v1/holder/batch` | [`balances`](/api-reference/queries/balances.md) (per wallet) | One call per wallet; aliases let you batch in a single GraphQL request. | ### Wallets | Birdeye | Codex equivalent | Notes | | :-- | :-- | :-- | | `GET /v1/wallet/token_list` | [`balances`](/api-reference/queries/balances.md) | Wallet portfolio with prices. Native balances on EVM chains require traces support; not available on Sui. | | `GET /v1/wallet/token_balance`, `POST /wallet/v2/token-balance` | [`balances`](/api-reference/queries/balances.md) with `tokens: [...]` | Pass the token IDs (`address:networkId`) you want; max 200 per request. | | `GET /v1/wallet/list_supported_chain` | [`getNetworks`](/api-reference/queries/getnetworks.md) | Network catalog used everywhere else in the API. | | `GET /v1/wallet/tx_list` | [`getTokenEventsForMaker`](/api-reference/queries/gettokeneventsformaker.md) | Codex returns swap events for a wallet; raw transfers are not exposed. | | `GET /wallet/v2/current-net-worth` | [`detailedWalletStats`](/api-reference/queries/detailedwalletstats.md) | PnL, volume, swap counts. | | `GET /wallet/v2/net-worth`, `/wallet/v2/net-worth-details`, `POST /wallet/v2/net-worth-summary/multiple` | [`walletChart`](/api-reference/queries/walletchart.md) + [`detailedWalletStats`](/api-reference/queries/detailedwalletstats.md) | Use GraphQL aliases to batch multiple wallets in one request. | | `GET /wallet/v2/pnl`, `/wallet/v2/pnl/summary`, `GET /wallet/v2/pnl/multiple`, `POST /wallet/v2/pnl/details` | [`detailedWalletStats`](/api-reference/queries/detailedwalletstats.md) + [`filterWallets`](/api-reference/queries/filterwallets.md) | | | `GET /wallet/v2/pnl/chart` | [`walletChart`](/api-reference/queries/walletchart.md) | PnL and volume over time; resolutions `60`, `240`, `1D`, `7D`. | | `GET /wallet/v2/leaderboard` | [`filterWallets`](/api-reference/queries/filterwallets.md) ranked by `realizedProfitUsd*` | Discover top wallets across all networks, not just a preset board. | | `GET /wallet/v2/balance-change` | [`onBalanceUpdated`](/api-reference/subscriptions/onbalanceupdated.md) subscription | Live balance changes; historical reconstruction requires combining events. | | `POST /wallet/v2/tx/first-funded` | Not directly supported | Flag during migration. | ### Traders | Birdeye | Codex equivalent | Notes | | :-- | :-- | :-- | | `GET /defi/v2/tokens/top_traders` | [`tokenTopTraders`](/api-reference/queries/tokentoptraders.md) | Top buyers/sellers/PnL for a token. | | `GET /trader/txs/seek_by_time` | [`getTokenEventsForMaker`](/api-reference/queries/gettokeneventsformaker.md) | | | `GET /trader/gainers-losers` | [`filterWallets`](/api-reference/queries/filterwallets.md) ranked by `realizedProfitUsd*` | Far richer filtering than Birdeye's endpoint. | ### Utility | Birdeye | Codex equivalent | Notes | | :-- | :-- | :-- | | `GET /defi/networks` | [`getNetworks`](/api-reference/queries/getnetworks.md) | Returns `networkId`s you'll use everywhere else. | | `GET /defi/v3/txs/latest-block` | [`blocks`](/api-reference/queries/blocks.md) | Look up blocks by `blockNumbers` or `timestamps`; pass the current timestamp to resolve the latest block. | | `GET /utils/v1/credits` | Codex usage in the [dashboard](https://dashboard.codex.io) | | ## Side-by-side examples The four patterns below are the ones Birdeye customers most commonly migrate first. Token addresses are real and queries are runnable. ### 1. Multi-token price ```bash Birdeye curl -X POST "https://public-api.birdeye.so/defi/multi_price" \ -H "X-API-KEY: $BIRDEYE_API_KEY" \ -H "x-chain: solana" \ -H "Content-Type: application/json" \ -d '{"list_address":"So11111111111111111111111111111111111111112,EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"}' ``` ```typescript Codex SDK const sdk = new Codex(process.env.CODEX_API_KEY!) const { getTokenPrices } = await sdk.queries.getTokenPrices({ inputs: [ { address: "So11111111111111111111111111111111111111112", networkId: 1399811149 }, { address: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", networkId: 1399811149 }, ], }) getTokenPrices.forEach((p) => console.log(p.address, p.priceUsd)) ``` ```graphql Codex GraphQL query MultiPrice { getTokenPrices( inputs: [ { address: "So11111111111111111111111111111111111111112", networkId: 1399811149 } { address: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", networkId: 1399811149 } ] ) { address networkId priceUsd timestamp } } ``` For a live price feed instead of polling, subscribe to [`onPricesUpdated`](/api-reference/subscriptions/onpricesupdated.md). ### 2. OHLCV chart ```bash Birdeye curl "https://public-api.birdeye.so/defi/v3/ohlcv?address=So11111111111111111111111111111111111111112&type=1H&time_from=1716595200&time_to=1717200000" \ -H "X-API-KEY: $BIRDEYE_API_KEY" \ -H "x-chain: solana" ``` ```graphql Codex GraphQL query TokenChart { getTokenBars( symbol: "So11111111111111111111111111111111111111112:1399811149" from: 1716595200 to: 1717200000 resolution: "60" ) { t o h l c volume } } ``` Codex supports resolutions from 1-second up to weekly (`7D`). Sub-minute resolutions (`1S`-`30S`) are only populated for the last 24 hours, and Birdeye's monthly (`1M`) candles have no direct equivalent (aggregate from `1D` or `7D` bars). For live chart updates, layer in the [`onBarsUpdated`](/api-reference/subscriptions/onbarsupdated.md) subscription. See the [Charts recipe](/recipes/charts.md) for a full Lightweight Charts integration. ### 3. Token overview (metadata + stats + safety) Birdeye splits this across `/defi/token_overview`, `/defi/v3/token/market-data`, and `/defi/token_security`. Codex returns the same picture in a single request. ```bash Birdeye curl "https://public-api.birdeye.so/defi/token_overview?address=So11111111111111111111111111111111111111112" \ -H "X-API-KEY: $BIRDEYE_API_KEY" \ -H "x-chain: solana" curl "https://public-api.birdeye.so/defi/token_security?address=So11111111111111111111111111111111111111112" \ -H "X-API-KEY: $BIRDEYE_API_KEY" \ -H "x-chain: solana" ``` ```graphql Codex GraphQL query TokenOverview { token(input: { address: "So11111111111111111111111111111111111111112", networkId: 1399811149 }) { name symbol decimals address isScam mintable freezable creatorAddress createdAt top10HoldersPercent info { circulatingSupply totalSupply imageLargeUrl description } socialLinks { twitter telegram discord website } launchpad { launchpadName graduationPercent completed } } getDetailedTokenStats( tokenAddress: "So11111111111111111111111111111111111111112" networkId: 1399811149 durations: [day1] ) { stats_day1 { statsUsd { volume { currentValue change } close { currentValue change } } statsNonCurrency { transactions { currentValue } buys { currentValue } sells { currentValue } } } } } ``` The [Detailed Token Page recipe](/recipes/detailed-token-page.md) shows the full pattern Codex customers use to build a token detail screen. ### 4. Wallet portfolio ```bash Birdeye curl "https://public-api.birdeye.so/v1/wallet/token_list?wallet=Bi4rd5FH5bYEN8scZ7wevxNZyNmKHdaBcvewdPFxYdLt" \ -H "X-API-KEY: $BIRDEYE_API_KEY" \ -H "x-chain: solana" ``` ```graphql Codex GraphQL query WalletPortfolio { balances( input: { walletAddress: "Bi4rd5FH5bYEN8scZ7wevxNZyNmKHdaBcvewdPFxYdLt" networks: [1399811149] removeScams: true } ) { items { tokenId shiftedBalance balanceUsd tokenPriceUsd } cursor } } ``` Enrich the response with live USD pricing by batching the returned `tokenId`s into [`getTokenPrices`](/api-reference/queries/gettokenprices.md). For wallet-level PnL and volume, see [`detailedWalletStats`](/api-reference/queries/detailedwalletstats.md) and the [Wallets recipe](/recipes/wallets/discover-traders.md). ## Real-time data Birdeye delivers real-time data exclusively through WebSocket subscriptions at `wss://public-api.birdeye.so/socket/`. Codex gives you the same data with two delivery options, and you can use both at once: - [WebSocket subscriptions](/concepts/subscriptions.md): persistent connection, updates pushed inline. Best for dashboards, trading UIs, anything user-facing. - [Webhooks](/concepts/webhooks.md): Codex calls an HTTP endpoint you control when an event fires. Best for background jobs, alerts, and queue-driven systems. | Birdeye channel | Codex subscription | Codex webhook | | :-- | :-- | :-- | | `SUBSCRIBE_PRICE` | [`onPriceUpdated`](/api-reference/subscriptions/onpriceupdated.md), [`onPricesUpdated`](/api-reference/subscriptions/onpricesupdated.md) | Price webhook | | `SUBSCRIBE_BASE_QUOTE_PRICE` | [`onBarsUpdated`](/api-reference/subscriptions/onbarsupdated.md) | | | `SUBSCRIBE_TOKEN_STATS` | [`onDetailedTokenStatsUpdated`](/api-reference/subscriptions/ondetailedtokenstatsupdated.md) | | | `SUBSCRIBE_TXS` | [`onTokenEventsCreated`](/api-reference/subscriptions/ontokeneventscreated.md), [`onEventsCreated`](/api-reference/subscriptions/oneventscreated.md) | Token swap webhook | | `SUBSCRIBE_WALLET_TXS` | [`onEventsCreatedByMaker`](/api-reference/subscriptions/oneventscreatedbymaker.md) | Token swap webhook (maker filter) | | `SUBSCRIBE_LARGE_TRADE_TXS` | [`onEventsCreated`](/api-reference/subscriptions/oneventscreated.md) with min-volume filter | | | `SUBSCRIBE_NEW_PAIR`, `SUBSCRIBE_TOKEN_NEW_LISTING` | [`onTokenLifecycleEventsCreated`](/api-reference/subscriptions/ontokenlifecycleeventscreated.md), [`onLatestTokens`](/api-reference/subscriptions/onlatesttokens.md), [`onLaunchpadTokenEvent`](/api-reference/subscriptions/onlaunchpadtokenevent.md) | | | `SUBSCRIBE_MEME` | [`onLaunchpadTokenEvent`](/api-reference/subscriptions/onlaunchpadtokenevent.md) + [`onDetailedTokenStatsUpdated`](/api-reference/subscriptions/ondetailedtokenstatsupdated.md) | | | `SUBSCRIBE_TRANSFER` | Not directly supported | Codex streams swap and lifecycle events, not arbitrary token transfers. Pair with an RPC provider if you need this. | ## Gaps Things Birdeye does that Codex doesn't, and what to do about them: - **Perpetuals data** (`/perps/v1/*`). Codex is a spot-trading API. If your product depends on open positions, liquidation maps, or perp wallets, keep Birdeye for that surface or pair Codex with a perps-native provider. - **Wallet-level transfers (non-swap)** (`/wallet/v2/transfer*`, `/token/v1/transfer*`). Codex returns swap and token-lifecycle events, not arbitrary token transfers. Combine Codex with an RPC provider or Etherscan-family API if transfer history is core to your product. - **RPC-style blockchain data** (`/blockchain/v1/account/*`, `/blockchain/v1/transaction/detail`, `/blockchain/v1/token/metadata`). Raw account, token-account, and transaction lookups are RPC-layer reads; pair Codex with an RPC provider for these. - **Wallet identity and domain resolution** (`/identity/v1/single`, `/identity/v1/multiple`, `/identity/v1/domains`). Codex doesn't resolve wallets to names/domains. `balances` and event queries always take raw addresses. - **First-buyers and wallet-tag analytics** (`/token/v1/first-buyers`, `/token/v1/wallet-tags-tracker`). No first-class equivalent; you can approximate first buyers from the earliest [`getTokenEvents`](/api-reference/queries/gettokenevents.md) for a token, but the curated tag surface has no analogue. - **Historical liquidity timeseries** (`/defi/v3/liquidity/ohlc/*`, `/defi/v3/liquidity/history/token`). Codex exposes *current* liquidity via [`liquidityMetadata`](/api-reference/queries/liquiditymetadata.md) and pair stats, not an OHLC curve of liquidity over time. - **NFT data.** Birdeye doesn't ship a full NFT product either, but if your codebase touches NFT collections or holdings, Codex won't fill that gap. - **Centralized exchange liquidity dashboards.** Codex is onchain-only. - **First-funded-by lookup** (`POST /wallet/v2/tx/first-funded`). No direct equivalent; flag during migration. - **Native-token balances on chains without traces.** `balances` returns ERC-20/SPL holdings on every supported network, but native-token amounts on EVM chains require traces support. Sui balances aren't available at all (`networkId: 101`). ## What you pick up Things Codex offers that Birdeye doesn't: - **One query, many shapes.** GraphQL lets you combine token metadata, price, holders, recent trades, and chart data into a single request and only pull the fields you render. A typical Birdeye-powered token page hits three or four endpoints; the Codex equivalent is one. - **Webhooks alongside subscriptions.** Push real-time data to your servers without holding open a WebSocket. Configure via [`createWebhooks`](/api-reference/mutations/createwebhooks.md). - **Prediction markets.** Polymarket and Kalshi event, market, trade, and trader data via the [`filterPredictionEvents`](/api-reference/queries/filterpredictionevents.md) family. See [Prediction Markets](/prediction-markets.md). - **Launchpad lifecycle data.** First-class support for pump.fun, LetsBonk, Believe, and other launchpads, including bonding-curve state, graduation, and migration events. See [Launchpads](/launchpads.md). - **Wallet discovery by performance.** [`filterWallets`](/api-reference/queries/filterwallets.md) lets you query for wallets matching specific PnL, win-rate, or trading-volume criteria across all networks, not just for a single token. - **Liquidity locks.** [`liquidityLocks`](/api-reference/queries/liquiditylocks.md) surfaces locked-LP context that Birdeye's `/defi/v3/token/exit-liquidity` doesn't. - **Built for AI agents.** A [docs MCP server](/agents/docs-mcp.md), prebuilt [Codex Skills](/agents/codex-skills.md) for Claude/Cursor/Codex CLI, and pay-per-query access via [MPP](/agents/mpp.md). ## AI migration prompt Most Birdeye integrations span dozens of call sites: a price service here, a chart loader there, a portfolio screen, a webhook handler. Hand the prompt below to an IDE agent (Claude Code, Cursor, Codex CLI, or similar), run it from the repo root, and it will discover every Birdeye touchpoint, propose a plan, and execute the migration with your approval. Pair this prompt with our [Codex Skills](/agents/codex-skills.md) and [docs MCP server](/agents/docs-mcp.md) so the agent can look up Codex queries on demand instead of guessing at field names. ````markdown You are migrating this codebase from Birdeye Data Services (BDS) to Codex (https://docs.codex.io). Birdeye and Codex overlap heavily on token, trade, holder, and wallet data, so most call sites have a direct replacement. A few do not, and those need to be surfaced rather than silently dropped. ## Phase 1: Discovery (do this first, do not edit yet) Search the codebase for every Birdeye integration point. At minimum, look for: - HTTP calls to `public-api.birdeye.so` (any path under `/defi`, `/wallet`, `/token`, `/trader`, `/perps`, `/smart-money`, `/utils`, `/holder`, or `/v1/wallet`). - WebSocket connections to `wss://public-api.birdeye.so/socket/...`. - Imports of any Birdeye SDK or client library. - Environment variables and config keys named `BIRDEYE_*` or `BDS_*`. - Header usage of `X-API-KEY` and `x-chain`. - Code that switches behavior on Birdeye `chain` strings (`solana`, `ethereum`, `base`, etc.). - Tests, fixtures, and mocks that reference any of the above. Produce a Migration Plan with: 1. A grouped list of every call site, organized by Birdeye endpoint. 2. The proposed Codex equivalent for each group (use the mapping below). 3. Any call sites you cannot map cleanly, flagged for human review. 4. The order you intend to make changes (shared client/config first, then leaf call sites, then tests). 5. New dependencies, env vars, and config you will introduce. Stop and surface the plan before editing any source files. Wait for confirmation. ## Phase 2: Execution (after the plan is approved) Ground rules: 1. Codex is a GraphQL API at `https://graph.codex.io/graphql`. Auth header is `Authorization: ` for long-lived keys, or `Authorization: Bearer ` for short-lived keys. 2. Codex ships an official SDK for TypeScript/JavaScript only (`@codex-data/sdk`). Prefer it over hand-rolled HTTP in TS/JS projects. For Python and every other language there is no official SDK: use raw GraphQL over HTTP against `https://graph.codex.io/graphql`. 3. Network is a parameter (`networkId`), not a header. Convert Birdeye `x-chain` strings to Codex network IDs: `solana` → 1399811149, `ethereum` → 1, `base` → 8453, `bsc` → 56, `polygon` → 137, `arbitrum` → 42161, `optimism` → 10, `avalanche` → 43114, `sui` → 101. For others, call `getNetworks` once and build a lookup. 4. Token IDs in Codex are the string `"
:"`. Construct them explicitly; never assume an integration relies on bare addresses. 5. Where a Birdeye integration hits two or three endpoints to fill one screen (for example token_overview + token_security + market-data), collapse them into a single GraphQL query. 6. For real-time data, use WebSocket subscriptions when the consumer is a long-lived client (dashboards, trading UIs) and webhooks when the consumer is a server endpoint (alerts, background workers, queues). 7. Preserve existing public function signatures, return shapes, and error semantics wherever possible. Internal helpers can be refactored freely. 8. Update tests as you change code. If a test relied on a Birdeye response fixture, replace the fixture with a Codex equivalent rather than deleting the test. 9. When you hit a gap (perpetuals, non-swap wallet transfers, first-funded-by lookup, CEX liquidity), do not silently drop the feature. Leave the call site intact, add a `TODO(migration):` comment with a one-line note explaining what's missing and what provider could fill it, and list it in your final report. Note that Sui (`networkId: 101`) is supported broadly, but `balances` is not available on Sui: flag any Sui balance lookups specifically. ## Birdeye → Codex endpoint mapping Prices and OHLCV: - `GET /defi/price`, `GET|POST /defi/multi_price` → `getTokenPrices(inputs: [...])` (max 25 inputs per call; chunk Birdeye `multi_price` batches larger than 25 or they will be silently truncated) - `GET /defi/historical_price_unix` → `getTokenPrices(inputs: [{ address, networkId, timestamp }])` (the historical price at a unix timestamp) - `GET /defi/history_price` → `getTokenBars` - `GET /defi/ohlcv`, `/defi/v3/ohlcv` → `getTokenBars` - `GET /defi/ohlcv/pair`, `/defi/v3/ohlcv/pair`, `/defi/ohlcv/base_quote` → `getBars` (use `quoteToken: token0 | token1` to invert the pair) - `GET /defi/price_volume/*`, `/defi/v3/price/stats/*` → `getDetailedTokenStats(tokenAddress, networkId, durations: [...])` (top-level args, not an `input` object) Token data: - `GET /defi/token_overview` → `token` + `getDetailedTokenStats` in one query - `GET /defi/v3/token/meta-data/single` → `token(input: { address, networkId })` - `GET /defi/v3/token/meta-data/multiple` → `tokens(ids: [{ address, networkId }, ...])` - `GET /defi/v3/token/market-data*`, `/defi/v3/token/trade-data/*` → `getDetailedTokenStats` - `GET /defi/token_security` → safety fields on `token` (`isScam`, `mintable`, `freezable`, `creatorAddress`, `top10HoldersPercent`). `circulatingSupply` and `totalSupply` live under `token.info`, not the top-level token object. - `GET /defi/token_creation_info` → `createdAt`, `creatorAddress` on `token` - `GET /defi/v3/token/exit-liquidity`, `/defi/v3/token/exit-liquidity/multiple` → `liquidityMetadata`, `liquidityMetadataByToken`, `liquidityLocks` Discovery: - `GET /defi/token_trending` → `filterTokens(rankings: [{ attribute: trendingScore24, direction: DESC }])` - `GET /defi/v3/token/list`, `/defi/v3/token/list/scroll`, `/defi/tokenlist`, `/defi/v2/tokens/new_listing` → `filterTokens(...)` - `GET /defi/v3/search` → `filterTokens(phrase: "$SYMBOL", ...)` - `GET /defi/v3/token/meme/*` → `filterTokens` plus launchpad fields on `token.launchpad` - `GET /smart-money/v1/token/list` → `filterWallets` + `filterTokens` (see Wallets recipe) Pairs: - `GET /defi/v3/pair/overview/single` → `getDetailedPairStats` (pair trade stats are part of this response; Birdeye has no separate pair `trade-data` endpoint) - `GET /defi/v3/pair/overview/multiple` → `getDetailedPairsStats` - `GET /defi/v2/markets` → `listPairsForToken` or `listPairsWithMetadataForToken` Trades: - `GET /defi/txs/token`, `/defi/txs/token/seek_by_time`, `/defi/v3/token/txs`, `/defi/v3/txs`, `/defi/v3/txs/recent` → `getTokenEvents(query: { address, networkId, ... })` - `GET /defi/txs/pair`, `/defi/txs/pair/seek_by_time` → `getTokenEvents` (pair address as `query.address`) - `GET /defi/v3/token/txs-by-volume` → `getTokenEvents` with `priceUsdTotal` filter - `GET /defi/v3/token/mint-burn-txs` → `getTokenEvents` with `eventDisplayType: [Mint, Burn]` (Solana only) - `GET /defi/v3/all-time/trades/single`, `POST /defi/v3/all-time/trades/multiple` → `getDetailedTokenStats` (read `statsUsd.volume.currentValue`, `statsNonCurrency.transactions.currentValue`) Holders: - `GET /defi/v3/token/holder` → `holders(input: { tokenId, sort: { attribute: BALANCE, direction: DESC } })` (`BALANCE` is the only supported sort attribute; `DATE` exists but is deprecated) - `GET /holder/v1/distribution`, `/token/v1/holder-profile` → `top10HoldersPercent` on `token` (combine with `holders` for the ranked list) - `GET /token/v1/holder/chart` → partial via `onHoldersUpdated` (live counts only; no first-class historical timeseries) - `GET /token/v1/holder-positions` → `filterTokenWallets` - `POST /token/v1/holder/batch` → `balances` per wallet (use GraphQL aliases to batch) Wallets: - `GET /v1/wallet/token_list`, `/v1/wallet/token_balance`, `POST /wallet/v2/token-balance` → `balances(input: { walletAddress, networks: [Int!], removeScams, tokens, limit })` (the network field is plural/array, not `networkId`; max 200 tokens per request) - `GET /v1/wallet/list_supported_chain` → `getNetworks` - `GET /v1/wallet/tx_list` → `getTokenEventsForMaker` (swap events; flag if raw transfers are required) - `GET /wallet/v2/current-net-worth`, `/wallet/v2/pnl`, `/wallet/v2/pnl/summary`, `/wallet/v2/pnl/multiple`, `POST /wallet/v2/pnl/details` → `detailedWalletStats` and `filterWallets` - `GET /wallet/v2/net-worth`, `/wallet/v2/net-worth-details`, `POST /wallet/v2/net-worth-summary/multiple` → `walletChart` + `detailedWalletStats` (alias multiple wallets in one request) - `GET /wallet/v2/balance-change` → `onBalanceUpdated` subscription Traders: - `GET /defi/v2/tokens/top_traders` → `tokenTopTraders(input: { tokenAddress, networkId, tradingPeriod })` (`tradingPeriod` is `DAY | WEEK | MONTH | YEAR` — no hourly value) - `GET /trader/txs/seek_by_time` → `getTokenEventsForMaker` - `GET /trader/gainers-losers` → `filterWallets(input: { rankings: [{ attribute: realizedProfitUsd1d, direction: DESC }] })` (pick `1d` / `1w` / `30d` / `1y` to match the timeframe) Real-time (WebSocket → Codex subscription): - `SUBSCRIBE_PRICE` → `onPriceUpdated` / `onPricesUpdated` - `SUBSCRIBE_BASE_QUOTE_PRICE` → `onBarsUpdated` - `SUBSCRIBE_TOKEN_STATS` → `onDetailedTokenStatsUpdated` - `SUBSCRIBE_TXS`, `SUBSCRIBE_LARGE_TRADE_TXS` → `onTokenEventsCreated` / `onEventsCreated` (apply a min-volume filter for the large-trade variant) - `SUBSCRIBE_WALLET_TXS` → `onEventsCreatedByMaker` - `SUBSCRIBE_NEW_PAIR`, `SUBSCRIBE_TOKEN_NEW_LISTING`, `SUBSCRIBE_MEME` → `onTokenLifecycleEventsCreated`, `onLatestTokens`, `onLaunchpadTokenEvent` - `SUBSCRIBE_TRANSFER` → not supported (Codex streams swaps and lifecycle events, not arbitrary transfers) Utility: - `GET /defi/networks` → `getNetworks` - `GET /defi/v3/txs/latest-block` → `blocks(input: { networkId, blockNumbers, timestamps })` Gaps (flag, do not drop): - `/perps/v1/*`: not supported - `/wallet/v2/transfer*`, `/token/v1/transfer*`: Codex is swap-focused; pair with an RPC provider for raw transfers - `/blockchain/v1/*` (account/token-account/transaction/token-metadata RPC-style reads): not supported; pair with an RPC provider - `/identity/v1/*` (wallet identity / domain resolution): not supported; Codex uses raw addresses - `/token/v1/first-buyers`, `/token/v1/wallet-tags-tracker`: no first-class equivalent (approximate first buyers from earliest `getTokenEvents`) - `/defi/v3/liquidity/ohlc/*`, `/defi/v3/liquidity/history/token`: Codex exposes current liquidity, not a historical liquidity OHLC timeseries - `POST /wallet/v2/tx/first-funded`: no direct equivalent - NFT endpoints: Codex does not expose NFT data - Sui (`networkId: 101`): broadly supported, but `balances` does not work on Sui. Flag Sui balance call sites specifically. - EVM native balances via `balances` are only available on networks with traces enabled. If a customer relies on native-token portfolio values on a chain without traces, flag it. When you need details on any Codex field, fetch the reference page at `https://docs.codex.io/api-reference/queries/` (or `subscriptions`, `mutations`) rather than guessing. Before migrating any real-time code, fetch `https://docs.codex.io/concepts/subscriptions` and `https://docs.codex.io/concepts/webhooks` so you pick the right delivery mechanism. ## Phase 3: Final report When the migration is done, produce a single report with: 1. Files changed, grouped by area (client/config, call sites, tests, docs). 2. Every `TODO(migration):` you added, with file path, line, and the reason. 3. New env vars and dependencies, with the line to add to `.env.example` and the package manager command to install. 4. Birdeye integrations that were removed entirely, and what replaced them. 5. A short manual-verification checklist the human should run before merging (which features to click through, which endpoints to spot-check, which dashboards to load). Run the project's linter and test suite before declaring the migration complete. If tests fail, fix the underlying integration, do not weaken the test. ```` ## Getting help - Browse the [API Reference](/api-reference/introduction.md) for the full schema. - Skim the [Recipes](/recipes/discover-tokens.md) for end-to-end examples that solve specific product problems. - Ask in [our community](https://t.me/codex_community) if you hit a wall during migration. # Bitquery to Codex Move your Bitquery integration to Codex for curated DEX data, first-class safety and PnL signals, and simpler queries Bitquery and Codex are both GraphQL APIs over onchain data, so this is one of the smoother migrations in this section: you already think in queries, selections, and variables. The shift is in altitude. Bitquery exposes a low-level "cube" of raw datasets (trades, transfers, balance updates, instructions) that you aggregate yourself. Codex exposes curated, ready-made answers: a token's price, its safety signals, its holders, a wallet's PnL, a launchpad's lifecycle. Most of what a Bitquery integration hand-builds from raw trades, Codex returns as a field. If your product depends on Bitquery's low-level surface — mempool/pending transactions, decoded logs and instructions across arbitrary contracts, ad-hoc aggregations over any onchain event, NFT trades, or non-EVM chains like Bitcoin, Tron, or Cardano — Codex is not a drop-in replacement for those. See [Gaps](#gaps) for the specifics. If you use Bitquery for DEX trades, prices, OHLCV, token metadata, holders, balances, or wallet activity, everything you rely on has a Codex equivalent, usually a shorter one. ## Mental model Bitquery organizes data as a cube: you enter a chain-family block (`EVM(network: eth, dataset: combined)`, `Solana`, `Tron`), pick a dataset inside it (`DEXTrades`, `DEXTradeByTokens`, `BalanceUpdates`, `Holders`, `Transfers`), and then filter, group, and aggregate the raw rows to produce the answer you want. It's powerful and general — you can compute almost anything — but you own the aggregation logic, and every query is priced by how much data it touches. Codex is a single Supergraph of purpose-built fields. One endpoint (`https://graph.codex.io/graphql`), one auth header, and the network is a numeric `networkId` parameter. Instead of assembling candles from a trade stream, you call [`getTokenBars`](/api-reference/queries/gettokenbars.md). Instead of grouping `DEXTrades` by trader to compute profit, you call [`detailedWalletStats`](/api-reference/queries/detailedwalletstats.md). Instead of inferring "is this a scam" from raw data, you read `isScam` off the [`token`](/api-reference/queries/token.md). What that means in practice: - **You stop hand-rolling aggregations.** OHLCV, trending, holder concentration, wallet PnL, buy/sell volume splits, and multi-timeframe stats are first-class fields, not queries you assemble and maintain. - **The network is a parameter, not a top-level block.** You stop nesting everything under `EVM(network: ...)` / `Solana`. Pass `networkId` (or `networks: [Int!]` on `balances`) and the same query covers any of [80+ networks](/networks.md). - **You pull, not compute, safety and lifecycle data.** Scam flags, mint/freeze authorities, launchpad graduation, and liquidity locks are indexed and returned directly. - **Queries get shorter.** "Give me this token's current price" is a single `getTokenPrices` call, not a trade query you sort and read the latest price from. If you've been writing Bitquery GraphQL, the [Learn GraphQL](/learn-graphql.md) primer is unnecessary — you already know the mechanics. Skim [Queries](/concepts/queries.md) and [Subscriptions](/concepts/subscriptions.md) for the Codex-specific conventions (token IDs, `networkId`, cursors). ## Authentication Bitquery's V2 API uses OAuth2: you exchange a `client_id`/`client_secret` for a short-lived Bearer token at `oauth2.bitquery.io`, then send it as `Authorization: Bearer ` against `https://streaming.bitquery.io/graphql`. (The legacy V1 endpoint `graphql.bitquery.io` used an `X-API-KEY` header; if your integration still uses it, you're on the old model.) Codex uses a single long-lived `Authorization` header with an API key from the [dashboard](https://dashboard.codex.io?utm_source=codex&utm_medium=docs&utm_campaign=migrations-bitquery) — no token-exchange step. ```bash Bitquery # 1. Exchange client credentials for an access token curl -X POST https://oauth2.bitquery.io/oauth2/token \ -d grant_type=client_credentials \ -d client_id=$BITQUERY_CLIENT_ID \ -d client_secret=$BITQUERY_CLIENT_SECRET # → { "access_token": "ory_at_...", ... } # 2. Call the API with the Bearer token curl -X POST https://streaming.bitquery.io/graphql \ -H "Authorization: Bearer $BITQUERY_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{"query":"{ EVM { DEXTrades(limit: {count: 1}) { Block { Time } } } }"}' ``` ```bash Codex curl https://graph.codex.io/graphql \ -H "Authorization: $CODEX_API_KEY" \ -H "Content-Type: application/json" \ -d '{"query":"{ getTokenPrices(inputs: [{ address: \"0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2\", networkId: 1 }]) { priceUsd timestamp } }"}' ``` For browser-facing apps, generate a short-lived JWT with [`createApiTokens`](/api-reference/mutations/createapitokens.md) and pass it as `Bearer `. See [Authentication](/concepts/authentication.md) for the full pattern. ## Endpoint mapping Bitquery is addressed by dataset, not by URL, so this table pairs each Bitquery dataset (and the common query pattern built on it) with the Codex field that returns the same thing directly. ### DEX trades and events | Bitquery dataset / pattern | Codex equivalent | Notes | | :-- | :-- | :-- | | `EVM { DEXTrades }`, `Solana { DEXTrades }` | [`getTokenEvents`](/api-reference/queries/gettokenevents.md) | One normalized swap event per trade. `DEXTrades` is one row per swap; Codex events carry maker, amounts, USD price, and tx hash inline. | | `EVM { DEXTradeByTokens }`, `Solana { DEXTradeByTokens }` | [`getTokenEvents`](/api-reference/queries/gettokenevents.md) (+ [`getDetailedTokenStats`](/api-reference/queries/getdetailedtokenstats.md) for aggregates) | `DEXTradeByTokens` emits two rows per trade (one per side) for easier aggregation; in Codex, buy/sell breakdowns are first-class stats (token-level buy/sell counts, pair-level buy/sell volume) rather than something you group by hand. | | `DEXTrades` filtered by `Trade.Buy.Buyer` / trader | [`getTokenEventsForMaker`](/api-reference/queries/gettokeneventsformaker.md) | Swap events for a single wallet. | | `Solana { Instructions }` for a DEX program | [`getTokenEvents`](/api-reference/queries/gettokenevents.md) with `eventDisplayType` | Codex decodes swap/mint/burn events for you; raw instruction decoding is a gap (see below). | ### Prices and OHLCV | Bitquery dataset / pattern | Codex equivalent | Notes | | :-- | :-- | :-- | | `Trade.PriceInUSD` read off the latest `DEXTrades` row | [`getTokenPrices`](/api-reference/queries/gettokenprices.md) | Direct current price by `address:networkId`; up to 25 inputs per request. No trade query to sort. | | Historical price via `DEXTrades` at a past block/time | [`getTokenPrices`](/api-reference/queries/gettokenprices.md) with a `timestamp` input | Price at a moment, without reconstructing it from trades. | | Crypto Price API `Trade { Price { Ohlc { … } } }`, or hand-rolled OHLC from `DEXTradeByTokens` bucketed by `Block { Time(interval:) }` | [`getTokenBars`](/api-reference/queries/gettokenbars.md) (token) / [`getBars`](/api-reference/queries/getbars.md) (pair) | First-class OHLCV, resolutions `1S`–`7D`. No candle assembly. | ### Token data | Bitquery dataset / pattern | Codex equivalent | Notes | | :-- | :-- | :-- | | `Currencies` / token fields on trades | [`token`](/api-reference/queries/token.md) + [`tokens`](/api-reference/queries/tokens.md) | Richer metadata: social links, image URLs, description, launchpad context. | | `TokenSupplyUpdates` (supply) | `token.info` (`circulatingSupply`, `totalSupply`) | Current supply on the token object. | | No first-class equivalent (infer from raw data) | Safety fields on [`token`](/api-reference/queries/token.md) | `isScam`, `mintable`, `freezable`, `creatorAddress`, `top10HoldersPercent` are indexed, not derived. | | Aggregated `DEXTradeByTokens` for volume/txn stats | [`getDetailedTokenStats`](/api-reference/queries/getdetailedtokenstats.md) | Multi-timeframe volume, transactions, buy/sell counts in one call. | ### Holders and balances | Bitquery dataset / pattern | Codex equivalent | Notes | | :-- | :-- | :-- | | `EVM { Holders }` (formerly `TokenHolders(date:)`, removed June 2026) | [`holders`](/api-reference/queries/holders.md) (+ [`top10HoldersPercent`](/api-reference/queries/top10holderspercent.md)) | Ranked holders with balances; concentration returned on the same response. Growth or Enterprise plan. | | `BalanceUpdates` aggregated to a wallet's current holdings | [`balances`](/api-reference/queries/balances.md) | Portfolio with USD pricing inline (`balanceUsd`, `tokenPriceUsd`); pass `networks: [Int!]`, max 200 tokens. Growth or Enterprise plan. | | `BalanceUpdates` reconstructed at a past date | Not supported | Codex returns current balances; historical point-in-time balances are a gap (see below). | | `Transfers` (arbitrary token transfers) | [`getTokenEvents`](/api-reference/queries/gettokenevents.md) / [`getTokenEventsForMaker`](/api-reference/queries/gettokeneventsformaker.md) | Codex indexes DEX swap and lifecycle events, not arbitrary transfers. See [Gaps](#gaps). | ### Wallets and traders | Bitquery dataset / pattern | Codex equivalent | Notes | | :-- | :-- | :-- | | `DEXTrades` grouped by trader, PnL computed yourself | [`detailedWalletStats`](/api-reference/queries/detailedwalletstats.md) | Realized PnL, volume, swap counts as first-class fields. | | Wallet performance over time (hand-rolled) | [`walletChart`](/api-reference/queries/walletchart.md) | PnL and volume time-series; resolutions `60`, `240`, `1D`, `7D`. | | Top traders for a token (aggregated `DEXTradeByTokens`) | [`tokenTopTraders`](/api-reference/queries/tokentoptraders.md) | Top buyers/sellers/PnL, `tradingPeriod` = `DAY \| WEEK \| MONTH \| YEAR`. | | "Find profitable wallets" (custom aggregation) | [`filterWallets`](/api-reference/queries/filterwallets.md) | Query wallets by PnL, win-rate, or volume across all networks. | ### Discovery, pairs, and markets | Bitquery dataset / pattern | Codex equivalent | Notes | | :-- | :-- | :-- | | Aggregated `DEXTradeByTokens` ("GMGN-style" trending queries) | [`filterTokens`](/api-reference/queries/filtertokens.md) ranked by `trendingScore24` | First-class ranked discovery; no aggregation to write. See [Discover Tokens](/recipes/discover-tokens.md). | | Custom token search via `Currencies` filters | [`filterTokens(phrase: ...)`](/api-reference/queries/filtertokens.md) | Use `$SYMBOL` for exact symbol matches. | | DEX pool / market fields on trades | [`pairMetadata`](/api-reference/queries/pairmetadata.md), [`getDetailedPairStats`](/api-reference/queries/getdetailedpairstats.md), [`listPairsForToken`](/api-reference/queries/listpairsfortoken.md) | First-class pair concept with stats over multiple timeframes. | | Ranked pool discovery (aggregated) | [`filterPairs`](/api-reference/queries/filterpairs.md) | Rich filter clauses across all networks. | | DEX/protocol list (from `Trade.Dex`) | [`filterExchanges`](/api-reference/queries/filterexchanges.md) | | | Chain list | [`getNetworks`](/api-reference/queries/getnetworks.md) | Returns the `networkId`s you'll use everywhere else. | | Block lookups (`Block` fields) | [`blocks`](/api-reference/queries/blocks.md) | Look up blocks by number or timestamp. | ### Launchpads and prediction markets | Bitquery dataset / pattern | Codex equivalent | Notes | | :-- | :-- | :-- | | pump.fun via `Solana { Instructions }` (program `pump`) + `TokenSupplyUpdates` | [`onLaunchpadTokenEvent`](/api-reference/subscriptions/onlaunchpadtokenevent.md) + `launchpad` fields on [`token`](/api-reference/queries/token.md) | Codex abstracts the full launchpad lifecycle (bonding curve, graduation, migration) instead of raw instructions. See [Launchpads](/launchpads.md). | | Polymarket via `EVM(network: matic)` events (`OrderFilled`, `ConditionResolution`) | [`filterPredictionEvents`](/api-reference/queries/filterpredictionevents.md) family | Higher-level event/market/trader data covering **both Polymarket and Kalshi**. Growth or Enterprise plan. See [Prediction Markets](/prediction-markets.md). | ## Side-by-side examples The four patterns below are the ones most Bitquery integrations start from. Codex addresses come through as `address:networkId`; Bitquery keeps the network in the top-level block. ### 1. DEX trades for a token ```graphql Bitquery { EVM(dataset: combined, network: eth) { DEXTrades( limit: { count: 25 } orderBy: { descending: Block_Time } where: { Trade: { Buy: { Currency: { SmartContract: { is: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2" } } } } } ) { Block { Time } Transaction { Hash From } Trade { Buy { Amount Price Currency { Symbol } } Sell { Amount Currency { Symbol } } Dex { ProtocolName } } } } } ``` ```graphql Codex GraphQL query TokenTrades { getTokenEvents( query: { address: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", networkId: 1 } limit: 25 ) { items { timestamp eventDisplayType maker transactionHash data { ... on SwapEventData { amountNonLiquidityToken priceUsd } } } cursor } } ``` For a live feed instead of polling, subscribe to [`onTokenEventsCreated`](/api-reference/subscriptions/ontokeneventscreated.md). ### 2. OHLCV chart Bitquery has no stored candle — you either use the Crypto Price API's pre-aggregated `Ohlc` block or bucket `DEXTradeByTokens` by a time interval and derive open/high/low/close yourself. Codex returns bars directly. ```graphql Bitquery { EVM(network: eth, dataset: combined) { DEXTradeByTokens( orderBy: { ascendingByField: "Block_Time" } where: { Trade: { Currency: { SmartContract: { is: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2" } } } } ) { Block { Time(interval: { count: 1, in: hours }) } open: Trade_PriceInUSD(minimum: Block_Time) high: quantile(of: Trade_PriceInUSD, level: 1.0) low: quantile(of: Trade_PriceInUSD, level: 0.0) close: Trade_PriceInUSD(maximum: Block_Time) volume: sum(of: Trade_Side_AmountInUSD) } } } ``` ```graphql Codex GraphQL query TokenChart { getTokenBars( symbol: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2:1" from: 1716595200 to: 1717200000 resolution: "60" ) { t o h l c volume } } ``` Codex resolutions run `1S, 5S, 15S, 30S, 1, 5, 15, 30, 60, 240, 720, 1D, 7D` (1-second up to weekly). Sub-minute bars are retained for the last 24 hours. Layer in [`onBarsUpdated`](/api-reference/subscriptions/onbarsupdated.md) for live updates, and see the [Charts recipe](/recipes/charts.md) for a full Lightweight Charts integration. ### 3. Token holders ```graphql Bitquery { EVM(network: eth, dataset: combined) { Holders( limit: { count: 10 } orderBy: { descending: Balance_Amount } where: { Currency: { SmartContract: { is: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48" } } } ) { Holder { Address } Balance { Amount AmountInUSD } Currency { Symbol } } } } ``` ```graphql Codex GraphQL query TopHolders { holders(input: { tokenId: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48:1" }) { count top10HoldersPercent items { address balance shiftedBalance } } } ``` Codex returns `top10HoldersPercent` on the same response, so a concentration metric doesn't need a second query. Note that Bitquery deprecated the older `TokenHolders(date:)` dataset in June 2026 in favor of the `Holders` cube shown here; the Codex query is unaffected either way. ### 4. Wallet balances ```graphql Bitquery { EVM(network: eth, dataset: combined, aggregates: yes) { BalanceUpdates( where: { BalanceUpdate: { Address: { is: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045" } } } orderBy: { descendingByField: "balance" } ) { Currency { Name Symbol SmartContract } balance: sum(of: BalanceUpdate_Amount, selectWhere: { gt: "0" }) } } } ``` ```graphql Codex GraphQL query WalletBalances { balances( input: { walletAddress: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045" networks: [1] removeScams: true } ) { items { tokenId shiftedBalance balanceUsd tokenPriceUsd } cursor } } ``` Codex returns USD pricing inline, so you don't aggregate `BalanceUpdate_Amount` or join a separate price query. For wallet-level PnL and volume, see [`detailedWalletStats`](/api-reference/queries/detailedwalletstats.md) and the [Wallets recipe](/recipes/wallets/discover-traders.md). ## Real-time data Both APIs deliver real-time data over GraphQL WebSocket subscriptions, so the mechanics port cleanly — swap the `subscription { EVM { … } }` block for the matching Codex subscription. The difference is that Codex also offers [webhooks](/concepts/webhooks.md) (Bitquery does not), so server-side consumers don't have to hold open a socket. | You want updates for... | Codex subscription | Codex webhook | | :-- | :-- | :-- | | Token prices | [`onPriceUpdated`](/api-reference/subscriptions/onpriceupdated.md), [`onPricesUpdated`](/api-reference/subscriptions/onpricesupdated.md) | `TOKEN_PRICE_EVENT` | | OHLCV bars | [`onBarsUpdated`](/api-reference/subscriptions/onbarsupdated.md), [`onTokenBarsUpdated`](/api-reference/subscriptions/ontokenbarsupdated.md) | | | DEX trades (token / pair) | [`onTokenEventsCreated`](/api-reference/subscriptions/ontokeneventscreated.md), [`onEventsCreated`](/api-reference/subscriptions/oneventscreated.md) | `TOKEN_PAIR_EVENT` | | A wallet's trades | [`onEventsCreatedByMaker`](/api-reference/subscriptions/oneventscreatedbymaker.md) (input field `makerAddress`) | `TOKEN_PAIR_EVENT` with a `maker` filter | | Balance changes | [`onBalanceUpdated`](/api-reference/subscriptions/onbalanceupdated.md) | `TOKEN_TRANSFER_EVENT` | | Holder count changes | [`onHoldersUpdated`](/api-reference/subscriptions/onholdersupdated.md) | | | New tokens / launchpad events | [`onTokenLifecycleEventsCreated`](/api-reference/subscriptions/ontokenlifecycleeventscreated.md), [`onLaunchpadTokenEvent`](/api-reference/subscriptions/onlaunchpadtokenevent.md) | | | Market cap thresholds | [`onPricesUpdated`](/api-reference/subscriptions/onpricesupdated.md) | `MARKET_CAP_EVENT` | For enterprise-scale throughput, Bitquery offers Kafka and gRPC streams; Codex's equivalent for high-volume server-side delivery is [webhooks](/concepts/webhooks.md) via [`createWebhooks`](/api-reference/mutations/createwebhooks.md). ## Gaps Things Bitquery does that Codex doesn't, and what to do about them: - **Mempool / pending transactions** (`EVM(mempool: true)`). Codex indexes confirmed onchain data, not the pending pool. If you build MEV, sandwich detection, or pre-confirmation UX, keep Bitquery or an RPC/mempool provider for that surface. - **Raw and decoded logs, calls, and instructions across arbitrary contracts** (`Events`, `Calls`, Solana `Instructions`). Codex is a curated DEX/token API, not a general log indexer. For decoding arbitrary contract activity, pair Codex with an RPC provider or a log-indexing service. - **Ad-hoc aggregations over any onchain field.** Bitquery's cube model lets you group and aggregate almost anything. Codex exposes fixed, purpose-built aggregates (stats, PnL, holder concentration); it doesn't run arbitrary group-bys. - **Historical point-in-time balances.** Bitquery reconstructs a wallet's balance at any past date from `BalanceUpdates`. Codex returns current balances; for historical snapshots, combine event history yourself or keep Bitquery for that query. - **NFT trades and collection analytics.** Codex is a fungible-token API. Pair with a dedicated NFT provider (Reservoir, OpenSea, Alchemy NFT). - **Non-EVM/UTXO chains Codex doesn't index** (Bitcoin, Litecoin, Cardano, Tron, XRP, and others). Codex covers 80+ EVM networks plus Solana, Sui, Aptos, and Tron-family where indexed — check [Networks](/networks.md). For chains outside that list, keep Bitquery. - **Kafka / gRPC stream transports.** Codex delivers real-time via WebSocket subscriptions and webhooks, not Kafka topics or gRPC. ## What you pick up Things Codex offers that Bitquery makes you build or doesn't have: - **First-class token safety signals.** `isScam`, `mintable`, `freezable`, `creatorAddress`, and top-holder concentration are indexed on the [`token`](/api-reference/queries/token.md) object. In Bitquery you'd infer these from raw supply and authority data. - **Wallet PnL and trader discovery, ready-made.** [`detailedWalletStats`](/api-reference/queries/detailedwalletstats.md), [`walletChart`](/api-reference/queries/walletchart.md), [`tokenTopTraders`](/api-reference/queries/tokentoptraders.md), and [`filterWallets`](/api-reference/queries/filterwallets.md) return realized PnL, win-rate, and volume directly — no grouping `DEXTrades` by trader and computing profit yourself. - **Launchpad lifecycle as an abstraction.** pump.fun, LetsBonk, Believe, and other launchpads modeled as lifecycle events with bonding-curve state, graduation, and migration — instead of raw `Instructions` you decode. See [Launchpads](/launchpads.md). - **Prediction markets across venues.** Both Polymarket and Kalshi via the [`filterPredictionEvents`](/api-reference/queries/filterpredictionevents.md) family, at the event/market/trader level, not raw Polygon logs. - **First-class trending and discovery.** [`filterTokens`](/api-reference/queries/filtertokens.md) / [`filterPairs`](/api-reference/queries/filterpairs.md) ranked by trending score, volume, or market cap — no "GMGN-style" aggregation query to maintain. - **OHLCV without candle assembly.** Stored bars from 1-second to weekly via [`getTokenBars`](/api-reference/queries/gettokenbars.md) and [`getBars`](/api-reference/queries/getbars.md). - **Simpler ergonomics.** One API-key header (no OAuth token exchange), one endpoint, a one-call `getTokenPrices`, and an official TypeScript SDK ([`@codex-data/sdk`](/sdk.md)). Webhooks mean server consumers don't have to hold a socket open. - **Built for AI agents.** A [docs MCP server](/agents/docs-mcp.md), prebuilt [Codex Skills](/agents/codex-skills.md) for Claude/Cursor/Codex CLI, and pay-per-query access via [MPP](/agents/mpp.md). ## AI migration prompt Bitquery integrations tend to concentrate in a few files — a GraphQL client, a set of query strings, and the aggregation code that turns raw rows into prices, candles, or PnL. Hand the prompt below to an IDE agent (Claude Code, Cursor, Codex CLI, or similar), run it from the repo root, and it will find every Bitquery touchpoint, propose a plan, and execute the migration with your approval. Pair this prompt with our [Codex Skills](/agents/codex-skills.md) and [docs MCP server](/agents/docs-mcp.md) so the agent can look up Codex queries on demand instead of guessing at field names. ````markdown You are migrating this codebase from the Bitquery GraphQL API to Codex (https://docs.codex.io). Both are GraphQL APIs, so the query mechanics carry over, but the data model is different: Bitquery exposes low-level datasets (DEXTrades, BalanceUpdates, Holders, Transfers) that the codebase aggregates itself, while Codex exposes purpose-built fields (getTokenPrices, getTokenBars, holders, detailedWalletStats) that return the finished answer. Much of the aggregation code in this repo can be deleted, not ported. ## Phase 1: Discovery (do this first, do not edit yet) Search the codebase for every Bitquery integration point. At minimum, look for: - HTTP/WebSocket calls to `streaming.bitquery.io/graphql`, `streaming.bitquery.io/eap`, `graphql.bitquery.io` (legacy V1), or `oauth2.bitquery.io`. - OAuth token-exchange logic against `oauth2.bitquery.io/oauth2/token` (client_id/client_secret → access_token). - Header usage of `X-API-KEY` (legacy V1) or `Authorization: Bearer` against a Bitquery host. - Environment variables and config keys named `BITQUERY_*`, `BQ_*`, `*_CLIENT_ID`/`*_CLIENT_SECRET` used for Bitquery. - GraphQL query/subscription strings containing chain blocks (`EVM(`, `Solana`, `Tron`) or datasets (`DEXTrades`, `DEXTradeByTokens`, `BalanceUpdates`, `Holders`, `TokenHolders`, `Transfers`, `Currencies`, `Instructions`, `TokenSupplyUpdates`). - Aggregation/derivation code that turns Bitquery rows into prices, OHLC candles, holder concentration, or wallet PnL — this is the code most likely to be replaced by a single Codex field. - Tests, fixtures, and mocks that reference any of the above. Produce a Migration Plan with: 1. A grouped list of every call site, organized by Bitquery dataset. 2. The proposed Codex equivalent for each group (use the mapping below). 3. Aggregation/derivation logic that can be deleted because Codex returns the value directly (call these out explicitly). 4. Any call sites you cannot map cleanly, flagged for human review. 5. The order you intend to make changes (shared client/auth/config first, then leaf call sites, then tests). 6. New dependencies, env vars, and config you will introduce. Stop and surface the plan before editing any source files. Wait for confirmation. ## Phase 2: Execution (after the plan is approved) Ground rules: 1. Codex is a GraphQL API at `https://graph.codex.io/graphql`. Auth is a single `Authorization: ` header for long-lived keys, or `Authorization: Bearer ` for short-lived keys. There is no OAuth token-exchange step — remove the `oauth2.bitquery.io` client-credentials flow and store one API key. 2. Prefer the official TypeScript SDK (`@codex-data/sdk`) for TS/JS projects. There is no official SDK for Python or other languages: for those, call raw GraphQL against `https://graph.codex.io/graphql`. 3. Network is a numeric parameter (`networkId`), not a top-level chain block. Convert Bitquery chain blocks to Codex network IDs: `EVM(network: eth)` → 1, `Solana` → 1399811149, `EVM(network: base)` → 8453, `bsc` → 56, `matic`/`polygon` → 137, `arbitrum` → 42161, `optimism` → 10, `avalanche` → 43114. For others, call `getNetworks` once and build a lookup. 4. Token IDs in Codex are the string `"
:"`. Pair IDs use the same shape. Construct them explicitly. 5. Delete aggregation you no longer need. Replace "fetch DEXTrades and read the latest PriceInUSD" with `getTokenPrices`; "bucket DEXTradeByTokens into candles" with `getTokenBars`/`getBars`; "group DEXTrades by trader to compute profit" with `detailedWalletStats`/`walletChart`/`tokenTopTraders`; "aggregate BalanceUpdates to current holdings" with `balances` (which returns `balanceUsd`/`tokenPriceUsd` inline — do not add a separate price query). 6. `getTokenPrices` is capped at 25 inputs per request; `balances` takes `networks: [Int!]` and max 200 tokens; `holders` defaults to 50 / max 200 per page. Adjust batching and pagination accordingly. 7. For real-time, port `subscription { EVM/Solana { ... } }` blocks to the matching Codex subscription (see mapping). Use webhooks (`createWebhooks`) for server-side consumers that shouldn't hold a socket open. 8. Preserve existing public function signatures, return shapes, and error semantics wherever possible. Internal helpers can be refactored freely. 9. Update tests as you change code. If a test relied on a Bitquery response fixture, replace the fixture with a Codex equivalent rather than deleting the test. 10. When you hit a gap (mempool/pending txs, decoded logs/calls/instructions across arbitrary contracts, ad-hoc aggregations, historical point-in-time balances, NFT data, non-EVM chains Codex doesn't index like Bitcoin/Cardano/XRP, Kafka/gRPC transports), do not silently drop the feature. Leave the call site intact, add a `TODO(migration):` comment with a one-line note explaining what's missing and what provider could fill it, and list it in your final report. ## Bitquery → Codex mapping DEX trades and events: - `EVM { DEXTrades }` / `Solana { DEXTrades }` → `getTokenEvents(query: { address, networkId })` - `DEXTradeByTokens` → `getTokenEvents` (per-side buy/sell volume is first-class via `getDetailedTokenStats`, not a manual group-by) - `DEXTrades` filtered by trader → `getTokenEventsForMaker(query: { maker, networkId })` - Solana DEX `Instructions` → `getTokenEvents` with `eventDisplayType` (raw instruction decoding is a gap) Prices and OHLCV: - latest `Trade.PriceInUSD` → `getTokenPrices(inputs: [{ address, networkId }])` (≤25 inputs; add `timestamp` for historical) - Crypto Price API `Ohlc` / hand-rolled candles from `DEXTradeByTokens` → `getTokenBars` (token) or `getBars` (pair), resolutions `1S`–`7D` Token data: - `Currencies` / token fields → `token` + `tokens` - `TokenSupplyUpdates` → `token.info` (`circulatingSupply`, `totalSupply`) - inferred safety → `isScam`, `mintable`, `freezable`, `creatorAddress`, `top10HoldersPercent` on `token` - aggregated volume/txn stats → `getDetailedTokenStats(tokenAddress, networkId, durations: [...])` Holders and balances: - `Holders` (formerly `TokenHolders(date:)`, removed 2026-06-15) → `holders(input: { tokenId: "address:networkId" })` (+ `top10HoldersPercent`) — Growth/Enterprise - `BalanceUpdates` → `balances(input: { walletAddress, networks: [networkId] })` — Growth/Enterprise; USD pricing inline - historical point-in-time `BalanceUpdates` → not supported (flag) - `Transfers` → `getTokenEvents` / `getTokenEventsForMaker` (DEX events only; arbitrary transfers are a gap) Wallets and traders: - `DEXTrades` grouped by trader → `detailedWalletStats`, `walletChart` (PnL/volume time-series), `tokenTopTraders` - "find profitable wallets" → `filterWallets(input: { rankings: [{ attribute: realizedProfitUsd1d, direction: DESC }] })` Discovery, pairs, markets: - aggregated "GMGN-style" trending → `filterTokens(rankings: { attribute: trendingScore24, direction: DESC })` - token search → `filterTokens(phrase: "$SYMBOL")` - pool/market fields → `pairMetadata`, `getDetailedPairStats`, `listPairsForToken`, `filterPairs` - DEX list → `filterExchanges`; chain list → `getNetworks`; blocks → `blocks` Launchpads and prediction markets: - pump.fun `Instructions` + `TokenSupplyUpdates` → `launchpad` fields on `token` + `onLaunchpadTokenEvent` - Polymarket via `EVM(network: matic)` events → `filterPredictionEvents` (covers Polymarket + Kalshi) — Growth/Enterprise Real-time (subscription → Codex subscription): - `subscription { EVM/Solana { DEXTrades } }` → `onTokenEventsCreated` / `onEventsCreated` - wallet trades → `onEventsCreatedByMaker` (input field `makerAddress`, not `maker`) - prices → `onPriceUpdated` / `onPricesUpdated` - balances → `onBalanceUpdated`; holders → `onHoldersUpdated` - new tokens / launchpads → `onTokenLifecycleEventsCreated` / `onLaunchpadTokenEvent` Gaps (flag, do not drop): - mempool/pending txs (`EVM(mempool: true)`): not supported - decoded logs/calls/instructions across arbitrary contracts: not supported (curated DEX/token API) - ad-hoc aggregations over arbitrary fields: not supported (fixed purpose-built aggregates only) - historical point-in-time balances: not supported - NFT trades/collections: not supported - non-EVM chains Codex doesn't index (Bitcoin, Cardano, XRP, etc.): not supported - Kafka/gRPC stream transports: use WebSocket subscriptions or webhooks instead When you need details on any Codex field, fetch the reference page at `https://docs.codex.io/api-reference/queries/` (or `subscriptions`, `mutations`) rather than guessing. Before migrating any real-time code, fetch `https://docs.codex.io/concepts/subscriptions` and `https://docs.codex.io/concepts/webhooks` so you pick the right delivery mechanism. ## Phase 3: Final report When the migration is done, produce a single report with: 1. Files changed, grouped by area (client/auth/config, call sites, tests, docs). 2. Aggregation/derivation code deleted because Codex returns the value directly. 3. Every `TODO(migration):` you added, with file path, line, and the reason. 4. New env vars and dependencies, with the line to add to `.env.example` and the package manager command to install. 5. Bitquery integrations that were removed entirely, and what replaced them. 6. A short manual-verification checklist the human should run before merging (which features to click through, which queries to spot-check, which dashboards to load). Run the project's linter and test suite before declaring the migration complete. If tests fail, fix the underlying integration, do not weaken the test. ```` ## Getting help - Browse the [API Reference](/api-reference/introduction.md) for the full schema. - Skim the [Recipes](/recipes/discover-tokens.md) for end-to-end examples that solve specific product problems. - Ask in [our community](https://t.me/codex_community) if you hit a wall during migration. # CoinGecko to Codex Move your CoinGecko integration to Codex for deeper onchain data, real-time events, and wallet analytics Codex competes most directly with CoinGecko's **OnChain DEX API** (the `/onchain/*` endpoints, powered by GeckoTerminal): pool, token, trade, and OHLCV data addressed by contract address. That's where this guide focuses. CoinGecko's classic market data API (coin-ID slugs like `bitcoin`, CEX tickers, derivatives, NFTs, treasury holdings, global aggregates) is a separate surface. Codex covers parts of it, doesn't cover others, and we call out which is which lower down on the page. If you only use CoinGecko for CEX tickers, derivatives, NFTs, treasury holdings, global market cap aggregates, or coin-ID lookups against non-DEX endpoints, Codex isn't a drop-in replacement. See [Gaps](#gaps) below for the specifics. If you use the `/onchain/*` endpoints (or `simple/token_price` and `coins/{id}/contract/{address}` with contract addresses), this guide is for you. Every CoinGecko field you rely on has a Codex equivalent, most Codex responses carry extra fields CoinGecko doesn't expose (safety signals, launchpad state, multi-timeframe stats), and you pick up entire surfaces CoinGecko has no answer for: live trade streams, wallet PnL and discovery, launchpad lifecycle events, and prediction markets. ## Mental model CoinGecko's OnChain DEX API is a REST surface where the network is a URL path segment (`/onchain/networks/eth/...`, `/onchain/networks/solana/...`) and every resource (token, pool, OHLCV, trades, holders) has its own endpoint with its own response shape. Codex is a single GraphQL Supergraph: one endpoint (`https://graph.codex.io/graphql`), one auth header, one query language, and the network is a numeric `networkId` parameter on each field. What that means in practice: - **Network is a parameter, not a URL.** You stop building `/onchain/networks/${chain}/tokens/${address}` strings. Pass `{ address, networkId: 1 }` to a Codex field and any of [80+ networks](/networks.md) routes through the same query. - **Token and pair IDs are explicit strings.** Codex addresses every token by `"
:"` and every pair by the same shape. Once your network-slug → `networkId` map is in place (`eth` → 1, `solana` → 1399811149, `base` → 8453, ...), everything composes cleanly. - **You compose data in one request.** GraphQL lets you fetch token metadata, current price, holders, recent trades, and OHLCV bars in a single round trip instead of three or four sequential REST calls. - **Real-time is first-class.** Most CoinGecko integrations poll on intervals. Codex gives you [WebSocket subscriptions](/concepts/subscriptions.md) and [webhooks](/concepts/webhooks.md) for live prices, trades, holders, and balances. If you also touch the coin-ID-based market data endpoints (`/simple/price?ids=...`, `/coins/{id}`), there's a one-time slug-to-address translation step covered in [Coin IDs vs contract addresses](#coin-ids-vs-contract-addresses) below. If you've never used GraphQL, [Learn GraphQL](/learn-graphql.md) is a 10-minute primer that's enough to follow the rest of this guide. ## Authentication CoinGecko uses one of two API key headers depending on plan: `x-cg-demo-api-key` for the free demo tier or `x-cg-pro-api-key` for paid plans (which also flips the base URL to `pro-api.coingecko.com`). Codex uses a single `Authorization` header with a key from the [dashboard](https://dashboard.codex.io?utm_source=codex&utm_medium=docs&utm_campaign=migrations-coingecko). ```bash CoinGecko curl "https://pro-api.coingecko.com/api/v3/onchain/simple/networks/eth/token_price/0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2" \ -H "x-cg-pro-api-key: $COINGECKO_API_KEY" ``` ```bash Codex curl https://graph.codex.io/graphql \ -H "Authorization: $CODEX_API_KEY" \ -H "Content-Type: application/json" \ -d '{"query":"{ getTokenPrices(inputs: [{ address: \"0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2\", networkId: 1 }]) { address priceUsd timestamp } }"}' ``` For browser-facing apps, generate a short-lived JWT with [`createApiTokens`](/api-reference/mutations/createapitokens.md) and pass it as `Bearer `. See [Authentication](/concepts/authentication.md) for the full pattern. ## Endpoint mapping ### OnChain DEX API: tokens The most-used CoinGecko OnChain endpoints. Codex addresses tokens by `address:networkId` where CoinGecko addresses them by `{network}/{address}`, but the data behind each endpoint is the same shape. | CoinGecko OnChain | Codex equivalent | Notes | | :-- | :-- | :-- | | `GET /onchain/networks/{network}/tokens/{address}` | [`token`](/api-reference/queries/token.md) | Richer metadata including safety signals, launchpad context, social links. | | `GET /onchain/networks/{network}/tokens/multi/{addresses}` | [`tokens`](/api-reference/queries/tokens.md) | Batch token metadata. | | `GET /onchain/networks/{network}/tokens/{address}/info` | [`token`](/api-reference/queries/token.md) | | | `GET /onchain/simple/networks/{network}/token_price/{addresses}` | [`getTokenPrices`](/api-reference/queries/gettokenprices.md) | For the `include_*` flags (`include_market_cap`, `include_24hr_vol`, `include_24hr_price_change`, `include_total_reserve_in_usd`), use [`filterTokens`](/api-reference/queries/filtertokens.md), whose results expose `marketCap`/`circulatingMarketCap`, `volume24`, `change24`, and `liquidity` directly. (`getDetailedTokenStats` covers bucketed volume and OHLC, but does not expose market cap.) | | `GET /onchain/networks/{network}/tokens/{address}/ohlcv/{timeframe}` | [`getTokenBars`](/api-reference/queries/gettokenbars.md) | | | `GET /onchain/networks/{network}/tokens/{address}/trades` | [`getTokenEvents`](/api-reference/queries/gettokenevents.md) | CoinGecko's token-level trades endpoint is Analyst+ and capped at the last 300 trades over 24 hours (across all of the token's pools); Codex retains the full event history on every plan. Note: `getTokenEvents` is pair-scoped (a token address resolves to its top pair), so to cover every pool either iterate the token's pairs or stream token-wide via [`onTokenEventsCreated`](/api-reference/subscriptions/ontokeneventscreated.md). | | `GET /onchain/networks/{network}/tokens/{address}/top_holders` | [`holders`](/api-reference/queries/holders.md) (+ [`top10HoldersPercent`](/api-reference/queries/top10holderspercent.md) for the aggregate) | | | `GET /onchain/networks/{network}/tokens/{address}/top_traders` | [`tokenTopTraders`](/api-reference/queries/tokentoptraders.md) | Supports time-range filters and PnL data. | | `GET /onchain/networks/{network}/tokens/{address}/holders_chart` | Partial via [`onHoldersUpdated`](/api-reference/subscriptions/onholdersupdated.md) | Live count; historical chart is not a first-class endpoint. | | `GET /onchain/tokens/info_recently_updated` | [`filterTokens`](/api-reference/queries/filtertokens.md) ranked by recent activity | Cross-network on CoinGecko; filter by `network` in Codex if you need to scope it. | ### OnChain DEX API: pools and pairs | CoinGecko OnChain | Codex equivalent | Notes | | :-- | :-- | :-- | | `GET /onchain/networks/{network}/pools/{pool}` | [`getDetailedPairStats`](/api-reference/queries/getdetailedpairstats.md) | | | `GET /onchain/networks/{network}/pools/multi/{addresses}` | [`getDetailedPairsStats`](/api-reference/queries/getdetailedpairsstats.md) | | | `GET /onchain/networks/{network}/tokens/{address}/pools` | [`listPairsForToken`](/api-reference/queries/listpairsfortoken.md) | | | `GET /onchain/networks/{network}/pools/{pool}/info` | [`pairMetadata`](/api-reference/queries/pairmetadata.md) | | | `GET /onchain/networks/{network}/pools/{pool}/trades` | [`getTokenEvents`](/api-reference/queries/gettokenevents.md) with a pair filter | Last 300 trades over 24 hours vs. Codex's full event history. | | `GET /onchain/networks/{network}/pools/{pool}/ohlcv/{timeframe}` | [`getBars`](/api-reference/queries/getbars.md) | Both APIs cover sub-minute candles; Codex extends one step further with `7D` (weekly) bars. | | `GET /onchain/networks/{network}/pools` | [`filterPairs`](/api-reference/queries/filterpairs.md) with `filters: { network: [] }` | Top pools on a single network. | | `GET /onchain/networks/{network}/new_pools`, `/onchain/networks/new_pools` | [`filterPairs`](/api-reference/queries/filterpairs.md) ranked by `createdAt` | Or subscribe to [`onTokenLifecycleEventsCreated`](/api-reference/subscriptions/ontokenlifecycleeventscreated.md). | | `GET /onchain/networks/trending_pools`, `/onchain/networks/{network}/trending_pools` | [`filterPairs`](/api-reference/queries/filterpairs.md) ranked by `trendingScore24` | | | `GET /onchain/pools/trending_search` | [`filterPairs(phrase: ..., rankings: { attribute: trendingScore24 })`](/api-reference/queries/filterpairs.md) | Trending matches for a search phrase. | | `GET /onchain/networks/{network}/dexes/{dex}/pools` | [`filterPairs`](/api-reference/queries/filterpairs.md) filtered by exchange | | | `GET /onchain/pools/megafilter` | [`filterPairs`](/api-reference/queries/filterpairs.md) | Rich filter clauses across all networks. | ### OnChain DEX API: networks, dexes, search | CoinGecko OnChain | Codex equivalent | Notes | | :-- | :-- | :-- | | `GET /onchain/networks` | [`getNetworks`](/api-reference/queries/getnetworks.md) | | | `GET /onchain/networks/{network}/dexes` | [`filterExchanges`](/api-reference/queries/filterexchanges.md) | | | `GET /onchain/search/pools` | [`filterPairs(phrase: ...)`](/api-reference/queries/filterpairs.md) | | | `GET /onchain/categories`, `GET /onchain/categories/{id}/pools` | Not supported | See [Gaps](#gaps) — Codex doesn't curate DEX pool categories. | ### CoinGecko market data (coin-ID based) These endpoints sit on the CoinGecko market data side rather than the OnChain DEX side. Most DEX-focused integrations don't hit them, but if yours does, here's the mapping. | CoinGecko | Codex equivalent | Notes | | :-- | :-- | :-- | | `GET /simple/price?ids=bitcoin,ethereum` | [`getTokenPrices`](/api-reference/queries/gettokenprices.md) (+ [`filterTokens`](/api-reference/queries/filtertokens.md)) | Resolve coin IDs to contract addresses first; see [Coin IDs vs contract addresses](#coin-ids-vs-contract-addresses) below. If you use `include_24hr_change`, `include_24hr_vol`, or `include_market_cap`, pull `change24`, `volume24`, and `marketCap`/`circulatingMarketCap` from `filterTokens` (market cap is not on `getDetailedTokenStats`). | | `GET /simple/token_price/{platform}?contract_addresses=...` | [`getTokenPrices`](/api-reference/queries/gettokenprices.md) | Direct mapping; `{platform}` becomes `networkId`. Same caveat as above for `include_*` flags. | | `GET /coins/{id}` | [`token`](/api-reference/queries/token.md) + [`getDetailedTokenStats`](/api-reference/queries/getdetailedtokenstats.md) (+ [`filterTokens`](/api-reference/queries/filtertokens.md) for market cap) | Resolve ID first. `token` returns metadata, safety, launchpad; `getDetailedTokenStats` adds volume/OHLC stats. For `market_cap`, read `marketCap`/`circulatingMarketCap` from `filterTokens` (neither `token` nor `getDetailedTokenStats` exposes it). | | `GET /coins/{id}/market_chart`, `/market_chart/range` | [`getTokenBars`](/api-reference/queries/gettokenbars.md) | OHLCV from 1-second up to weekly (`7D`). | | `GET /coins/{id}/ohlc`, `/ohlc/range` | [`getTokenBars`](/api-reference/queries/gettokenbars.md) | | | `GET /coins/{id}/contract/{address}/market_chart`, `/market_chart/range` | [`getTokenBars`](/api-reference/queries/gettokenbars.md) | Already contract-addressed; no slug resolution. | | `GET /coins/{id}/history?date=...` | [`getBars`](/api-reference/queries/getbars.md) with a single bar covering the date | | | `GET /coins/markets` | [`filterTokens`](/api-reference/queries/filtertokens.md) with ranking | Filter by network, liquidity, market cap; rank by volume, trending, etc. | | `GET /coins/list`, `/token_lists/{asset_platform_id}/all.json` | [`filterTokens`](/api-reference/queries/filtertokens.md) (optionally `filters: { network: [] }`) | Filter at the point of use instead of maintaining a full list. | | `GET /coins/list/new` | [`filterTokens`](/api-reference/queries/filtertokens.md) ranked by `createdAt`, or [`onTokenLifecycleEventsCreated`](/api-reference/subscriptions/ontokenlifecycleeventscreated.md) | | | `GET /coins/top_gainers_losers` | [`filterTokens`](/api-reference/queries/filtertokens.md) ranked by `change24` | `change24` is the 24h price-change ranking attribute on tokens (the pair-side analogue is `priceChange24`). | | `GET /coins/{id}/tickers` | [`listPairsForToken`](/api-reference/queries/listpairsfortoken.md) + [`listPairsWithMetadataForToken`](/api-reference/queries/listpairswithmetadatafortoken.md) | DEX pairs only; CEX tickers are out of scope. | | `GET /coins/{id}/contract/{contract_address}` | [`token`](/api-reference/queries/token.md) | The contract-address form is already the Codex native shape. | | `GET /coins/{id}/circulating_supply_chart`, `/total_supply_chart` (+ `/range`) | Not supported | See [Gaps](#gaps) — no historical supply timeseries. Current supply is on `token.info`. | | `GET /search?query=...` | [`filterTokens(phrase: "$SYMBOL", ...)`](/api-reference/queries/filtertokens.md) | Use `$SYMBOL` prefix for exact symbol matches. | | `GET /search/trending` | [`filterTokens(rankings: { attribute: trendingScore24, direction: DESC })`](/api-reference/queries/filtertokens.md) | Tokens only; trending NFTs and categories are out of scope. See the [Discover Tokens recipe](/recipes/discover-tokens.md). | | `GET /coins/categories`, `/coins/categories/list` | Not supported | See [Gaps](#gaps). | | `GET /simple/supported_vs_currencies`, `/exchange_rates` | Not supported | Codex returns USD only. Pair with an FX provider. | ### Utilities | CoinGecko | Codex equivalent | Notes | | :-- | :-- | :-- | | `GET /asset_platforms` | [`getNetworks`](/api-reference/queries/getnetworks.md) | Returns `networkId`s you'll use everywhere else. | | `GET /ping` | Not needed | Codex doesn't require health-checking. | | `GET /key` | Codex usage in the [dashboard](https://dashboard.codex.io) | CoinGecko's `/key` returns your plan's usage, rate limits, and remaining credits; Codex surfaces the same in the dashboard. | ## Coin IDs vs contract addresses This only matters if your integration touches CoinGecko's coin-ID-based endpoints (`/simple/price?ids=...`, `/coins/{id}`, `/coins/{id}/market_chart`). CoinGecko addresses tokens by slug (`bitcoin`, `ethereum`, `pepe`); Codex takes a contract address plus a `networkId`. The OnChain DEX endpoints already use contract addresses, so they translate cleanly. The cleanest path is to keep a small static mapping for the head tokens you reference by ID (BTC, ETH, USDC, USDT, SOL, etc.), and use [`filterTokens(phrase: ...)`](/api-reference/queries/filtertokens.md) at runtime for the long tail. If you currently call `GET /coins/list` to maintain a coin-ID map, replace it with that filter at the point of use. ## Side-by-side examples ### 1. Token price by contract address ```bash CoinGecko curl "https://pro-api.coingecko.com/api/v3/onchain/simple/networks/eth/token_price/0x2260fac5e5542a773aa44fbcfedf7c193bc2c599,0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2" \ -H "x-cg-pro-api-key: $COINGECKO_API_KEY" ``` ```typescript Codex SDK const sdk = new Codex(process.env.CODEX_API_KEY!) const { getTokenPrices } = await sdk.queries.getTokenPrices({ inputs: [ { address: "0x2260fac5e5542a773aa44fbcfedf7c193bc2c599", networkId: 1 }, // WBTC { address: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", networkId: 1 }, // WETH ], }) getTokenPrices.forEach((p) => console.log(p.address, p.priceUsd)) ``` ```graphql Codex GraphQL query MultiPrice { getTokenPrices( inputs: [ { address: "0x2260fac5e5542a773aa44fbcfedf7c193bc2c599", networkId: 1 } { address: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", networkId: 1 } ] ) { address networkId priceUsd timestamp } } ``` For a live price feed instead of polling, subscribe to [`onPricesUpdated`](/api-reference/subscriptions/onpricesupdated.md). If you're calling `/simple/price?ids=...` instead, see [Coin IDs vs contract addresses](#coin-ids-vs-contract-addresses) for the slug-to-address translation step. ### 2. Pool detail CoinGecko's `/onchain/networks/{network}/pools/{pool}` returns a pool snapshot. Codex returns the same shape plus stats over multiple timeframes in a single query. ```bash CoinGecko curl "https://pro-api.coingecko.com/api/v3/onchain/networks/eth/pools/0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640?include=base_token,quote_token,dex" \ -H "x-cg-pro-api-key: $COINGECKO_API_KEY" ``` ```graphql Codex GraphQL query PairOverview { getDetailedPairStats( pairAddress: "0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640" networkId: 1 durations: [hour1, day1] ) { pairAddress networkId stats_day1 { statsUsd { volume { currentValue change } buyVolume { currentValue } sellVolume { currentValue } } statsNonCurrency { transactions { currentValue change } buyers { currentValue } sellers { currentValue } } } stats_hour1 { statsUsd { volume { currentValue change } } statsNonCurrency { transactions { currentValue } } } } pairMetadata(pairId: "0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640:1") { liquidity fee exchangeId price priceChange24 volume24 token0 { name symbol address } token1 { name symbol address } } } ``` ### 3. OHLCV chart ```bash CoinGecko curl "https://pro-api.coingecko.com/api/v3/onchain/networks/eth/pools/0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640/ohlcv/hour?aggregate=1&limit=100" \ -H "x-cg-pro-api-key: $COINGECKO_API_KEY" ``` ```graphql Codex GraphQL query PairChart { getBars( symbol: "0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640:1" from: 1716595200 to: 1717200000 resolution: "60" ) { t o h l c volume } } ``` Codex's resolution set is `1S, 5S, 15S, 30S, 1, 5, 15, 30, 60, 240, 720, 1D, 7D` (1-second up to weekly), one step longer than CoinGecko's longest bar (`day`). Sub-minute bars are only retained for the last 24 hours. Layer in [`onBarsUpdated`](/api-reference/subscriptions/onbarsupdated.md) for live chart updates, and see the [Charts recipe](/recipes/charts.md) for a full Lightweight Charts integration. ### 4. Trending pools ```bash CoinGecko curl "https://pro-api.coingecko.com/api/v3/onchain/networks/trending_pools?include=base_token,quote_token,dex" \ -H "x-cg-pro-api-key: $COINGECKO_API_KEY" ``` ```graphql Codex GraphQL query TrendingPairs { filterPairs( filters: { liquidity: { gt: 100000 } } rankings: { attribute: trendingScore24, direction: DESC } limit: 25 ) { results { pair { address token0 token1 exchangeHash } liquidity volumeUSD24 priceChange24 marketCap } } } ``` For trending tokens (rather than pools), use [`filterTokens`](/api-reference/queries/filtertokens.md) with the same ranking attribute. See [Discover Tokens](/recipes/discover-tokens.md). ## Real-time data CoinGecko offers WebSocket streams on higher tiers, but most CoinGecko integrations are built on polling. Moving to Codex usually means replacing polling loops with [WebSocket subscriptions](/concepts/subscriptions.md) (for user-facing dashboards and trading UIs) or [webhooks](/concepts/webhooks.md) (for server-side alerts and queues). | You want updates for... | Codex subscription | Codex webhook | | :-- | :-- | :-- | | Token prices | [`onPriceUpdated`](/api-reference/subscriptions/onpriceupdated.md), [`onPricesUpdated`](/api-reference/subscriptions/onpricesupdated.md) | `TOKEN_PRICE_EVENT` | | OHLCV bars | [`onBarsUpdated`](/api-reference/subscriptions/onbarsupdated.md), [`onTokenBarsUpdated`](/api-reference/subscriptions/ontokenbarsupdated.md) | | | Token-level stats | [`onDetailedTokenStatsUpdated`](/api-reference/subscriptions/ondetailedtokenstatsupdated.md) | | | Pair-level stats | [`onDetailedStatsUpdated`](/api-reference/subscriptions/ondetailedstatsupdated.md) | | | Market cap thresholds | [`onPricesUpdated`](/api-reference/subscriptions/onpricesupdated.md) (derive: price × circulating supply) | `MARKET_CAP_EVENT` (thresholds on `fdvMarketCapUsd` / `circulatingMarketCapUsd`) | | Token trade events | [`onTokenEventsCreated`](/api-reference/subscriptions/ontokeneventscreated.md), [`onEventsCreated`](/api-reference/subscriptions/oneventscreated.md) | `TOKEN_PAIR_EVENT` (swaps, mints, burns) | | Token transfers | — | `TOKEN_TRANSFER_EVENT` | | Holder count changes | [`onHoldersUpdated`](/api-reference/subscriptions/onholdersupdated.md) | Approximate via `TOKEN_TRANSFER_EVENT` | | New pools/listings | [`onTokenLifecycleEventsCreated`](/api-reference/subscriptions/ontokenlifecycleeventscreated.md), [`onLaunchpadTokenEvent`](/api-reference/subscriptions/onlaunchpadtokenevent.md) | | ## Gaps Things CoinGecko covers that Codex doesn't, and what to do about them: - **CEX tickers, exchange metadata, and order-book data** (`/coins/{id}/tickers` for centralized exchanges, `/exchanges`, `/exchanges/list`, `/exchanges/{id}`, `/exchanges/{id}/tickers`, `/exchanges/{id}/volume_chart`, `/exchanges/{id}/volume_chart/range`). Codex is onchain-only. Keep CoinGecko for CEX coverage or pair with an exchange-data provider. - **Derivatives and futures** (`/derivatives`, `/derivatives/exchanges`, `/derivatives/exchanges/{id}`, `/derivatives/exchanges/list`). Codex doesn't cover perps or futures venues. - **NFTs** (`/nfts/*`, NFT floor prices, collection data, trending NFTs from `/search/trending`). Codex is a fungible-token API. Pair with a dedicated NFT provider (Reservoir, OpenSea, Alchemy NFT). - **Treasury holdings and entity data** (the current entity-based surface: `/entities/list`, `/{entity}/public_treasury/{coin_id}`, `/public_treasury/{entity_id}`, `/public_treasury/{entity_id}/{coin_id}/holding_chart`, `/public_treasury/{entity_id}/transaction_history`, plus the legacy `/companies/public_treasury/{coin_id}` shape). No equivalent. - **Global market data aggregates** (`/global`, `/global/decentralized_finance_defi`, `/global/market_cap_chart`). Codex doesn't produce "total crypto market cap" rollups. - **Historical supply timeseries** (`/coins/{id}/circulating_supply_chart`, `/total_supply_chart`, and their `/range` variants). Codex exposes current supply on `token.info`, not historical curves. - **Crypto news** (`/news`). Out of scope. - **Fiat exchange rates beyond USD** (`/exchange_rates`, `/simple/supported_vs_currencies`). Codex returns USD pricing only; pair with an FX provider for other quote currencies. - **Hand-curated categories** (`/coins/categories`, `/coins/categories/list`, `/onchain/categories`, `/onchain/categories/{id}/pools`, trending categories from `/search/trending`). Codex doesn't classify tokens or pools into curated categories. ## What you pick up Things Codex offers that CoinGecko doesn't: - **Full trade history with real-time streaming.** Per-trade fields are roughly comparable between the two APIs (maker address, token-in/token-out amounts, USD price at execution, transaction hash). The differentiator is depth and delivery: Codex retains every swap per token and per pair and pushes new ones live via [`onTokenEventsCreated`](/api-reference/subscriptions/ontokeneventscreated.md) or [`onEventsCreated`](/api-reference/subscriptions/oneventscreated.md), where CoinGecko's onchain trade endpoints cap at the last 300 trades over 24 hours with no streaming surface. - **Wallet analytics.** [`balances`](/api-reference/queries/balances.md), [`detailedWalletStats`](/api-reference/queries/detailedwalletstats.md), [`walletChart`](/api-reference/queries/walletchart.md), and [`filterWallets`](/api-reference/queries/filterwallets.md) let you build portfolio screens, PnL summaries, and "smart money" discovery flows. None of this is in CoinGecko. - **Launchpad lifecycle data.** First-class support for pump.fun, LetsBonk, Believe, and other launchpads, including bonding-curve state, graduation, and migration events. See [Launchpads](/launchpads.md). - **Prediction markets.** Polymarket and Kalshi event, market, trade, and trader data via the [`filterPredictionEvents`](/api-reference/queries/filterpredictionevents.md) family (Growth or Enterprise plan). See [Prediction Markets](/prediction-markets.md). - **Liquidity locks.** [`liquidityLocks`](/api-reference/queries/liquiditylocks.md) surfaces locked-LP context that CoinGecko doesn't track. - **Webhooks.** Push real-time data to your servers without holding open a WebSocket. Configure via [`createWebhooks`](/api-reference/mutations/createwebhooks.md). - **One query, many shapes.** GraphQL lets you combine token metadata, price, holders, recent trades, pair stats, and chart data into a single request. Three-or-four-call sequences collapse to one. - **Built for AI agents.** A [docs MCP server](/agents/docs-mcp.md), prebuilt [Codex Skills](/agents/codex-skills.md) for Claude/Cursor/Codex CLI, and pay-per-query access via [MPP](/agents/mpp.md). ## AI migration prompt Most CoinGecko integrations spread across many call sites: a price service, a chart loader, a token search, a trending feed, polling loops, maybe a few `/onchain` calls for pool data. Hand the prompt below to an IDE agent (Claude Code, Cursor, Codex CLI, or similar), run it from the repo root, and it will discover every CoinGecko touchpoint, propose a plan, and execute the migration with your approval. Pair this prompt with our [Codex Skills](/agents/codex-skills.md) and [docs MCP server](/agents/docs-mcp.md) so the agent can look up Codex queries on demand instead of guessing at field names. ````markdown You are migrating this codebase from the CoinGecko API to Codex (https://docs.codex.io). CoinGecko has two distinct API surfaces: a coin-ID-based market data API and a contract-address-based OnChain DEX API. Most of the OnChain DEX API maps cleanly to Codex; some of the market data API does not. ## Phase 1: Discovery (do this first, do not edit yet) Search the codebase for every CoinGecko integration point. At minimum, look for: - HTTP calls to `api.coingecko.com`, `pro-api.coingecko.com`, or `api.geckoterminal.com` (any path). - Imports of any CoinGecko SDK (`coingecko-api-v3`, `@coingecko/coingecko-typescript`, `pycoingecko`, etc.) or GeckoTerminal client. - Environment variables and config keys named `COINGECKO_*`, `CG_*`, `GECKOTERMINAL_*`. - Header usage of `x-cg-pro-api-key` or `x-cg-demo-api-key`. - Hardcoded coin-ID slugs (`"bitcoin"`, `"ethereum"`, `"solana"`, `"pepe"`, etc.) used to address CoinGecko endpoints. - Network-slug mappings (`eth`, `polygon_pos`, `bsc`, `solana`) used in `/onchain` paths. - Tests, fixtures, and mocks that reference any of the above. Produce a Migration Plan with: 1. A grouped list of every call site, organized by CoinGecko endpoint. 2. The proposed Codex equivalent for each group (use the mapping below). 3. Any call sites you cannot map cleanly, flagged for human review. 4. The order you intend to make changes (shared client/config first, then leaf call sites, then tests). 5. New dependencies, env vars, and config you will introduce. 6. A list of every coin-ID slug used in the codebase that needs a contract-address mapping. Stop and surface the plan before editing any source files. Wait for confirmation. ## Phase 2: Execution (after the plan is approved) Ground rules: 1. Codex is a GraphQL API at `https://graph.codex.io/graphql`. Auth header is `Authorization: ` for long-lived keys, or `Authorization: Bearer ` for short-lived keys. 2. Prefer the official TypeScript SDK (`@codex-data/sdk`) for TS/JS projects. There is no official SDK for Python or other languages: for those, call raw GraphQL against `https://graph.codex.io/graphql`. 3. Network is a numeric parameter (`networkId`), not a URL path segment. Convert CoinGecko network slugs to Codex network IDs: `eth` → 1, `solana` → 1399811149, `base` → 8453, `bsc` → 56, `polygon_pos` → 137, `arbitrum` → 42161, `optimism` → 10, `avax` → 43114, `sui-network` → 101. For others, call `getNetworks` once and build a lookup. 4. Token IDs in Codex are the string `"
:"`. Pair IDs use the same shape. Construct them explicitly. 5. Resolve coin-ID slugs to contract addresses once at startup (or call `filterTokens(phrase: ...)` at the call site). Store the resulting address+networkId in your config rather than carrying the slug through the codebase. 6. Where a CoinGecko integration hits two or three endpoints to fill one screen (for example `/coins/{id}` + `/coins/{id}/market_chart` + `/coins/{id}/tickers`), collapse them into a single GraphQL query. 7. For real-time data, replace polling loops with WebSocket subscriptions when the consumer is a long-lived client (dashboards, trading UIs) and webhooks when the consumer is a server endpoint (alerts, background workers, queues). 8. Preserve existing public function signatures, return shapes, and error semantics wherever possible. Internal helpers can be refactored freely. 9. Update tests as you change code. If a test relied on a CoinGecko response fixture, replace the fixture with a Codex equivalent rather than deleting the test. 10. When you hit a gap (CEX tickers, derivatives, NFTs, treasury holdings, global market cap aggregates, news, non-USD fiat conversions, hand-curated categories), do not silently drop the feature. Leave the call site intact, add a `TODO(migration):` comment with a one-line note explaining what's missing and what provider could fill it, and list it in your final report. ## CoinGecko → Codex endpoint mapping OnChain DEX tokens (the most commonly migrated surface): - `GET /onchain/networks/{network}/tokens/{address}` → `token` - `GET /onchain/networks/{network}/tokens/multi/{addresses}` → `tokens` - `GET /onchain/networks/{network}/tokens/{address}/info` → `token` - `GET /onchain/simple/networks/{network}/token_price/{addresses}` → `getTokenPrices` - `GET /onchain/networks/{network}/tokens/{address}/ohlcv/{timeframe}` → `getTokenBars` - `GET /onchain/networks/{network}/tokens/{address}/trades` → `getTokenEvents` (pair-scoped: a token address resolves to its top pair; iterate the token's pairs or use `onTokenEventsCreated` for token-wide coverage across all pools) - `GET /onchain/networks/{network}/tokens/{address}/top_holders` → `holders(input: { tokenId: "
:" })` (default sort is holdings DESC) - `GET /onchain/networks/{network}/tokens/{address}/top_traders` → `tokenTopTraders` - `GET /onchain/networks/{network}/tokens/{address}/holders_chart` → partial via `onHoldersUpdated` (live only; flag if historical timeseries is required) - `GET /onchain/tokens/info_recently_updated` → `filterTokens` ranked by recent activity (CoinGecko's path is cross-network, with no `networks/{network}` segment) OnChain DEX (pools and pairs): - `GET /onchain/networks/{network}/pools/{pool}` → `getDetailedPairStats(pairAddress: "0x...", networkId: , durations: [...])` (top-level args, not `input: { pairId }`) - `GET /onchain/networks/{network}/pools/multi/{addresses}` → `getDetailedPairsStats` - `GET /onchain/networks/{network}/tokens/{address}/pools` → `listPairsForToken` - `GET /onchain/networks/{network}/pools/{pool}/info` → `pairMetadata` (selects `exchangeId`, not `exchangeHash`) - `GET /onchain/networks/{network}/pools/{pool}/trades` → `getTokenEvents` with pair filter - `GET /onchain/networks/{network}/pools/{pool}/ohlcv/{timeframe}` → `getBars` (use `volume` field, not deprecated `v`) - `GET /onchain/networks/{network}/pools` → `filterPairs(filters: { network: [] })` - `GET /onchain/networks/{network}/new_pools`, `/onchain/networks/new_pools` → `filterPairs` ranked by `createdAt` or subscribe to `onTokenLifecycleEventsCreated` - `GET /onchain/networks/trending_pools`, `/onchain/networks/{network}/trending_pools` → `filterPairs(rankings: { attribute: trendingScore24, direction: DESC })`. `trendingScore24` is a ranking attribute, not a queryable result field. - `GET /onchain/pools/trending_search` → `filterPairs(phrase: ..., rankings: { attribute: trendingScore24 })` - `GET /onchain/networks/{network}/dexes/{dex}/pools` → `filterPairs` filtered by exchange - `GET /onchain/pools/megafilter` → `filterPairs` OnChain DEX (networks, dexes, search): - `GET /onchain/networks` → `getNetworks` - `GET /onchain/networks/{network}/dexes` → `filterExchanges` - `GET /onchain/search/pools` → `filterPairs(phrase: ...)` - `GET /onchain/categories`, `GET /onchain/categories/{id}/pools` → not supported (no curated DEX categories) CoinGecko market data, coin-ID based (only relevant if the codebase hits these endpoints): - `GET /simple/price?ids=...` → resolve IDs to addresses, then `getTokenPrices(inputs: [...])`. If the call uses `include_24hr_change`, `include_24hr_vol`, or `include_market_cap`, also call `filterTokens` and read `change24`, `volume24`, and `marketCap`/`circulatingMarketCap` from its results. `getDetailedTokenStats` provides bucketed volume/OHLC but does not expose market cap. - `GET /simple/token_price/{platform}?contract_addresses=...` → `getTokenPrices(inputs: [...])` (no slug resolution needed). Same caveat for `include_*` flags. - `GET /coins/{id}` → resolve ID first, then `token` + `getDetailedTokenStats` (+ `filterTokens` for `marketCap`/`circulatingMarketCap`, which neither of the first two exposes) - `GET /coins/{id}/market_chart`, `/market_chart/range`, `/ohlc`, `/ohlc/range` → `getTokenBars` - `GET /coins/{id}/contract/{address}/market_chart`, `/market_chart/range` → `getTokenBars` (already contract-addressed) - `GET /coins/{id}/history` → `getBars` with a single bar covering the date - `GET /coins/markets` → `filterTokens(rankings: ..., filters: ...)` - `GET /coins/list`, `GET /token_lists/{asset_platform_id}/all.json` → `filterTokens` at point of use; drop the local coin list - `GET /coins/list/new` → `filterTokens(rankings: { attribute: createdAt, direction: DESC })` or `onTokenLifecycleEventsCreated` - `GET /coins/top_gainers_losers` → `filterTokens(rankings: { attribute: change24, direction: DESC })` (use `change24`; `priceChange24` is the pair-side analogue and is not valid on `TokenRankingAttribute`) - `GET /coins/{id}/tickers` → `listPairsForToken` / `listPairsWithMetadataForToken` (DEX only; flag CEX) - `GET /coins/{id}/contract/{address}` → `token` - `GET /coins/{id}/circulating_supply_chart`, `/total_supply_chart` (+ `/range`) → not supported (no historical supply timeseries; current supply on `token.info`) - `GET /search?query=...` → `filterTokens(phrase: "$SYMBOL", ...)` - `GET /search/trending` → `filterTokens(rankings: { attribute: trendingScore24, direction: DESC })`. Tokens only — flag NFT and category results as gaps. - `GET /exchange_rates`, `/simple/supported_vs_currencies` → not supported (USD only) - `GET /coins/categories`, `/coins/categories/list` → not supported (no curated categories) Real-time (polling → Codex subscription): - Polled `/simple/price`, `/simple/token_price` → `onPriceUpdated` / `onPricesUpdated` - Polled `/onchain/.../ohlcv` → `onBarsUpdated` / `onTokenBarsUpdated` - Polled token-stats endpoints → `onDetailedTokenStatsUpdated` - Polled pair-stats endpoints → `onDetailedStatsUpdated` - Polled trades → `onTokenEventsCreated` / `onEventsCreated` - Polled holders → `onHoldersUpdated` - Polled `new_pools` → `onTokenLifecycleEventsCreated` / `onLaunchpadTokenEvent` Gaps (flag, do not drop): - `/coins/{id}/tickers` for CEX exchanges: Codex is onchain-only - `/exchanges`, `/exchanges/list`, `/exchanges/{id}`, `/exchanges/{id}/tickers`, `/exchanges/{id}/volume_chart`, `/derivatives*`: not supported - `/nfts/*` and NFT/category buckets in `/search/trending`: Codex is a fungible-token API - `/entities/list`, `/public_treasury/*`, legacy `/companies/public_treasury/*`: not supported - `/global`, `/global/decentralized_finance_defi`, `/global/market_cap_chart`: Codex does not aggregate global market totals - `/coins/{id}/circulating_supply_chart`, `/total_supply_chart` (and `/range` variants): no historical supply timeseries (current supply available on `token.info`) - `/news`: not supported - `/exchange_rates`, non-USD `vs_currencies`: Codex returns USD only - `/coins/categories`, `/coins/categories/list`, `/onchain/categories`, `/onchain/categories/{id}/pools`: Codex does not curate token or pool categories When you need details on any Codex field, fetch the reference page at `https://docs.codex.io/api-reference/queries/` (or `subscriptions`, `mutations`) rather than guessing. Before migrating any real-time code, fetch `https://docs.codex.io/concepts/subscriptions` and `https://docs.codex.io/concepts/webhooks` so you pick the right delivery mechanism. ## Phase 3: Final report When the migration is done, produce a single report with: 1. Files changed, grouped by area (client/config, call sites, tests, docs). 2. Every `TODO(migration):` you added, with file path, line, and the reason. 3. The coin-ID → contract-address mapping you ended up with, so the human can audit it. 4. New env vars and dependencies, with the line to add to `.env.example` and the package manager command to install. 5. CoinGecko integrations that were removed entirely, and what replaced them. 6. A short manual-verification checklist the human should run before merging (which features to click through, which endpoints to spot-check, which dashboards to load). Run the project's linter and test suite before declaring the migration complete. If tests fail, fix the underlying integration, do not weaken the test. ```` ## Getting help - Browse the [API Reference](/api-reference/introduction.md) for the full schema. - Skim the [Recipes](/recipes/discover-tokens.md) for end-to-end examples that solve specific product problems. - Ask in [our community](https://t.me/codex_community) if you hit a wall during migration. # Dune Sim to Codex Move from Dune's now-discontinued Sim API to Codex Dune retired its Sim API on August 1, 2026 (new signups closed on May 18, 2026), and pointed customers at three alternatives: Zerion, Codex, and Mobula. This guide maps every Sim endpoint to its closest Codex equivalent, shows working side-by-side examples for the most common patterns, and ends with a copy-paste prompt you can hand to an LLM to translate the rest of your integration. Sim's API and documentation are offline now that the service has sunset. The Sim endpoint paths and parameters referenced here are historical, preserved so you can match them against your existing integration while you port it over. ## Mental model Sim is a REST API, organized by chain family (EVM, SVM), with one resource per URL. Codex is a single GraphQL Supergraph: one endpoint (`https://graph.codex.io/graphql`), one auth header, one query language, and the network is just a parameter on each query (`networkId: Int` on most queries, `networks: [Int!]` on `balances`). Most patterns that took two or three Sim calls collapse into a single Codex query. A few practical consequences: - You don't need different code paths per chain. Codex covers Ethereum, Solana, and [80+ networks](https://docs.codex.io/networks) under the same schema. - Real-time data is first-class. Anything available as a query usually has a matching [GraphQL subscription](/concepts/subscriptions.md) over WebSocket, plus an option to fan out to your servers via [webhooks](/concepts/webhooks.md). - You request only the fields you need, so payloads are usually smaller than the equivalent Sim response. If you've never used GraphQL, [Learn GraphQL](/learn-graphql.md) is a 10-minute primer that's enough to follow the rest of this guide. ## Authentication Sim uses an `X-Sim-Api-Key` header. Codex uses an `Authorization` header with your API key from the [dashboard](https://dashboard.codex.io?utm_source=codex&utm_medium=docs&utm_campaign=migrations-dune-sim). ```bash Sim curl https://api.sim.dune.com/v1/evm/balances/0xd8da6bf26964af9d7eed9e03e53415d37aa96045 \ -H "X-Sim-Api-Key: $SIM_API_KEY" ``` ```bash Codex curl https://graph.codex.io/graphql \ -H "Authorization: $CODEX_API_KEY" \ -H "Content-Type: application/json" \ -d '{"query":"{ balances(input: { walletAddress: \"0xd8da6bf26964af9d7eed9e03e53415d37aa96045\", networks: [1] }) { items { tokenId balance shiftedBalance walletId } } }"}' ``` If you serve requests from a browser, generate a short-lived JWT with [`createApiTokens`](/api-reference/mutations/createapitokens.md) (a Growth or Enterprise plan feature) and pass it as `Bearer ` (see [Authentication](/concepts/authentication.md) for the full pattern). ## Endpoint mapping | Sim endpoint | Codex equivalent | Notes | | :-- | :-- | :-- | | `GET /v1/evm/balances/{address}` | [`balances`](/api-reference/queries/balances.md) query | Native + ERC-20 with USD pricing inline (`balanceUsd`, `tokenPriceUsd`). Works on Solana too. Requires a Growth or Enterprise plan. | | `GET /v1/evm/balances/{address}/token/{token_address}` | [`balances`](/api-reference/queries/balances.md) with `tokens: ["
:"]` | Same query, narrowed to specific tokens (up to 200 per request). | | `GET /v1/evm/balances/{address}/stablecoins` | [`balances`](/api-reference/queries/balances.md) with a curated stablecoin `tokens` list, or [`filterTokens`](/api-reference/queries/filtertokens.md) | No dedicated stablecoin endpoint; pass the stablecoin token IDs you care about. | | `GET /v1/evm/activity/{address}` | [`getTokenEventsForMaker`](/api-reference/queries/gettokeneventsformaker.md) query | Sim returns transfers, NFT moves, approvals, swaps, and decoded contract calls; Codex returns DEX events only (Swap, Mint, Burn, Sync, Collect, CollectProtocol, PoolBalanceChanged, LiquidityLock). If your code uses `activity_type=swap`, the port is straightforward; for `approve`/`call`/NFT activity, see "Gaps" below. | | `GET /v1/evm/transactions/{address}` | [`getTokenEventsForMaker`](/api-reference/queries/gettokeneventsformaker.md) query | Same DEX-only caveat. Codex doesn't expose raw transactions with gas, nonce, or decoded calldata; see "Gaps" below. | | `GET /v1/evm/collectibles/{address}` | Not supported | Codex is a fungible-token API. See "Gaps" below. | | `GET /v1/evm/token-info/{address}?chain_ids=...` | [`token`](/api-reference/queries/token.md) query + [`getTokenPrices`](/api-reference/queries/gettokenprices.md) | Codex returns richer metadata: safety signals, launchpad context, 19 social link fields, supply, image URLs. | | `GET /v1/evm/token-holders/{chain_id}/{address}` | [`holders`](/api-reference/queries/holders.md) query | Returns ranked holders with balances. `top10HoldersPercent` is returned on the same response, or available as a standalone [`top10HoldersPercent`](/api-reference/queries/top10holderspercent.md) query. Codex's `limit` defaults to 50, max 200 (Sim defaults to 500). Paginate to match. Requires a Growth or Enterprise plan. | | `GET /v1/evm/search/tokens?query=...` | [`filterTokens`](/api-reference/queries/filtertokens.md) query | Far more powerful: rank by trending score, volume, market cap, plus filter clauses. Paginates up to 200 per request (Sim caps at 50). | | `GET /v1/evm/defi/positions/{address}` | Partial via [`liquidityMetadata`](/api-reference/queries/liquiditymetadata.md) / [`liquidityMetadataByToken`](/api-reference/queries/liquiditymetadatabytoken.md) | Codex exposes pair-level liquidity and lock breakdowns, not aggregated per-wallet LP positions across protocols. Requires Growth or Enterprise plan. See "Gaps" below. | | `GET /v1/evm/defi/supported-protocols` | Not directly supported | Codex doesn't aggregate per-wallet DeFi positions, so there's no protocol-family list. Use [`filterTokens`](/api-reference/queries/filtertokens.md) with `filters: { exchangeId: ... }` if you need to confirm coverage of a specific DEX. | | `GET /v1/evm/supported-chains` | [`getNetworks`](/api-reference/queries/getnetworks.md) query | Returns the full list of networks Codex indexes, including chain IDs. Takes no arguments. | | `GET /beta/svm/balances/{address}` | [`balances`](/api-reference/queries/balances.md) with `networks: [1399811149]` | Same query, different network ID. Note Sim's SVM endpoints use `chains=solana,eclipse` (not `chain_ids`) — drop the param shape when porting. | | `GET /beta/svm/transactions/{address}` | [`getTokenEventsForMaker`](/api-reference/queries/gettokeneventsformaker.md) | Same shape as EVM; Sim's response wraps raw RPC data, Codex returns decoded DEX events. | | Sim Balances webhook (`POST /beta/evm/subscriptions/webhooks`, `type: balances`) | [`onBalanceUpdated`](/api-reference/subscriptions/onbalanceupdated.md) subscription or [`createWebhooks`](/api-reference/mutations/createwebhooks.md) with `TOKEN_TRANSFER_EVENT` | Codex's `TOKEN_TRANSFER_EVENT` filters by target wallet with direction `TO`/`FROM`/both. | | Sim Activities webhook (`type: activities`) | [`onEventsCreatedByMaker`](/api-reference/subscriptions/oneventscreatedbymaker.md) subscription or `TOKEN_PAIR_EVENT` webhook | Subscription gives per-wallet streams; `TOKEN_PAIR_EVENT` accepts a `maker` filter condition, so you can also fire it for a single wallet's trades server-side. | | Sim Transactions webhook (`type: transactions`) | No direct equivalent | Codex doesn't fan out raw txs. Closest is `TOKEN_TRANSFER_EVENT` for transfer-style traffic. | **Plan requirements.** A few of the mappings above need a Growth or Enterprise plan: [`balances`](/api-reference/queries/balances.md), [`holders`](/api-reference/queries/holders.md), [`liquidityMetadata`](/api-reference/queries/liquiditymetadata.md), every WebSocket subscription, and the [`createWebhooks`](/api-reference/mutations/createwebhooks.md) and [`createApiTokens`](/api-reference/mutations/createapitokens.md) mutations. The rest, including [`getTokenEventsForMaker`](/api-reference/queries/gettokeneventsformaker.md), [`token`](/api-reference/queries/token.md), [`getTokenPrices`](/api-reference/queries/gettokenprices.md), [`filterTokens`](/api-reference/queries/filtertokens.md), [`getNetworks`](/api-reference/queries/getnetworks.md), and [`top10HoldersPercent`](/api-reference/queries/top10holderspercent.md), don't carry that requirement. Check the [dashboard](https://dashboard.codex.io?utm_source=codex&utm_medium=docs&utm_campaign=migrations-dune-sim) for your plan's current limits. ## Side-by-side examples The four patterns below cover most Sim integrations. Token addresses and the wallet (vitalik.eth's resolved address) are real and the queries are runnable in our [Explorer](/explore.md). Codex's `balances` does not resolve ENS names, so always pass the raw address. ### 1. Wallet balances ```bash Sim curl "https://api.sim.dune.com/v1/evm/balances/0xd8da6bf26964af9d7eed9e03e53415d37aa96045?chain_ids=1" \ -H "X-Sim-Api-Key: $SIM_API_KEY" ``` ```typescript Codex SDK const sdk = new Codex(process.env.CODEX_API_KEY!) const { balances } = await sdk.queries.balances({ input: { walletAddress: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045", networks: [1], }, }) balances.items.forEach((b) => { console.log(b.tokenId, b.shiftedBalance) }) ``` ```graphql Codex GraphQL query WalletBalances { balances( input: { walletAddress: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045" networks: [1] } ) { items { tokenId balance shiftedBalance balanceUsd tokenPriceUsd walletId } } } ``` USD pricing is returned inline: `balanceUsd` and `tokenPriceUsd` come back on each item, so most Sim integrations that hit `/v1/evm/balances` and read `value_usd` only need one Codex call, not two. Reach for [`getTokenPrices`](/api-reference/queries/gettokenprices.md) only when you need historical prices, a specific pool, or per-block pricing (capped at 25 inputs per request). If balances feel stale (Codex caches them), call the [`refreshBalances`](/api-reference/mutations/refreshbalances.md) mutation first. ### 2. Wallet activity Sim's activity feed filters by `activity_type` across `send`, `receive`, `mint`, `burn`, `swap`, `approve`, `call`, and `transfer`. Codex's `getTokenEventsForMaker` returns DEX-only events (`Swap`, `Mint`, `Burn`, `Sync`, `Collect`, `CollectProtocol`, `PoolBalanceChanged`, `LiquidityLock`) and exposes the same set as an `eventType` filter on the query. If your Sim integration is mostly `swap`, the port is one-for-one. If it leans on `approve` / `call` or NFT moves, see "Gaps" below. ```bash Sim curl "https://api.sim.dune.com/v1/evm/activity/0xd8da6bf26964af9d7eed9e03e53415d37aa96045?chain_ids=1&limit=25" \ -H "X-Sim-Api-Key: $SIM_API_KEY" ``` ```graphql Codex GraphQL query MakerActivity { getTokenEventsForMaker( query: { maker: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045" networkId: 1 } limit: 25 ) { items { timestamp eventType eventDisplayType maker transactionHash networkId token0Address token1Address quoteToken data { ... on SwapEventData { amountNonLiquidityToken priceUsd } } } cursor } } ``` For a live feed instead of polling, swap the query for the [`onEventsCreatedByMaker`](/api-reference/subscriptions/oneventscreatedbymaker.md) subscription over WebSocket. ### 3. Token holders ```bash Sim curl "https://api.sim.dune.com/v1/evm/token-holders/1/0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48?limit=50" \ -H "X-Sim-Api-Key: $SIM_API_KEY" ``` ```graphql Codex GraphQL query TopHolders { holders( input: { tokenId: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48:1" } ) { count top10HoldersPercent items { walletId tokenId address balance shiftedBalance } } } ``` A few things to note: Codex token IDs are `address:networkId`, and the `holders` response also returns a `top10HoldersPercent` field alongside `items` if you only need the concentration metric. There's also a standalone [`top10HoldersPercent`](/api-reference/queries/top10holderspercent.md) query that takes a token ID directly. Page sizes differ: Sim's `token-holders` defaults to and caps at 500 per page, while Codex's `limit` defaults to 50 and maxes at 200, so a naïve copy of the request will shrink your pages by up to 10× until you adjust pagination. ### 4. Token info and price ```bash Sim curl "https://api.sim.dune.com/v1/evm/token-info/0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48?chain_ids=1" \ -H "X-Sim-Api-Key: $SIM_API_KEY" ``` ```graphql Codex GraphQL query TokenInfo { token(input: { address: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", networkId: 1 }) { name symbol decimals address networkId isScam info { circulatingSupply totalSupply imageLargeUrl description } socialLinks { twitter website } } getTokenPrices( inputs: [ { address: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", networkId: 1 } ] ) { priceUsd timestamp } } ``` The two fields are returned by a single GraphQL request. Codex's response also carries safety signals and launchpad context that Sim's `token-info` doesn't expose: `isScam` everywhere, plus `mintable` and `freezable` (the actual authority addresses, or null) on Solana SPL tokens. ## Real-time data Sim ships real-time updates exclusively through webhooks. Codex gives you two ways to consume the same events, and you can mix them in the same app: - **WebSocket subscriptions**: a persistent connection delivers updates inline. Good for dashboards, trading UIs, anything user-facing. See [Subscriptions](/concepts/subscriptions.md). - **Webhooks**: Codex calls an HTTP endpoint you own when an event fires. Good for background jobs, alerts, server-to-server fan-out. See [Webhooks](/concepts/webhooks.md) and the [`createWebhooks`](/api-reference/mutations/createwebhooks.md) mutation. Common Sim webhook patterns and their Codex equivalents: | You want to know when... | Codex subscription (WebSocket) | Codex webhook (`WebhookType`) | | :-- | :-- | :-- | | A wallet's balance changes | [`onBalanceUpdated`](/api-reference/subscriptions/onbalanceupdated.md) | `TOKEN_TRANSFER_EVENT` (filter by target wallet, direction `TO`/`FROM`/both) | | A wallet makes a swap | [`onEventsCreatedByMaker`](/api-reference/subscriptions/oneventscreatedbymaker.md) (input field is `makerAddress`) | `TOKEN_PAIR_EVENT` (filter by `maker` for a specific wallet, or by pair) | | A token's price moves | [`onPriceUpdated`](/api-reference/subscriptions/onpriceupdated.md) | `TOKEN_PRICE_EVENT` (or `MARKET_CAP_EVENT` if you trigger on market cap thresholds) | | New holders appear on a token | [`onHoldersUpdated`](/api-reference/subscriptions/onholdersupdated.md) | Subscription only (no equivalent webhook type today) | ## Gaps A few things Sim does that Codex doesn't, and what to do about them: - **NFTs (ERC-721 / ERC-1155 collectibles).** Codex is a fungible-token API. If NFT data is core to your product, you'll want to combine Codex with a dedicated NFT provider (Alchemy, Reservoir, OpenSea). - **Aggregated per-wallet DeFi positions.** Codex exposes pair-level liquidity via [`liquidityMetadata`](/api-reference/queries/liquiditymetadata.md) and [`liquidityMetadataByToken`](/api-reference/queries/liquiditymetadatabytoken.md), but not "this wallet holds these LP positions across these protocols." Zerion and DeBank are the usual fill-ins here. - **Raw transaction-level data and decoded contract calls.** Codex returns trading events, not every transaction a wallet ever sent. If you need full tx history, pair Codex with an RPC provider or Etherscan-family API. - **Dedicated stablecoin endpoint.** Use [`filterTokens`](/api-reference/queries/filtertokens.md) with a maintained list of stablecoin addresses. ## What you pick up Capabilities Codex offers that Sim didn't: - **One query, many shapes.** GraphQL lets you combine token metadata, price, holders, and recent trades into a single request and only pull the fields you render. - **Live charting data.** OHLCV bars via [`getBars`](/api-reference/queries/getbars.md) and [`getTokenBars`](/api-reference/queries/gettokenbars.md), and live updates with [`onBarsUpdated`](/api-reference/subscriptions/onbarsupdated.md). Sim didn't ship a charting endpoint. - **Wallet PnL and trader discovery.** [`filterWallets`](/api-reference/queries/filterwallets.md), [`detailedWalletStats`](/api-reference/queries/detailedwalletstats.md), and [`walletChart`](/api-reference/queries/walletchart.md) power discovery of profitable traders, with realized profit, swap counts, win/loss tallies, and per-network breakdowns. Available on Growth and Enterprise plans. See the [Wallets recipe](/recipes/wallets/discover-traders.md). - **Launchpad lifecycle data.** Native support for pump.fun and other launchpads, including graduation status, bonding curves, and migration events. See [Launchpads](/launchpads.md). - **Prediction markets.** Polymarket and Kalshi data via the `filterPredictionEvents` family. See the [Prediction Markets](/prediction-markets.md) section. - **Pair-level data.** Codex has first-class concepts of trading pairs, exchanges, and liquidity locks (Sim is wallet- and token-centric only). - **Built for AI agents.** A [docs MCP server](/agents/docs-mcp.md), prebuilt [Codex Skills](/agents/codex-skills.md) for Claude/Cursor/Codex CLI, and pay-per-query access via [MPP](/agents/mpp.md). ## AI migration prompt Most teams don't migrate one file at a time. They hand the whole codebase to an IDE agent (Claude Code, Cursor, Codex CLI, or similar) and tell it to do the job. The prompt below is written for that: drop it in your agent of choice, run it from the repo root, and it will discover Sim usage, propose a plan, and execute the migration with your approval. Pair this prompt with our [Codex Skills](/agents/codex-skills.md) and [docs MCP server](/agents/docs-mcp.md) so the agent can look up Codex queries on demand instead of guessing at field names. ````markdown You are migrating this codebase from Dune's Sim API to Codex (https://docs.codex.io). Sim was discontinued on August 1, 2026, so this migration needs to be complete and correct, not partial — the old API is no longer available to fall back on. ## Phase 1: Discovery (do this first, do not edit yet) Search the codebase for every Sim integration point. At minimum, look for: - HTTP calls to `api.sim.dune.com` (any path). - Imports of any Sim-specific SDK or client library. - Environment variables and config keys with names like `SIM_API_KEY`, `DUNE_SIM_*`, `SIM_*`. - Header usage of `X-Sim-Api-Key`. - Webhook handlers that decode Sim payloads. - Tests, fixtures, and mocks that reference any of the above. Produce a Migration Plan with: 1. A grouped list of every call site, organized by Sim endpoint. 2. The proposed Codex equivalent for each group (use the mapping below). 3. Any call sites you cannot map cleanly, flagged for human review. 4. The order you intend to make changes (shared client and config first, then leaf call sites, then tests). 5. New dependencies, env vars, and config you will introduce. Stop and surface the plan before editing any source files. Wait for confirmation. ## Phase 2: Execution (after the plan is approved) Ground rules: 1. Codex is a GraphQL API at `https://graph.codex.io/graphql`. Auth header is `Authorization: ` for long-lived keys, or `Authorization: Bearer ` for short-lived keys. 2. For TypeScript/JavaScript, prefer the official SDK (`@codex-data/sdk`) over hand-rolled HTTP. There is no SDK for other languages (including Python), so use raw GraphQL over HTTP there. 3. Network is a parameter, not part of the URL. Most queries take a scalar `networkId: Int`; `balances` takes `networks: [Int!]`. Ethereum is 1, Solana is 1399811149. For other chains, call `getNetworks` once and cache the result. 4. Token IDs in Codex are the string `"
:"`. Construct them explicitly; never assume an integration relies on bare addresses. 5. Prefer combining fields into a single GraphQL query over multiple sequential calls. Sim integrations often chain two or three REST calls for one screen; collapse those. The `balances` query already returns `balanceUsd` and `tokenPriceUsd` inline — do not introduce a follow-up `getTokenPrices` call unless the integration genuinely needs historical or per-pool prices. `getTokenPrices` is capped at 25 inputs per request. 6. For real-time data, use WebSocket subscriptions when the consumer is a long-lived client (dashboards, trading UIs) and webhooks when the consumer is a server endpoint (alerts, background workers, queues). Watch out for one Codex inconsistency: the query `getTokenEventsForMaker` takes a field called `maker`, but the matching subscription `onEventsCreatedByMaker` takes `makerAddress`. Do not copy the name across. 7. Preserve existing public function signatures, return shapes, and error semantics wherever possible. Internal helpers can be refactored freely. 8. Update tests as you change code. If a test relied on a Sim response fixture, replace the fixture with a Codex equivalent rather than deleting the test. 9. When you hit a gap (NFTs/collectibles, aggregated per-wallet DeFi positions, raw transaction-level data with gas/nonce/decoded calldata, dedicated stablecoin filter), do not silently drop the feature. Leave the call site intact, add a `TODO(migration):` comment with a one-line note explaining what's missing and what provider could fill it, and list it in your final report. 10. Watch for parameter-shape differences when porting Sim's SVM endpoints: Sim uses `chains=solana,eclipse` (the string `chains`), Codex uses `networks: [1399811149]` (an array of integer IDs). Sim's EVM endpoints use `chain_ids` plural even when one value is passed. 11. Pagination defaults differ. Sim's `token-holders` defaults to and caps at 500 per page; Codex's `holders` defaults to 50 and caps at 200. Sim's `search/tokens` caps at 50; Codex's `filterTokens` caps at 200. Adjust loop counts accordingly so the migration doesn't silently shrink page sizes by 10×. 12. Some Codex features require a Growth or Enterprise plan: the `balances` and `holders` queries, `liquidityMetadata`, all WebSocket subscriptions, and the `createWebhooks` and `createApiTokens` mutations. `getTokenEventsForMaker`, `token`, `getTokenPrices`, `filterTokens`, `getNetworks`, and `top10HoldersPercent` do not. If the integration depends on a gated feature, note it in the final report so the human can confirm plan coverage before merging. ## Sim → Codex endpoint mapping - `GET /v1/evm/balances/{address}` → `balances(input: { walletAddress, networks: [networkId] })` - `GET /v1/evm/balances/{address}/token/{token_address}` → `balances(input: { walletAddress, networks: [networkId], tokens: ["address:networkId"] })` - `GET /v1/evm/balances/{address}/stablecoins` → `balances` with a curated stablecoin `tokens` list - `GET /v1/evm/activity/{address}` → `getTokenEventsForMaker(query: { maker, networkId }, limit)` (DEX swap/pool events only; field is `maker`, not `makerAddress`) - `GET /v1/evm/transactions/{address}` → `getTokenEventsForMaker` (flag if raw txs or contract calls are required) - `GET /v1/evm/collectibles/{address}` → not supported; flag for a dedicated NFT provider - `GET /v1/evm/token-info/{address}?chain_ids=...` → `token(input: { address, networkId })` plus `getTokenPrices(inputs: [...])` in one query - `GET /v1/evm/token-holders/{chain_id}/{address}` → `holders(input: { tokenId: "address:networkId" })` (default sort is balance descending; pass `sort: { attribute: BALANCE, direction: DESC }` to be explicit) - `GET /v1/evm/search/tokens?query=...` → `filterTokens(phrase, rankings, filters)` - `GET /v1/evm/defi/positions/{address}` → partial via `liquidityMetadata` / `liquidityMetadataByToken` (Growth/Enterprise plan); flag aggregated per-wallet positions for human review - `GET /v1/evm/defi/supported-protocols` → no direct equivalent; if confirming DEX coverage, use `filterTokens` with `filters: { exchangeId: ... }` - `GET /v1/evm/supported-chains` → `getNetworks` (no arguments) - Sim Balances webhook (`type: balances`) → `onBalanceUpdated` subscription, or `createWebhooks` with `TOKEN_TRANSFER_EVENT` filtered by wallet (direction: `TO`/`FROM`/both) - Sim Activities webhook (`type: activities`) → `onEventsCreatedByMaker` subscription (input field is `makerAddress`, not `maker`), or `TOKEN_PAIR_EVENT` webhook - Sim Transactions webhook (`type: transactions`) → no direct equivalent; closest fallback is `TOKEN_TRANSFER_EVENT`. Flag for human review if the consumer needs raw tx fields. - Webhook events on price/market cap → `TOKEN_PRICE_EVENT` or `MARKET_CAP_EVENT`. Use `TOKEN_PRICE_EVENT` for token price thresholds; the bare `PRICE_EVENT` enum value is legacy, so don't reach for it. When you need details on any Codex field, fetch the reference page at `https://docs.codex.io/api-reference/queries/` (or `subscriptions`, `mutations`) rather than guessing. Before migrating any real-time code, fetch `https://docs.codex.io/concepts/subscriptions` and `https://docs.codex.io/concepts/webhooks` so you pick the right delivery mechanism. ## Phase 3: Final report When the migration is done, produce a single report with: 1. Files changed, grouped by area (client/config, call sites, tests, docs). 2. Every `TODO(migration):` you added, with file path, line, and the reason. 3. New env vars and dependencies, with the line to add to `.env.example` and the package manager command to install. 4. Sim integrations that were removed entirely, and what replaced them. 5. A short manual-verification checklist the human should run before merging (which features to click through, which endpoints to spot-check, which dashboards to load). Run the project's linter and test suite before declaring the migration complete. If tests fail, fix the underlying integration, do not weaken the test. ```` ## Getting help - Browse the [API Reference](/api-reference/introduction.md) for the full schema. - Skim the [Recipes](/recipes/discover-tokens.md) for end-to-end examples that solve specific product problems. - Ask in [our community](https://t.me/codex_community) if you hit a wall during migration. We're actively supporting teams moving off Sim. --- # API Reference The Codex GraphQL API has 95 query/subscription/mutation endpoints. Full schema details (types, enums, input objects) are available via GraphQL introspection or at https://docs.codex.io/api-reference/introduction.md. - **API Reference**: Explore all of the GraphQL queries, mutations, and types available in the Codex API - **filterTokens**: Discover, screen, and rank tokens across every supported network using 100+ on-chain signals: trading activity, liquidity, holder behavior, fee economics, and launchpad lifecycle. - **filterExchanges**: Returns a list of exchanges based on a variety of filters. - **getDetailedTokenStats**: Returns bucketed stats for a given token. - **getTokenPrices**: Returns real-time or historical prices for a list of tokens, fetched in batches. - **getTokenBars**: Returns aggregated bar chart data to track price changes over time. - **top10HoldersPercent**: Returns the percentage of a token's total supply held collectively by its top 10 holders. - **token**: Returns a single token by its address & network id. - **tokens**: Returns a list of tokens by their addresses & network id, with pagination. - **tokenSparklines**: Returns a list of token simple chart data (sparklines) for the given tokens. - **tokenTopTraders**: Returns a list of top traders for a given token. - **filterPairs**: Returns a list of pairs based on a variety of filters. - **getDetailedPairStats**: Returns bucketed stats for a given token within a pair. - **getDetailedPairsStats**: Returns bucketed stats for a given token within a list of pairs. - **getSymbol**: Returns charting metadata for a given pair. Used for implementing a Trading View datafeed. - **getBars**: Returns bar chart data to track price changes over time. - **getExchanges**: Returns a list of decentralized exchange metadata. - **chartUrls**: Returns a URL for a pair chart. - **pairMetadata**: Returns metadata for a pair of tokens. - **listPairsForToken**: Returns a list of pairs containing a given token. - **listPairsWithMetadataForToken**: Returns a list of pair metadata for a token. - **liquidityLocks**: Returns liquidity locks for a given pair. - **liquidityMetadata**: Returns liquidity metadata for a given pair. Includes liquidity lock data. - **liquidityMetadataByToken**: Returns liquidity metadata for a given token. Includes liquidity lock data for up to 100 pairs that the token is in. - **getTokenEvents**: Returns transactions for a pair. - **getTokenEventsForMaker**: Returns a list of token events for a given maker (wallet address). - **getEventLabels**: Returns a list of event labels for a pair. - **walletLabelTypes**: Returns the full vocabulary of wallet label types and their metadata. - **walletAggregateBackfillState**: Once a wallet backfill has been triggered, this query can be used to check the status of the backfill. - **filterWallets**: Returns a list of wallets based on a variety of filters. - **filterTokenWallets**: Returns a list of wallets with stats narrowed down to a specific token. - **detailedWalletStats**: Returns detailed stats for a wallet. - **walletChart**: Returns a chart of a wallet's activity. - **apiTokens**: Get all active short-lived api tokens for this api key - **apiToken**: Get the active short-lived api token for this api key by the short-lived token - **balances**: Returns list of token balances that a wallet has. - **holders**: Returns list of wallets that hold a given token, ordered by holdings descending. Also has the unique count of holders for that token. - **categories**: Returns the list of token categories, optionally filtered by kind (canonical or narrative) and lifecycle status. Defaults to active categories. - **category**: Returns a single token category by its slug, or null if none matches. - **categoryTokens**: Returns the tokens assigned to a category (including its subcategories), with optional `filterTokens`-style filters and rankings applied. - **getCommunityNotes**: Returns community gathered notes. - **blocks**: Returns block data for the input blockNumbers or timestamps, maximum 25 inputs. - **getNetworks**: Returns a list of all networks supported on Codex. - **getNetworkStatus**: Returns the status of a list of networks supported on Codex. - **getNetworkConfigs**: Returns a list of network configurations. - **filterNetworks**: Returns a list of networks based on a variety of filters. - **getNetworkStats**: Returns metadata for a given network supported on Codex. - **Predictions Overview**: How prediction events, markets, outcomes, and traders fit together, and which query to reach for - **filterPredictionEvents**: Filters prediction events using optional text, IDs, and ranking criteria. - **detailedPredictionEventStats**: Returns windowed and all-time stats for a prediction event. - **predictionEventBars**: Returns bar data for a prediction event. - **predictionEventTopMarketsBars**: Returns bar data for top markets inside a prediction event. - **predictionCategories**: Returns available prediction categories and nested subcategories. - **filterPredictionMarkets**: Filters prediction markets using optional text, IDs, event constraints, and ranking criteria. - **eventScopedFilterPredictionMarkets**: Filters prediction markets within a single event and returns structured classification metadata for each market. Use this instead of `filterPredictionMarkets` when you need entrant/segment/ladder details (country codes, period+stat grouping, parsed numeric/date rungs) without re-parsing labels client-side. - **predictionMarkets**: Returns prediction markets by ID. - **detailedPredictionMarketStats**: Returns windowed and all-time stats for a prediction market. - **predictionMarketBars**: Returns OHLC-style bar data for a prediction market. - **predictionMarketPrice**: Returns price data for a prediction market at a specific timestamp or latest. - **predictionTokenHolders**: Returns token holder balances for a prediction market. - **filterPredictionTraders**: Filters prediction traders using optional text, IDs, and ranking criteria. - **predictionTraders**: Returns prediction traders by ID. - **detailedPredictionTraderStats**: Returns windowed and all-time stats for a prediction trader. - **predictionTraderMarketsStats**: Returns per-market performance stats for a specific trader. - **filterPredictionTraderMarkets**: Filters trader-market records using trader, market, event, and ranking criteria. - **predictionTraderHoldings**: Returns all prediction token holdings for a specific trader. - **predictionTraderBars**: Returns bar data for a prediction trader over a time range. - **predictionTrades**: Returns prediction trades with cursor-based pagination. - **predictionOutcomeOrderBooks**: Returns live order books for a set of prediction outcomes, fetched from the venue's CLOB. Polymarket and Kalshi; outcomes from other venues will return null. Cached for up to 10s. - **getWebhooks**: Returns a user's list of webhooks. - **backfillWalletAggregates**: Backfill wallet aggregates (trading stats) for a given wallet. This is the data used in the filterWallet/filterTokenWallets and detailedWalletStats queries. - **createApiTokens**: Create a new set of short-lived api access tokens - **deleteApiToken**: Delete a single short-lived api access token by id - **refreshBalances**: Force refreshes the balance for a token in a wallet, persisting the result so subsequent `balances` queries reflect it. Supports contract tokens (`<tokenAddress>:<networkId>`, EVM only) and native token balances (`native:<networkId>`, EVM, Solana, and Starknet). Entries that cannot be refreshed (e.g. an unreachable network) are omitted from the response rather than erroring. - **createWebhooks**: Create event webhooks for price, token/pair, transfer, market cap, and prediction market trades. - **deleteWebhooks**: Delete multiple webhooks. - **onTokenBarsUpdated**: Live-streamed aggregate bar chart data to track price changes over time for a token. - **onPriceUpdated**: Live-streamed price updates for a token. - **onPricesUpdated**: Live-streamed price updates for multiple tokens. - **onDetailedTokenStatsUpdated**: Live-streamed bucketed stats for a given token. - **onFilterTokensUpdated**: Live-streamed filter token updates for the current `filterTokens` result set. - **onBarsUpdated**: Live-streamed bar chart data to track price changes over time. Processed updates are projected into `aggregates` using the confirmed bar shape. - **onPairMetadataUpdated**: Live-streamed stat updates for a given token within a pair. - **onDetailedStatsUpdated**: Live-streamed bucketed stats for a given token within a pair. - **onEventsCreated**: Live-streamed transactions for a pair. - **onEventsCreatedByMaker**: Live-streamed transactions for a maker. - **onEventLabelCreated**: Live-streamed event labels for a token. - **onTokenEventsCreated**: Live-streamed events for a given token across all it's pools - **onLaunchpadTokenEventBatch**: Live-streamed launchpad token events batched (more efficient). - **onLaunchpadTokenEvent**: Live-streamed launchpad token event. - **onHoldersUpdated**: Live-streamed list of wallets that hold a given token. Also has the unique count of holders for that token. - **onBalanceUpdated**: Live-streamed balance updates for a given wallet. - **onPredictionTradesCreated**: Streams new prediction trades as they are ingested. - **onDetailedPredictionMarketStatsUpdated**: Streams updated detailed stats for a specific prediction market. - **onDetailedPredictionEventStatsUpdated**: Streams updated detailed stats for a specific prediction event. - **onPredictionMarketBarsUpdated**: Live-streamed bar chart data to track price changes over time for a prediction market. - **onPredictionEventBarsUpdated**: Live-streamed bar chart data to track price changes over time for a prediction event.