DexScreener Token Scraper - DEX Pool Prices, Liquidity, Volume
Pricing
$0.75 / 1,000 pools
DexScreener Token Scraper - DEX Pool Prices, Liquidity, Volume
228 networks in one run, 95 fields per pool, 40 of 40 pools merged from both sources. DexScreener scraper plus GeckoTerminal API for crypto token price, liquidity, FDV, buys, sells and unique buyers. Trending and new pools, monitor mode for price moves. No API key.
Pricing
$0.75 / 1,000 pools
Rating
0.0
(0)
Developer
Snow Leo Data
Maintained by CommunityActor stats
0
Bookmarked
1
Total users
0
Monthly active users
24 days ago
Last modified
Categories
Share
DexScreener & GeckoTerminal Scraper — DEX pools across 228 networks
Live prices, liquidity, volume, trade counts and unique wallet counts for decentralised-exchange token pools, assembled from two open APIs into one row: GeckoTerminal for the pool listing and DexScreener for the token profile. No API key, no proxy, no browser.
One run covers 228 networks, not one chain per run. A run that only wants to know what moved since the previous run is one checkbox away.
mode: trending -> the 50 hottest pools right now, every networkmode: pairs -> a fixed watchlist of pool addresses, pricedonlyChanged: true -> only the pools whose price actually moved
What exactly does this Actor return?
Every row is one pool (a trading pair on one DEX on one network), carrying 95 fields. A compact 23-field shape is available for agents and spreadsheets.
| Group | Fields |
|---|---|
| Identity | pool_id, chain, network, network_name, dex_id, dex_name, pool_name, pool_address |
| Base token | base_token_address, base_token_name, base_token_symbol, base_token_decimals, base_token_image, base_token_coingecko_id |
| Quote token | quote_token_address, quote_token_name, quote_token_symbol |
| Price | price_usd, price_native, quote_token_price_usd |
| Price windows | price_change_m5, price_change_m15, price_change_m30, price_change_h1, price_change_h6, price_change_h24 |
| Volume windows | volume_m5, volume_m15, volume_m30, volume_h1, volume_h6, volume_h24 |
| Trades | txns_m5_buys … txns_h24_sells for six windows, plus txns_h24_total and buy_sell_ratio_h24 |
| Wallets | buyers_m5, sellers_m5 … buyers_h24, sellers_h24 — unique addresses, six windows |
| Money | liquidity_usd, liquidity_base, liquidity_quote, market_cap_usd, fdv_usd |
| Age | pool_created_at, pool_age_hours |
| Links | website_url, twitter_url, telegram_url, discord_url, github_url, medium_url, reddit_url, tiktok_url, youtube_url, instagram_url, facebook_url, plus the raw websites and socials lists |
| Presentation | description, image_url, header_image_url, open_graph_url, labels, boosts_active |
| Change tracking | change_type, price_usd_previous, price_change_since_last_run_pct, liquidity_usd_previous, liquidity_change_since_last_run_pct, last_seen_at |
| Provenance | sources, discovery_mode, scraped_at, dexscreener_url, geckoterminal_url |
An empty field means neither source published the value. Nothing is inferred, nothing is filled in by a model.
Why merge two sources instead of scraping one?
Because each one is missing something the other has, and the gap is not
cosmetic. Measured on a live trending run of 20 pools
(python3 tools/measure_sources.py):
| Field | GeckoTerminal | DexScreener | Filled in a 40-pool run |
|---|---|---|---|
price_change_m15, price_change_m30 | yes | no | 38 of 40 |
buyers_h24 / sellers_h24 (unique wallets) | yes | no | 40 of 40 |
socials, websites, image_url | no | yes | 35 of 40 |
labels (v3, DLMM, …), boosts_active | no | yes | 38 of 40 |
| Pool listing with pagination | yes | no | both used |
In that same run 40 of 40 pools (100%) carried data from both sources, and the rows spanned four networks at once.
Unique wallets matter more than they look. By trade count a pool worked by one
bot and a pool traded by a thousand people are indistinguishable. By
buyers_h24 they are not. The minBuyers24h filter exists for exactly that.
Search shows the same effect from the other side. DexScreener's search endpoint
returns at most 30 pairs no matter how many match. GeckoTerminal's pool
search is paginated. Running both and merging them returned
58 pools for PEPE, 66 for USDC and 47 for BONK against the 30
DexScreener returns for each, whatever happens.
What are the real limits of these APIs, and how are they handled?
All four were found by live probing, and all four are handled in code rather
than mentioned in passing. Reproduce every one with
python3 tools/measure_sources.py.
GeckoTerminal stops at page 10. Page 11 of /networks/<net>/pools answers
HTTP 401 — Your request exceeds the allowed max number for page (10). That is
20 pools per page × 10 pages = 200 pools per network and sort order, a hard
ceiling. The Actor gets past it the only honest way: by fanning out across
networks, across DEXes inside a network, and across both server-side sorts.
The network list ends with an error, not with silence. /networks?page=4
answers HTTP 400. A crawler that waits for an empty page retries a hard error
three times per page and reports nothing. This Actor treats 400 and 401 as
"stop here" and never retries them.
DexScreener silently truncates address lists at 30. Send 40 token
addresses and you get HTTP 200 with exactly 30 records. The other ten
vanish — no error, no warning, no count. Everything here is cut into batches of
30 before the request leaves, so a 40-address request returns records for all
40. Measured directly: raw call with 40 addresses → 30 rows; the same 40
through this Actor → more than 30.
Only two sort orders are open. h24_volume_usd_desc and
h24_tx_count_desc are accepted; fdv_usd_desc, pool_created_at_desc and
trending_score_desc all answer HTTP 400 with the list of allowed values.
Sorting by market cap, FDV, price change, unique buyers or pool age therefore
happens after collection, on what this run collected — the input field says
so, and so does this sentence.
Rate limit. GeckoTerminal's open tier allows roughly 30 requests per
minute per address, and answers HTTP 429 above it. The HTTP layer here holds
a per-host brake shared by every thread, doubles the pause on each 429 and
releases it gradually, so a burst does not turn into a wall of failures.
DexScreener allows far more — 300 requests per minute on the pair endpoints.
How does the change-tracking mode work?
Turn on "Only what changed since the last run". The Actor keeps a record of
every pool it has already delivered — the pool id, the price and the liquidity
you were last charged for — in a named key-value store that survives runs
(dexscreener-pool-state). On the next run every pool is tagged:
NEW— never delivered before;UPDATED— delivered before and the price moved;UNCHANGED— delivered before and the price did not move enough.
UNCHANGED rows are not written to the dataset, so a scheduled monitor
stops paying for the same pools every hour.
DEX prices never stand still, so a bare change detector would call every pool
updated on every run and save nothing. That is what
"Report a pool only if its price moved by at least (%)" is for. Set it to 5
and you are told only about pools that moved 5% or more against the price you
were last billed for. Each such row carries price_usd_previous,
price_change_since_last_run_pct and last_seen_at, so the move is visible in
the row rather than something you have to compute.
The memory holds 50 000 pools; when it overflows the oldest keys are dropped first. That record measures 2.9 MB against the platform's 9 MB per-record limit, which is checked by a test rather than assumed.
Order of operations matters here and is enforced by a test that deliberately kills a run mid-export: each batch is delivered first and only then marked as delivered. A run interrupted after 3 530 of 9 000 rows remembered exactly 3 530 — not 9 000, which would have made the missing 5 470 rows unreachable forever.
Which collection modes are there?
| Mode | What it does | Needs |
|---|---|---|
| Trending pools | The trending ranking, globally or per network, over a 5m / 1h / 6h / 24h window | nothing |
| New pools | Pools created most recently — the launch feed | nothing |
| Top pools of a network | Highest 24h volume or 24h transactions | a network |
| Pools of one DEX | Everything on Raydium, Uniswap v3, PancakeSwap … | a network + a DEX id |
| Search | Name, symbol, token address or pool address, across both sources | a search term |
| Given token addresses | Price a watchlist of contracts | a network + addresses |
| Given pool addresses | Price a watchlist of exact pools — the cheapest monitor | a network + addresses |
| Newly listed token profiles | DexScreener's freshest token cards, expanded into priced pools | nothing |
| Boosted tokens | Paid promotions, latest or largest, expanded into priced pools | nothing |
The two DexScreener feeds return tokens, not pools, and carry no price at all — only artwork, a description and links. This Actor expands each of them into real pools with real numbers in the same run, because a row without a single figure in it is not a result.
What can I filter on, and when is the filter applied?
Before anything is written to the dataset, so filtered-out pools are never billed. The run report lists how many each filter removed.
Numeric ranges: liquidity, market cap, fully diluted valuation, 24h volume, 24h transactions (both bounds), 24h buys, 24h sells, 24h unique buyers, 24h price change, pool age in hours and price in USD. Text filters: keep or drop by quote symbol, keep by base symbol, match a word in the pool or token name, and "only tokens that published a website or a social link".
One deliberate choice: a pool whose liquidity is missing does not pass a
minimum-liquidity filter. A missing figure means the source did not say, not
that the value is zero, and quietly letting it through would be a lie about the
result set. The report counts those separately, as minLiquidityUsd:missing.
How is this different from the other DexScreener scrapers on Apify?
The strongest competing Actor scrapes the DexScreener website with a browser and rotating proxies, and asks for 2 048 MB of memory. This one reads the public APIs of both sources with the standard library, and asks for 1 024 MB. Line by line against its published input and output:
| Capability | Strongest competitor | This Actor |
|---|---|---|
| Chains per run | one (chainName is required) | 228, several in one run |
| Sources | DexScreener only | DexScreener and GeckoTerminal, merged |
| Fields per row | 15 base + optional extras | 95 (23 in compact mode) |
| Price windows | 4 (5m, 1h, 6h, 24h) | 6 (adds 15m and 30m) |
| Unique buyers / sellers | maker count only | buyers and sellers separately, six windows |
| Socials and websites | extra paid page visit per token | included in the base row |
| Incremental / change mode | none | NEW / UPDATED / UNCHANGED with a price threshold |
| Previous price in the row | no | price_usd_previous, price_change_since_last_run_pct |
| Compact output for agents | no | yes |
| Drop empty fields | no | yes |
| New-pool feed | via sort | dedicated mode |
| Boosted / promoted tokens | via sort | dedicated mode with boost counts |
| Token profile feed | no | dedicated mode |
| Run report of what was filtered | no | yes, per filter, per reason |
| Requests a proxy | yes | no |
| Memory requested | 2 048 MB | 1 024 MB |
Where the competitor is ahead, stated plainly. Two things it offers cannot be reproduced from the open APIs, and they are not reproduced here:
- Top trader wallets per token. Its
addTopTradersoption reads a page the public API does not expose. This Actor has no equivalent and does not pretend to. - Launchpad bonding-curve progress (
launchpadProgressfor Solana launchpads). Same reason.
Three smaller gaps, also honest:
- its
rankByoffers sixteen server-side sort orders because the website accepts them, while the open API accepts two — the rest are applied here after collection, over the pools this run gathered; - its raw
filterArgsescape hatch replays any DexScreener URL. The equivalent here is a mode plus filters, which covers the documented cases but not an arbitrary URL; - it reports a single
makerCount. This Actor reportsbuyers_h24andsellers_h24separately, which is more information but not the same number — a wallet that both bought and sold is counted in each.
Every other input it has is matched: chain and DEX targeting, the 5m/1h/6h/24h
window, sort direction, all the liquidity / market cap / FDV / age / volume /
transaction / buys / sells / price-change bounds, the page range (fromPage
plus pages per network), result caps, and the token-detail enrichment — which
is on by default here rather than billed as an extra page visit.
How much does a run cost me in platform credits?
Requests, not pages, drive the cost, and the open GeckoTerminal tier answers roughly 30 requests per minute, so wide runs spend most of their time waiting rather than working. Measured on the platform with memory pinned to 1 024 MB:
| Run | Rows | Time | Compute units |
|---|---|---|---|
| Trending, 1 page | 20 | 5.2 s | 0.00143 |
| Trending, 3 pages, first ever run | 50 | 92.0 s | 0.02556 |
| Trending, 3 pages, later runs | 50 | 8.5 s | 0.00237 |
| Token profiles | 23 | 4.8 s | 0.00133 |
| Top pools, 4 networks × 10 pages | 800 | 545.4 s | 0.15150 |
The gap between the first run and later ones is the network directory: naming 228 networks costs three requests to a rate-limited API, so it is cached in the same named store for seven days and only the first run pays for it.
Two practical consequences. Watchlist modes — Given pool addresses and
Given token addresses — lean on DexScreener, which allows 300 requests per
minute, and finish in seconds. Wide multi-network sweeps are slow by nature;
set pagesPerNetwork to what you actually need.
A run that is about to hit its platform timeout stops collecting 45 seconds
early on its own, delivers what it has and writes the report with
stopped_by_timeout: true, instead of being killed with no report at all.
How do I run this on a schedule?
- Pick the mode. For a watchlist use Given pool addresses; for market-wide watching use Trending pools or New pools.
- Turn on Only what changed since the last run.
- Set Report a pool only if its price moved by at least (%) — 5 is a sensible start, 1 for tight watching, 0 to be told about every move.
- Schedule the Actor hourly or every 15 minutes.
- Read the dataset, or point a webhook at it. Every row tells you whether it
is
NEWorUPDATEDand by how much it moved.
The run report in the key-value store (REPORT) records the mode, the networks
used, how many pools were collected, how many the second source enriched, where
the source cut the listing short and why, what each filter removed, and the
NEW / UPDATED / UNCHANGED counts.
Frequently asked questions
Do I need a DexScreener or CoinGecko API key? No. Both endpoints used here are open and were verified with a plain HTTP request from the standard library — no key, no proxy, no browser.
Which networks can I use?
Any of the 228 that GeckoTerminal lists, by its id: solana, eth, bsc,
base, arbitrum, polygon_pos, avax, ton, sui-network, hyperevm and
the rest. python3 tools/measure_sources.py prints the live count.
Why do some rows have fewer fields filled in?
Because only one source knew that pool. Rows enriched by both list both in
sources. On a measured trending run, 20 of 20 pools carried both sources; the
DexScreener-only feeds (token profiles, boosted tokens) carry one.
Is market_cap_usd the same as fdv_usd?
Not always, and they are kept apart on purpose. GeckoTerminal publishes them as
two different figures; DexScreener frequently reports the same number for both.
Whichever source filled the row, you get what that source actually said.
Why is price_usd a number and not a string?
Both APIs publish the price as a string. A string in a numeric column
breaks Excel, BigQuery and pandas alike, so every numeric field is converted in
one place, and an unparseable value becomes null rather than "".
Why is pool_age_hours sometimes empty?
Because the source did not publish a creation time for that pool.
DexScreener's pairCreatedAt is in milliseconds; read as seconds it lands
in the year 54 852 and makes the age negative. It is read as milliseconds here,
which a test pins down.
Can I get the top traders of a token? No. That data is not in the open API. See the comparison table above.
Does the free Apify plan cover this? A default 50-pool run is small enough that the free platform credit covers many of them. Wide multi-network runs and long schedules will need a paid plan for platform usage, as with any Actor.
A run succeeded but the dataset is empty — is that a failure?
Not necessarily, and the Actor always says which case it is. The run report
carries empty_reason, and the log carries a warning naming it:
nothing-changed (the change mode working as asked — every pool was already
delivered at this price, so nothing was written and nothing was charged),
filtered-out (your filters removed everything that was collected), or
source-returned-nothing (the sources had no pools for that mode and network).
The default input cannot produce an empty dataset — a test asserts it.
What happens if a run is interrupted? Delivered rows stay delivered and are remembered; undelivered rows are not marked and come back on the next run. This is tested by killing a run mid-export on purpose.
Can I limit what I spend? Yes. Set the Apify spending limit on the run; the Actor reads it at start and stops at that many rows instead of continuing to scrape for free.
Running the checks yourself
python3 tests/run_all.py # logic and lifecycle, no networkpython3 tests/run_all.py --live # adds live source checkspython3 tools/measure_sources.py # every number in this READMEpython3 tools/verify_readme.py # README, input schema and code must agree
tools/verify_readme.py fails if a number in this file stops matching the
code — field counts, source ceilings, batch sizes and the store card. It has
been proven to fail by breaking it on purpose.
Data, sources and fair use
Data comes from the public endpoints of DexScreener and GeckoTerminal. Both are read at a polite rate with an identifying User-Agent, and neither is scraped through a browser or a proxy. Market data belongs to the venues that publish it; this Actor collects and normalises it, nothing more. Nothing here is investment advice, and no personal data of private individuals is collected — only public on-chain pool and token records.