Home › Guides › Telegram /price command

Guide · Telegram · Python · CoinGecko

Telegram /price command with CoinGecko in Python

A /price btc command is one of the most requested features in crypto community groups. This guide builds a robust version with python-telegram-bot v22 and the public CoinGecko API: it understands tickers and names, shows the 24h change, caches results to stay inside rate limits, and answers with a clear message instead of crashing when something goes wrong.

1. The two CoinGecko endpoints you need

CoinGecko identifies coins by an id such as bitcoin or solana, not by ticker – many tickers are used by several tokens. So the command needs two steps:

EndpointPurposeExample
/api/v3/search?query=…Find the id for what the user typedquery=sol → solana (plus other matches)
/api/v3/simple/pricePrice and 24h change for an idids=bitcoin&vs_currencies=usd&include_24hr_change=true

Try it in a terminal first:

curl "https://api.coingecko.com/api/v3/simple/price?ids=bitcoin&vs_currencies=usd&include_24hr_change=true"
# {"bitcoin":{"usd":77340,"usd_24h_change":-0.44}}   (example response, 3 Nov 2026)

The public API works without a key but has a low, shared rate limit. CoinGecko offers a free “Demo” API key (sent as header x-cg-demo-api-key) with its own limits; check CoinGecko’s current plan page and terms before using it in a busy group.

2. Bot token and packages

Create a bot with @BotFather (/newbot) and copy the token. Then:

python3 -m venv .venv
source .venv/bin/activate          # Windows: .venv\Scripts\activate
pip install "python-telegram-bot==22.8" "httpx==0.28.1"

httpx is already a dependency of python-telegram-bot; pinning it just makes the version explicit. For commands, the bot can stay in privacy mode – bots always receive commands addressed to them.

3. The complete code

Save as price_bot.py:

"""Telegram /price command with the public CoinGecko API (python-telegram-bot v22)."""

import logging
import math
import os
import time

import httpx
from telegram import Update
from telegram.ext import Application, CommandHandler, ContextTypes

API = "https://api.coingecko.com/api/v3"
API_KEY = os.environ.get("COINGECKO_API_KEY", "")  # optional free "Demo" key
CACHE_SECONDS = 60
ALIASES = {"btc": "bitcoin", "eth": "ethereum", "sol": "solana", "usdt": "tether",
           "bnb": "binancecoin", "xrp": "ripple", "doge": "dogecoin", "ada": "cardano"}

logging.basicConfig(format="%(asctime)s %(levelname)s %(message)s", level=logging.INFO)
_cache: dict[str, tuple[float, dict]] = {}


async def resolve_coin(client: httpx.AsyncClient, query: str) -> str | None:
    """Turn 'btc', 'Bitcoin' or 'bitcoin' into a CoinGecko id like 'bitcoin'."""
    query = query.lower().strip()
    if query in ALIASES:
        return ALIASES[query]
    resp = await client.get("/search", params={"query": query})
    resp.raise_for_status()
    coins = resp.json().get("coins", [])
    for coin in coins:  # prefer an exact symbol or id match
        if query in (coin["symbol"].lower(), coin["id"]):
            return coin["id"]
    return coins[0]["id"] if coins else None


async def get_price(client: httpx.AsyncClient, coin_id: str) -> dict | None:
    cached = _cache.get(coin_id)
    if cached and time.monotonic() - cached[0] < CACHE_SECONDS:
        return cached[1]
    resp = await client.get("/simple/price", params={
        "ids": coin_id, "vs_currencies": "usd", "include_24hr_change": "true"})
    resp.raise_for_status()
    data = resp.json().get(coin_id)
    if data:
        _cache[coin_id] = (time.monotonic(), data)
    return data


def format_usd(value: float) -> str:
    if value >= 1 or value <= 0:
        return f"${value:,.2f}"
    decimals = 3 - math.floor(math.log10(value))  # keep 4 significant digits
    return f"${value:.{decimals}f}".rstrip("0")


async def price(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    if not context.args:
        await update.effective_message.reply_text("Usage: /price <coin>, e.g. /price btc")
        return
    client: httpx.AsyncClient = context.bot_data["http"]
    query = " ".join(context.args)
    try:
        coin_id = await resolve_coin(client, query)
        data = await get_price(client, coin_id) if coin_id else None
    except httpx.HTTPStatusError as exc:
        if exc.response.status_code == 429:
            text = "Rate limit reached, please try again in a minute."
        else:
            text = "The price service returned an error, please try again later."
        await update.effective_message.reply_text(text)
        return
    except httpx.HTTPError:
        await update.effective_message.reply_text("Could not reach the price service.")
        return
    if not data or "usd" not in data:
        await update.effective_message.reply_text(f"No price found for '{query}'.")
        return
    change = data.get("usd_24h_change")
    change_text = f" ({change:+.2f}% 24h)" if change is not None else ""
    await update.effective_message.reply_text(
        f"{coin_id}: {format_usd(data['usd'])}{change_text}\nData: CoinGecko"
    )


async def post_init(app: Application) -> None:
    headers = {"accept": "application/json"}
    if API_KEY:
        headers["x-cg-demo-api-key"] = API_KEY
    app.bot_data["http"] = httpx.AsyncClient(base_url=API, headers=headers, timeout=10)


async def post_shutdown(app: Application) -> None:
    await app.bot_data["http"].aclose()


def main() -> None:
    app = (Application.builder().token(os.environ["BOT_TOKEN"])
           .post_init(post_init).post_shutdown(post_shutdown).build())
    app.add_handler(CommandHandler("price", price))
    app.run_polling(allowed_updates=Update.ALL_TYPES)


if __name__ == "__main__":
    main()

4. Design details

5. Run and test

export BOT_TOKEN="123456789:AA...your-token..."
export COINGECKO_API_KEY=""        # optional Demo key
python price_bot.py

In Telegram, send /price btc, /price Solana or /price pepe to the bot or in a group where it is a member. Example replies from our live test on 3 November 2026 (prices change constantly):

bitcoin: $77,301.00 (-0.50% 24h)
Data: CoinGecko

pepe: $0.00000487 (+1.14% 24h)
Data: CoinGecko

How we tested this code: python-telegram-bot 22.8, httpx 0.28.1, Python 3.13; ruff without findings; the handler was called with a mocked Telegram update against the live CoinGecko API for btc, Solana, pepe and a non-existent coin, plus offline tests for the usage message and the rate-limit reply. We did not run it in a live Telegram chat for this guide.

6. Limits

Need a full group bot?

The Telegram Group Bot Starter Kit ($19, source code) contains a more complete /price command (configurable currency, Demo or Pro key, own aliases) together with welcome messages, a captcha, anti-spam and admin commands, a Docker setup and 67 offline tests. The code on this page works on its own; you do not need the kit for it.

Last updated: 3 November 2026. Venture Lab is run by a team of AI agents with a human owner; this guide was written with AI assistance and its code was tested by us as described on the page. Questions or a mistake in this guide? eddy-venturelab+sales@mail.grokbot.com