Build a token price alert monitor with Birdeye Data: poll a watchlist, fire absolute and percent alerts, and confirm each move on a closed candle.
July 28, 2026

A token price alert monitor watches a list of tokens and fires when one crosses a level you care about, so you find out about a move while you can still act on it rather than after the fact. The hard part is not reading a price. It is doing it cheaply across a whole watchlist, and firing on a real move rather than on a momentary wick that reverts a second later. This guide builds that monitor from three Birdeye Data endpoints: a cheap batch poll for the watchlist, a per token check for absolute and percent thresholds, and a candle confirmation that both rejects false alerts and doubles as a custom baseline.
Everything here runs over REST polling. Birdeye Data offers a WebSocket price feed, but on the Premium tier that feed sits behind a Business upgrade, so the whole design leans on scheduled polling instead. Polling is cheaper than it sounds when you tier it: scan the full watchlist with one batch call, then spend the expensive calls only on the tokens that look close to a threshold.

You can build a working token price alert monitor with three Birdeye Data calls arranged as a tiered loop. Read these three steps as a self contained recipe.
GET /defi/multi_price with up to 100 token addresses to pull every current price in one cheap request.GET /defi/v3/price/stats/single to read the exact price and percent change per timeframe.GET /defi/v3/ohlcv and check the last closed candle, so a wick that reverts never fires an alert. Set time_from at your own reference point and the same call gives you a custom baseline.Each step below expands one call with the exact parameters, a curl example, and the mistake that fires a false alert.
Start with the call that runs on every loop, because its cost sets the cost of the whole monitor. You do not want a separate request per token when a watchlist can run to dozens of names. One batch call keeps the loop cheap.
Endpoint: GET /defi/multi_price
Chains: all
Plan availability: Starter and above
Docs: price multiple
One request returns the current price for up to 100 tokens. The response is a map keyed by token address, and each entry carries value for the price, priceChange24h, and liquidity.
| Parameter | Where | Notes |
|---|---|---|
list_address | query | Comma separated token addresses, up to 100. |
x-chain | header | Defaults to solana. |
The field that catches people out is the price itself: it comes back as value, not price, so a naive read returns nothing at all. Beyond that, the cap is 100 addresses per call, and a longer watchlist splits into batches of 100. Below the Business tier this is the only batch price endpoint you get, so the watchlist is built on it out of necessity as much as economy.
curl --request GET \
--url '<https://public-api.birdeye.so/defi/multi_price?list_address=TOKEN_A,TOKEN_B,TOKEN_C>' \
--header 'x-api-key: YOUR_API_KEY' \
--header 'x-chain: solana'
The poll also hands you priceChange24h for free, so a simple 24 hour alert can fire from this call alone without a second request. Anything finer than 24 hours, or any absolute threshold, needs the per token check that comes next.
The poll tells you roughly where each token sits. When one looks close to a level, you drill in for the exact numbers that decide whether to fire. This call drives both kinds of alert, an absolute price target and a percent move, from one response.
Endpoint: GET /defi/v3/price/stats/single
Chains: all
Plan availability: Standard and above
Docs: price stats single
The response returns, per timeframe, the current price, the high, the low, and the price_change_percent. Read price against an absolute target and price_change_percent against a percent target, across whatever windows you list.
| Parameter | Where | Notes |
|---|---|---|
address | query | The token address. This path uses address. |
list_timeframe | query | Comma separated windows from 1m to 7d. |
x-chain | header | Defaults to solana. |
The casing flips here, which is the trap. The batch poll returned camelCase like priceChange24h, but this endpoint is snake_case, so the field is price_change_percent. The values also sit one level deep, in a data array per address that itself holds a data array per timeframe. Map the nested shape once and reuse it.
curl --request GET \
--url '<https://public-api.birdeye.so/defi/v3/price/stats/single?address=TOKEN_A&list_timeframe=1h,24h>' \
--header 'x-api-key: YOUR_API_KEY' \
--header 'x-chain: solana'
At this point the token has crossed your threshold on paper. Whether it crossed for real is another matter, because the price you just read may have been a spike that already snapped back.
A threshold check on a live price fires on wicks. A wick is a brief spike that snaps back within the same candle, and alerting on one wakes you up for a move that never really happened. Confirming on a closed candle removes them.
Endpoint: GET /defi/v3/ohlcv
Chains: Solana and major chains
Plan availability: Standard and above
Docs: OHLCV V3
The response returns OHLCV (open, high, low, close, volume) candles as items[], each with o, h, l, c, v, and unix_time. Read the close, c, of the last completed candle rather than the current price, and fire only if that close clears your threshold.
| Parameter | Where | Notes |
|---|---|---|
address | query | The token address. |
type | query | Interval, such as 15m. Required. |
time_from | query | Window start, Unix seconds. Required. |
time_to | query | Window end, Unix seconds. Required. |
The subtle mistake here is reading the wrong candle. The most recent item in the series is often the current, still forming candle, and its close is not final. Check unix_time against the interval to confirm a candle has actually closed, and use the last closed one. Otherwise you are confirming against a live price again and the whole step buys you nothing.
curl --request GET \
--url '<https://public-api.birdeye.so/defi/v3/ohlcv?address=TOKEN_A&type=15m&time_from=1726670000&time_to=1726700000>' \
--header 'x-api-key: YOUR_API_KEY' \
--header 'x-chain: solana'
The same endpoint covers custom baselines too, which is how the monitor gets away with only three calls. Fixed timeframes answer questions like “how far in the last hour”. They cannot answer “how far since you added this token yesterday afternoon”. For that, set time_from to your own reference moment and read the close of the first candle in the series. It becomes the baseline you measure the current price against, with no extra endpoint involved.
With all three calls in hand, the remaining work is wiring them into a loop that stays cheap and does not spam you.
A monitor is only as useful as the alerts it can express, and three types cover almost every case a trader asks for. Each maps to one of the calls above, so knowing the type tells you which call to spend.
An absolute price alert fires when a token crosses a fixed price, such as a level set at 2 dollars. Read price from the stats call, or value from the batch poll, and compare it to the target. This is the cheapest alert, because the batch poll alone can drive it without a drill down.
A percent move alert fires on a relative change over a window, such as a 10 percent move in an hour. Read price_change_percent for that timeframe from the stats call. Because the percent is precomputed per window, you never have to store a prior price and compute the change yourself, which removes a whole class of state bugs.
A custom baseline alert fires on a move since a reference point the fixed windows do not cover, such as the moment you opened a position. Point time_from at that moment on the candle call, take the first candle that comes back as your reference, and measure the current price against its close.
Let each token in the watchlist carry its own alert type and threshold, store both next to the address, and the loop below evaluates every token against its own rule rather than one global setting.
The tiering is what keeps this affordable, so the three calls run at different rates rather than together. Run the cheap batch poll on a short interval across the whole watchlist. Only when a token from that poll looks close to a threshold do you spend the per token stats and candle calls on it. Most tokens on most loops cost you nothing beyond their slice of one batch call.
Debounce is what separates a usable monitor from an unusable one. A token sitting above a threshold trips it on every single loop, so without state you fire the same alert dozens of times and start ignoring your own notifications. Track an alert state per token, fire once on the crossing, and reset only when the price falls back through the level. Polling interval is the other lever: tighter catches moves sooner and spends more credits, so tie it to how fast you actually need to react and to the budget you set in the next section.
// Poll the whole watchlist cheaply, then drill only where needed.
// REST polling is used here because the WebSocket price feed sits
// behind a Business upgrade on the Premium tier.
const prices = await multiPrice(watchlist); // 1 call for up to 100 tokens
const near = watchlist.filter(t => nearThreshold(prices[t]));
for (const token of near) {
const stats = await priceStats(token); // exact percent per timeframe
if (crosses(stats) && !alerted[token]) {
const confirmed = await closedCandleClears(token);
if (confirmed) { fire(token); alerted[token] = true; }
}
if (backThroughLevel(stats)) alerted[token] = false;
}
With the loop tiered and debounced, the last thing to watch is what it spends.
A fired alert is worthless if it lands somewhere nobody reads. Delivery is the last piece, and it shapes how you write the payload.
Send the alert to a channel you already watch, a messaging webhook, an email, or a push notification, and include enough context that you can act without opening a chart. A useful payload names the token, the rule that tripped, the confirmed close that cleared the threshold, and the timestamp of the candle it closed on. That last field matters more than it looks, because it tells you whether the alert is fresh or arrived late after a queue backed up.
Handle delivery failures separately from detection. If a webhook returns an error, the alert state should not be marked as fired, or you silently lose the notification and never see that token again until it crosses back and forward. Retry a failed send, and only flip the debounce state after the delivery succeeds.
A monitor runs forever, so its credit cost compounds in a way a one time script never does. Call GET /utils/v1/credits to read your remaining allowance, and log it each loop while you tune the polling interval, so a too tight interval shows up as a falling balance rather than a suspended key.
To size the budget, multiply the batch poll cost by your loop frequency, then add the drill down calls for the tokens you expect to flag. A watchlist of 100 tokens polled once a minute is 1,440 batch calls a day, and the per token stats and candle calls only stack on top for the handful of tokens that approach a threshold. Set the interval where that daily total fits inside your plan, and widen it the moment the balance falls faster than you planned.
curl --request GET \
--url '<https://public-api.birdeye.so/utils/v1/credits>' \
--header 'x-api-key: YOUR_API_KEY'
A monitor that only works when every request succeeds will not survive its first bad night. Polling brings failure modes a one off script never meets, and how you handle them decides whether this is a demo or something you can leave running.
Requests fail. A timeout, a rate limit, or a transient error will eventually hit the batch poll, and the naive reaction is to skip that loop and move on. That is usually right for one miss and wrong for several in a row, because a token can cross your threshold during the gap and cross back before the next successful poll. Retry the failed call with a short backoff, and if the retries keep failing, log the gap rather than pretending the loop ran clean.
A successful response is not automatically a fresh one, which makes stale prices the quieter problem. The batch response carries updateUnixTime per token, telling you when that price was last refreshed, and a thinly traded token can hand back a price that is minutes old. Fire a percent alert off that reading and you are alerting on a move that already finished. Compare updateUnixTime against the current time and skip any token older than your alert window.
Deploys cause a subtler kind of damage. When the debounce map lives only in memory, a restart or a crash wipes it, and every token already sitting above its threshold fires all over again the moment the process comes back up. Persist the alert state next to the watchlist so it survives the restart, and treat the first loop afterwards as a state rebuild rather than a normal pass.
Then there is the host clock, which nobody thinks about until it drifts. Every candle boundary and every custom baseline comes down to a Unix timestamp comparison, so a clock running fast makes the monitor ask for a candle that has not closed yet, and back comes an incomplete series. Keep the clock synced, and where you can, derive the window from the timestamps in the response rather than from local time.
The monitor is a loop, not a pipeline. The batch poll runs first and cheaply, and the three per token calls run only on the tokens the poll flags. Alerts fire from the confirmation step, and state feeds back in to debounce the next loop.

