Back home
API Reference

SDK Integration

One method. Pass the LLM response — get it back with a native ad injected and the keyboard merged. SDK handles errors, timeouts, and Telegram limits automatically.

Install

pip
pip install sidekick-ads

# with framework extras:
pip install sidekick-ads[aiogram]    # aiogram 3.x
pip install sidekick-ads[ptb]        # python-telegram-bot 20+

Quick start

Initialize with your API key and platform ID (from your platforms dashboard), then call inject() on every LLM response.

from sidekick_ads import Sidekick

sk = Sidekick(
    api_key="sk_live_xxxxx",
    platform_id="plt_xxxxx",
)

# in your handler:
result = await sk.inject(
    user_id=message.from_user.id,
    message=llm_reply,
    language_code=message.from_user.language_code,
    is_premium=message.from_user.is_premium,
    keyboard=your_existing_keyboard,  # optional
    parse_mode="HTML",  # optional — matches your bot's parse_mode
)

await message.answer(result.message, reply_markup=result.keyboard, parse_mode="HTML")

Constructor

ParameterTypeDescription
api_key / apiKeyrequiredstringYour API key (sk_live_…). Generate in settings. settings
platform_id / platformIdrequiredstringPlatform public ID (plt_…). platforms dashboard
base_url / baseUrlstringAPI base URL. Default: "https://sidekick-ads.com".
timeoutnumberRequest timeout in seconds. Default: 1.0.
platformstringSurface type. Default: "telegram".

inject()

The only method you call. Async, never throws — on any error returns the original message and keyboard unchanged.

ParameterTypeDescription
user_id / userIdrequirednumberTelegram user ID (ctx.from.id). Telegram User docs
messagerequiredstringLLM response text to inject an ad into.
language_code / languageCodestring?IETF language tag. SDK sends "" if null/None. Telegram User docs
is_premium / isPremiumbool?Telegram Premium status. SDK sends false if null/None. Telegram User docs
keyboardInlineKeyboardMarkup?Your existing keyboard. SDK merges the ad button into it.
parse_mode / parseModestring?"HTML", "MarkdownV2", or "Markdown". Server escapes and formats the ad CTA for the given parse mode.
ad_button_position / adButtonPosition"bottom" | "top"Where to add the ad button row. Default: "bottom".

Return value

FieldTypeDescription
messagestringReady-to-send text — original or with ad CTA appended.
has_ad / hasAdboolWhether an ad was injected.
impression_id / impressionIdstring?Impression ID for tracking.
adobject?Ad object with text, button_text, button_url.
keyboardInlineKeyboardMarkup?Merged keyboard (your buttons + ad button), or original if no ad.

Framework examples

Copy-paste examples for every supported framework. Each one is a complete, working handler.

aiogram 3.x — with middleware

bot.py
from aiogram import Bot, Dispatcher, types
from sidekick_ads import Sidekick
from sidekick_ads.middleware import SidekickMiddleware

bot = Bot(token="BOT_TOKEN")
dp = Dispatcher()

# Register middleware — injects `sidekick` into every handler
dp.message.middleware(SidekickMiddleware(
    api_key="sk_live_xxxxx",
    platform_id="plt_xxxxx",
))

@dp.message()
async def handle(message: types.Message, sidekick: Sidekick):
    llm_reply = await get_llm_response(message.text)

    # Your existing keyboard (if any)
    kb = types.InlineKeyboardMarkup(inline_keyboard=[
        [types.InlineKeyboardButton(text="👍", callback_data="like"),
         types.InlineKeyboardButton(text="👎", callback_data="dislike")],
    ])

    result = await sidekick.inject(
        user_id=message.from_user.id,
        message=llm_reply,
        language_code=message.from_user.language_code,
        is_premium=message.from_user.is_premium,
        keyboard=kb,
    )

    await message.answer(result.message, reply_markup=result.keyboard)

aiogram 3.x — without middleware

bot.py
from aiogram import Bot, Dispatcher, types
from sidekick_ads import Sidekick

bot = Bot(token="BOT_TOKEN")
dp = Dispatcher()

sk = Sidekick(api_key="sk_live_xxxxx", platform_id="plt_xxxxx")

@dp.message()
async def handle(message: types.Message):
    llm_reply = await get_llm_response(message.text)

    result = await sk.inject(
        user_id=message.from_user.id,
        message=llm_reply,
        language_code=message.from_user.language_code,
        is_premium=message.from_user.is_premium,
    )

    await message.answer(result.message, reply_markup=result.keyboard)

python-telegram-bot v20+

bot.py
from telegram import Update, InlineKeyboardMarkup, InlineKeyboardButton
from telegram.ext import ApplicationBuilder, MessageHandler, ContextTypes, filters
from sidekick_ads import Sidekick

sk = Sidekick(api_key="sk_live_xxxxx", platform_id="plt_xxxxx")

async def handle(update: Update, context: ContextTypes.DEFAULT_TYPE):
    user = update.message.from_user
    llm_reply = await get_llm_response(update.message.text)

    kb = InlineKeyboardMarkup([
        [InlineKeyboardButton("👍", callback_data="like"),
         InlineKeyboardButton("👎", callback_data="dislike")],
    ])

    result = await sk.inject(
        user_id=user.id,
        message=llm_reply,
        language_code=user.language_code,
        is_premium=user.is_premium,
        keyboard=kb,
    )

    await update.message.reply_text(result.message, reply_markup=result.keyboard)

app = ApplicationBuilder().token("BOT_TOKEN").build()
app.add_handler(MessageHandler(filters.TEXT, handle))
app.run_polling()

Raw usage (no framework)

main.py
import httpx
from sidekick_ads import Sidekick

sk = Sidekick(api_key="sk_live_xxxxx", platform_id="plt_xxxxx")

async def on_message(user_id: int, text: str, lang_code: str | None, premium: bool | None):
    llm_reply = await get_llm_response(text)

    result = await sk.inject(
        user_id=user_id,
        message=llm_reply,
        language_code=lang_code,
        is_premium=premium,
    )

    # Send via any HTTP client
    async with httpx.AsyncClient() as client:
        await client.post(
            f"https://api.telegram.org/bot{BOT_TOKEN}/sendMessage",
            json={
                "chat_id": user_id,
                "text": result.message,
                **({"reply_markup": result.keyboard} if result.keyboard else {}),
            },
        )

Error handling

inject() never throws. On any error — timeout, network failure, 5xx, malformed JSON — it returns your original message and keyboard unchanged. Your bot keeps working no matter what.

What happens on error
# Sidekick is down? Bot still works.
result = await sk.inject(user_id=123, message="Hello world")
# result.message == "Hello world"  (unchanged)
# result.has_ad == False
# result.keyboard == None  (or your original keyboard)

Keyboard merging

  • Ad button is always added as a separate row — never mixed with your buttons.
  • Position controlled by ad_button_position: "bottom" (default) or "top".
  • Telegram allows max 13 keyboard rows. If you already have 13 — ad button is skipped, text CTA only.
  • Your existing buttons are never modified or removed.
  • If message + ad exceeds 4096 chars, text CTA is dropped but button is still added.

Notes

  • Impressions are tracked automatically. When an ad is returned, the server already recorded the impression. No extra call needed.
  • Clicks go through the redirect. The button URL handles click tracking and redirects to the advertiser. Just use it as-is.
  • Connection pooling. Python SDK reuses an httpx.AsyncClient across calls. Node.js uses the built-in fetch agent.
  • Timeout default is 1 second. If the API doesn't respond in time, your bot gets the original message back instantly.