Skip to main content

REST API errors

All REST API errors return a JSON object with a message field (and sometimes an error field). Here’s every status code you can encounter:

200 Success

Normal response. Parse the JSON body. One important edge case: batch pricing (GET /networks/{network}/multi/prices) returns HTTP 200 with an empty array [] when none of the requested tokens have pricing data. This is not an error. It means the tokens were processed but none had prices.

400 Bad Request

The request was malformed or contained invalid parameters. The body names the offending parameter and, where there is a fixed set, lists the accepted values:
Common causes:
  • Invalid order_by on /networks/{network}/pools/search. The accepted values are volume_usd_24h, volume_usd_7d, volume_usd_30d, liquidity_usd, txns_24h, created_at, price_usd, price_change_percentage_24h, price_change_percentage_6h, price_change_percentage_1h and price_change_percentage_5m. The older volume_usd spelling is rejected
  • Invalid order_by on /networks/{network}/tokens/search, which takes a different list: volume_usd_24h, volume_usd_7d, volume_usd_30d, liquidity_usd, txns_24h, price_change_percentage_24h, created_at and fdv_usd
  • Sorting tokens by price. Do not read price_usd out of the token 400 body and retry with it. That value appears in the message, and order_by=price_usd on either token search path answers with a second 400. Ordering tokens by raw price is not supported. Sort by volume_usd_24h or liquidity_usd instead and read price_usd off the rows you get back. The eight values listed above are the ones that actually return 200
  • Sorting tokens by a short price change window. The 6h, 1h and 5m windows exist on pool search only, so passing one as order_by to either token search endpoint returns a 400
  • Using an unknown parameter name. Unknown names are silently dropped rather than rejected, so a request with sort_by= or volume_24h_min= returns 200 with default ordering and no filter applied. Check the echoed query object in the response to see what the API actually used
  • Invalid interval value for OHLCV (must be one of: 1m, 5m, 10m, 15m, 30m, 1h, 6h, 12h, 24h)
  • Batch pricing with more than 10 tokens
  • Batch pricing with zero tokens (empty tokens parameter)
  • Invalid UNIX timestamp format in filter parameters
  • Missing required parameters (e.g., start for OHLCV)
What to do: Check the parameter values against the API reference or common patterns.

404 Not Found

The requested resource doesn’t exist.
Common causes:
  • Invalid network ID (e.g., eth instead of ethereum, sol instead of solana)
  • Token address doesn’t exist on that network
  • Pool address doesn’t exist on that network
  • Typo in the URL path
What to do:
  1. Verify the network ID by checking GET /networks
  2. Use GET /search?query={name} to find the correct network and address
  3. Check that the address format matches the chain (e.g., 0x... for EVM chains, base58 for Solana)

410 Gone

The endpoint has been permanently removed. The body names its own replacement:
Currently applies to (all return 410 Gone):
  • GET /pools: use GET /pools/search instead.
  • GET /networks/{network}/pools: use GET /networks/{network}/pools/search instead.
  • GET /networks/{network}/pools/filter: use GET /networks/{network}/pools/search instead.
  • GET /networks/{network}/dexes/{dex}/pools: use GET /networks/{network}/pools/search?dex_name={dex} instead.
  • GET /networks/{network}/tokens/{token_address}/pools: use GET /networks/{network}/pools/search?token_address={token_address} instead.
  • GET /networks/{network}/tokens/filter: use GET /networks/{network}/tokens/search instead.
  • GET /networks/{network}/tokens/top: use GET /networks/{network}/tokens/search instead.
The whole route is removed in each case, not just the specific network, DEX, or token. A nonsense DEX slug on /networks/{network}/dexes/{dex}/pools returns the same 410, not a 404, which is how you tell a removed endpoint from a bad identifier.

401 Unauthorized

The Authorization header is missing, or its value is not a valid key. The key goes in the header on its own, with no scheme word in front of it. This is the single most common cause of a 401 here, because almost every other API expects the opposite.
Send the key on its own. A request carrying a scheme word in front of it is not the documented form, and on the Pro host it is indistinguishable from a wrong key. What you see can differ by host, which is why this is harder to diagnose than it should be. api-pro.dexpaprika.com sits behind an edge rule that can reject a request before it reaches the API, answering HTTP 403 with an HTML page rather than the JSON error above. If you get an HTML body from an API that otherwise always answers JSON, check the header format and the base URL first, before you start regenerating the key. What to do: send the key as the entire header value, starting with api_. If you are using an SDK or an HTTP client that prepends a scheme word for you, which many do when you fill in a token field, set a raw header instead. A key that is correct but sent to the wrong base URL returns 403, not 401. If you have just upgraded and this is your first call to api-pro.dexpaprika.com, start from upgrading to Pro: the host needs an Authorization header carrying your key on every request, including the root path.

402 Payment Required

Your monthly credit allowance is exhausted. This is not a rate limit, and retrying does not help: the allowance only refills when the period rolls over, or when you add credits. The body is structured and names the next step for your tier:
resets_at is the UTC timestamp when the allowance rolls over. A 402 deliberately carries no Retry-After header, which is how you tell it apart from a 429 in code. What to do:

403 Forbidden (wrong host)

Your key is valid, but it is being sent to the wrong base URL: free keys are served on api.dexpaprika.com, paid keys on api-pro.dexpaprika.com. Endpoints, paths and parameters are identical on both hosts; only the base URL changes.
The mirror case, a paid key sent to api.dexpaprika.com, returns the same wrong_host error pointing at api-pro.dexpaprika.com. What to do: update the base URL in your client and retry. Nothing is wrong with the key or the plan. Upgrading to Pro has the full host and header mapping, in both directions.