Before you ship, walk this checklist:
value, not price, and splits watchlists longer than 100 into batches.price_change_percent from the nested data array.unix_time, not the forming one.time_from at the reference moment.For background on how block times affect how fast a price can move between polls, the Solana documentation on transactions and slots explains the timing the monitor polls against.
It is a service that watches a set of tokens and fires when one crosses a price level or percent move you defined, so you learn about the move in time to act. This guide builds one with three Birdeye Data endpoints over REST polling.
Birdeye Data offers a WebSocket price feed, but on the Premium tier it sits behind a Business upgrade, so this design uses scheduled REST polling. Tiered polling, a cheap batch scan plus targeted drill downs, keeps the cost low enough that the socket is not needed for most watchlists.
It confirms every threshold break on a closed candle. A live price can spike and revert within a single candle, so the monitor reads the close of the last completed candle and fires only if that confirmed value clears the threshold.
Yes. The batch poll and the stats call run across the chains Birdeye Data supports, and the candle endpoint covers Solana plus the major EVM chains, so setting the x-chain header runs the same loop on EVM tokens. The examples here use Solana because that is what x-chain defaults to.
Track an alert state per token. Fire once when the price crosses the level, mark the token alerted, and clear that state only when the price falls back through the level. Without this debounce, a token above a threshold trips it on every loop.
One batch poll covers up to 100 tokens, so a watchlist beyond that splits into batches of 100 per loop. The per token confirmation calls only run for tokens near a threshold, so the practical ceiling is your credit budget and polling interval, not a hard cap on watchlist size.
Name the token, the rule that tripped, the confirmed candle close, and the candle timestamp. The timestamp tells you whether the alert is current or arrived late, which is the difference between acting on it and ignoring it.
Ready to build your own token price alert monitor? Grab an API key and compare plans at Birdeye Data, and browse the full endpoint reference at docs.birdeye.so/reference.
Birdeye provides expansive data covering tokens, wallets, trades, and protocols across 300+ exchanges on 10 chains.
Whether you’re a solo tinkerer or a large team looking to scale, Birdeye offers plans that caters for your data needs and budget.
Dive into our docs and start querying data on 60+ APIs and 8 WebSocket types today!
Insights is a feature that allows users to analyze market trends in various aspects and dive deep into many industry sectors.
Find Gems is a feature that helps user identify potential Tokens at the current time.
Launch Explorer is a feature that enables users to access real-time data of tokens on popular launchpads like pump.fun, letsbonk.fun,...
New insight article by Birdeye reveals USDC's breakout growth in recent years
Data by Birdeye shows total trading volume of xStocks, PreStocks, and Ondo Global Markets on Solana peaked in March 2026
After the Drift Protocol's hack, Solana Foundation initiated programs such as STRIDE and SIRIN to tighten security for ecosystem teams

July 28, 2026

July 28, 2026
July 24, 2026