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
| Parameter | Type | Description |
|---|---|---|
api_key / apiKeyrequired | string | Your API key (sk_live_…). Generate in settings. settings → |
platform_id / platformIdrequired | string | Platform public ID (plt_…). platforms dashboard → |
base_url / baseUrl | string | API base URL. Default: "https://sidekick-ads.com". |
timeout | number | Request timeout in seconds. Default: 1.0. |
platform | string | Surface type. Default: "telegram". |
inject()
The only method you call. Async, never throws — on any error returns the original message and keyboard unchanged.
| Parameter | Type | Description |
|---|---|---|
user_id / userIdrequired | number | Telegram user ID (ctx.from.id). Telegram User docs → |
messagerequired | string | LLM response text to inject an ad into. |
language_code / languageCode | string? | IETF language tag. SDK sends "" if null/None. Telegram User docs → |
is_premium / isPremium | bool? | Telegram Premium status. SDK sends false if null/None. Telegram User docs → |
keyboard | InlineKeyboardMarkup? | Your existing keyboard. SDK merges the ad button into it. |
parse_mode / parseMode | string? | "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
| Field | Type | Description |
|---|---|---|
message | string | Ready-to-send text — original or with ad CTA appended. |
has_ad / hasAd | bool | Whether an ad was injected. |
impression_id / impressionId | string? | Impression ID for tracking. |
ad | object? | Ad object with text, button_text, button_url. |
keyboard | InlineKeyboardMarkup? | 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.