429 Too Many Requests

Per-minute request rate exceeded. This is separate from the monthly credit allowance, which returns 402 instead. A 429 carries a Retry-After header giving the seconds to wait. Free tier limit: 15 requests a minute without a key, 30 with a free key, against a monthly allowance of 50,000 credits without a key (300,000 with a free key). Pro allows 300 requests a minute against 5,000,000 credits. What to do:
  • Retry after the number of seconds in Retry-After
  • Cache responses for data that changes rarely (network lists, DEX lists)
  • Use batch pricing instead of individual token requests. It does not reduce credits, but it does reduce HTTP calls, which is what the per-minute limit counts
  • Use the streaming API for live prices instead of polling
  • Consider the Pro API for 300 requests a minute

500 Internal Server Error

Something went wrong on our side. What to do: Retry with exponential backoff (wait 1s, then 2s, then 4s, etc.). If the error persists, check our Discord for status updates or contact support.

Streaming API errors

The streaming API at https://streaming.dexpaprika.com can fail in two ways: HTTP errors before the stream starts, or SSE error events during an active stream.

HTTP errors (before stream starts)

The 400 behavior is strict: In a POST request with multiple tokens, if even one token is invalid, the entire request fails. Validate all tokens via the REST API before streaming them.

SSE errors (during active stream)

If something goes wrong during an active stream, the error arrives as an SSE event:
What to do: Close the connection and reconnect with exponential backoff.

Pagination gotchas

Two styles are in play, and mixing them up is the most common migration bug. Cursor-based, which covers every search endpoint (/networks/{network}/pools/search, /pools/search, /networks/{network}/tokens/search, /tokens/search): read has_next_page and pass next_cursor back as cursor. There is no page parameter and no page_info object. Passing page here does nothing: unknown query parameters are dropped silently and you get a plausible-looking first page back every time. Page-based, which is now only GET /networks/{network}/dexes and GET /networks/{network}/pools/{pool_address}/transactions: page=1 returns the first page and page=0 is silently treated as page=1. Maximum page size is 100 items (via limit parameter) in both styles. Transaction pagination is limited to 100 pages; for deeper history use the cursor parameter there too.

Common mistakes and fixes

Symptom: GET /networks/{network}/pools/search returns {"results": [], "has_next_page": false}Cause: Filters are too strict (for example a high volume_usd_24h_min on a quiet network), or a very small limit.Fix: Loosen the filters, or use limit=10 or higher. Note the response wraps rows in results and pages with next_cursor, not page_info.
Symptom: GET /search?query=USDC returns empty arrays.Cause: Search is best-effort and may not match very common/generic terms well. It searches across tokens, pools, and DEXes.Fix: Try more specific queries (e.g., the token address), or use the token endpoint directly if you know the network and address.
Symptom: OHLCV request returns [].Cause: The start date might be before the pool existed, or the pool may have very low activity in the requested period.Fix: Check the pool’s created_at field to ensure your date range is valid. Try a broader interval (e.g., 24h instead of 1h).
Symptom: Code that used /pools, /networks/{network}/pools, or /networks/{network}/pools/filter now returns 410 Gone.Cause: Those endpoints were removed. The replacement is GET /networks/{network}/pools/search (single network) or GET /pools/search (across networks).Fix: Repoint to */search and update params and response handling: volume_24h_min becomes volume_usd_24h_min, sort_by becomes order_by, sort_dir becomes sort, and page becomes cursor-based (has_next_page + next_cursor). The response wraps rows in results; each row uses id (pool address), volume_usd_24h, and transactions_24h. See the pool filtering tutorial.
Symptom: Code that used /networks/{network}/dexes/{dex}/pools now returns 410 Gone.Cause: The endpoint was removed. The DEX moved out of the path and into the dex_name filter on pool search.Fix: Call GET /networks/{network}/pools/search?dex_name={dex}. dex_name takes the DEX id, the dex_id field from GET /networks/{network}/dexes, matched case-insensitively. That is what the old path segment held, so it is the value already in your code. Passing that response’s dex_name field instead, a display name like Uniswap V3, returns HTTP 200 with an empty results array rather than an error, so an empty page here usually means a display name went in where an id belonged.
Three response changes bite here. Rows come back under results, not pools. Pagination is has_next_page + next_cursor instead of page + page_info. And the 24h volume field is volume_usd_24h, with no bare volume_usd on the row; passing the old order_by=volume_usd is rejected with a 400 naming the accepted values.
Symptom: Streaming connection returns 400 instantly.Cause: One or more assets in the request are invalid (wrong chain ID, non-existent token address).Fix: Validate every token via the REST API before adding it to a streaming request. In a POST batch, all assets must be valid. One bad asset cancels the entire stream.

FAQs

Not the draft standard RateLimit-* family. Every response does carry X-Api-Plan, naming the plan the request was evaluated against. For counts, call GET /usage or open console.dexpaprika.com. See response headers.
The per-minute rate frees up within the next minute; after a 429, wait the Retry-After seconds and retry. The monthly credit allowance resets at the time given in resets_at on a 402.
No. Every endpoint costs one credit per request, with no per-endpoint weights or compute units. The one wrinkle is batch endpoints, which charge one credit per item in the batch.