Skip to main content

Python SDK for the Sidekick ad-injection API

Project description

sidekick-ads

Python SDK for the Sidekick ad-injection API.

Installation

# Core (httpx only)
pip install sidekick-ads

# With aiogram support
pip install sidekick-ads[aiogram]

# With python-telegram-bot support
pip install sidekick-ads[ptb]

Quick start

aiogram 3.x with middleware

from aiogram import Bot, Dispatcher, Router
from aiogram.types import Message
from sidekick_ads import Sidekick
from sidekick_ads.middleware import SidekickMiddleware

bot = Bot(token="BOT_TOKEN")
dp = Dispatcher()
router = Router()
dp.include_router(router)

# Register the middleware — it injects a Sidekick instance into every handler
sidekick_middleware = SidekickMiddleware(api_key="sk_xxx", platform_id="plt_xxx")
dp.message.middleware(sidekick_middleware)

# Close the underlying HTTP pool on dispatcher shutdown (avoids leaks)
dp.shutdown.register(sidekick_middleware.aclose)


@router.message()
async def handle(message: Message, sidekick: Sidekick):
    llm_response = "Here is your answer..."

    user = message.from_user
    user_id = user.id if user else 0
    language_code = user.language_code if user else None
    is_premium = user.is_premium if user else None

    result = await sidekick.inject(
        user_id=user_id,
        message=llm_response,
        language_code=language_code,
        is_premium=is_premium,
        keyboard=None,  # or an existing InlineKeyboardMarkup
    )

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

aiogram 3.x without middleware

from aiogram import Bot, Dispatcher, Router
from aiogram.types import Message
from sidekick_ads import Sidekick

bot = Bot(token="BOT_TOKEN")
dp = Dispatcher()
router = Router()
dp.include_router(router)

sidekick = Sidekick(api_key="sk_xxx", platform_id="plt_xxx")


@router.message()
async def handle(message: Message):
    llm_response = "Here is your answer..."

    user = message.from_user
    user_id = user.id if user else 0
    language_code = user.language_code if user else None
    is_premium = user.is_premium if user else None

    result = await sidekick.inject(
        user_id=user_id,
        message=llm_response,
        language_code=language_code,
        is_premium=is_premium,
        keyboard=None,  # or an existing InlineKeyboardMarkup
    )

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

python-telegram-bot v20+

from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update
from telegram.ext import ApplicationBuilder, CommandHandler, ContextTypes
from sidekick_ads import Sidekick

sidekick = Sidekick(api_key="sk_xxx", platform_id="plt_xxx")


async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
    llm_response = "Here is your answer..."

    user = update.effective_user
    user_id = user.id if user else 0
    language_code = user.language_code if user else None
    is_premium = user.is_premium if user else None

    # Example: existing keyboard with one button
    existing_kb = InlineKeyboardMarkup(
        [[InlineKeyboardButton(text="My button", url="https://example.com")]]
    )

    result = await sidekick.inject(
        user_id=user_id,
        message=llm_response,
        language_code=language_code,
        is_premium=is_premium,
        keyboard=existing_kb,
    )

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


app = ApplicationBuilder().token("BOT_TOKEN").build()
app.add_handler(CommandHandler("start", start))
app.run_polling()

Raw usage (no framework)

import asyncio
from sidekick_ads import Sidekick


async def main():
    sidekick = Sidekick(api_key="sk_xxx", platform_id="plt_xxx")

    result = await sidekick.inject(
        user_id=123456789,
        message="Here is your answer...",
        language_code="en",
        is_premium=False,
    )

    print(result.message)
    print(result.has_ad)
    print(result.impression_id)
    print(result.ad)
    print(result.keyboard)

    await sidekick.close()


asyncio.run(main())

fetch() — privacy-friendly alternative

If your LLM reply contains user data (names, query fragments) that you'd prefer not to send to Sidekick, use fetch() instead of inject(). It returns the ad as separate fields, and you render them into your reply yourself.

import asyncio
from sidekick_ads import Sidekick


async def main():
    sidekick = Sidekick(api_key="sk_live_xxx", platform_id="plt_xxx")

    result = await sidekick.fetch(
        user_id=123456789,
        language_code="en",
        parse_mode="HTML",  # optional — adds `ad_text_formatted`
    )

    llm_reply = "Here is your answer..."

    if result.has_ad and result.ad is not None:
        text = f"{llm_reply}\n\n{result.ad.ad_text_formatted}"
        keyboard = {"inline_keyboard": [[
            {"text": result.ad.button_text, "url": result.ad.button_url}
        ]]}
        # send `text` with `keyboard` via your bot framework
    else:
        # send `llm_reply` unchanged
        pass

    await sidekick.close()


asyncio.run(main())

await sidekick.fetch(...) -> FetchResult

Parameter Type Default
user_id int required
language_code str required
is_premium bool | None None
parse_mode str | None None

FetchResult and FetchAdData

@dataclass
class FetchAdData:
    ad_text: str
    ad_url: str
    button_text: str
    button_url: str
    ad_text_formatted: Optional[str] = None  # only when parse_mode is sent


@dataclass
class FetchResult:
    has_ad: bool
    impression_id: Optional[str]
    ad: Optional[FetchAdData]

Like inject(), fetch() never raises — on any error (timeout, network failure, 5xx) it returns FetchResult(has_ad=False, impression_id=None, ad=None).

See the /api/v1/ad/fetch HTTP endpoint for the full wire format.

API reference

Sidekick(api_key, platform_id, *, base_url, timeout, platform)

Parameter Type Default
api_key str required
platform_id str required
base_url str https://sidekick-ads.com
timeout float 3.0
platform str "telegram"

await sidekick.inject(...) -> InjectResult

Parameter Type Default
user_id int required
message str required
language_code str | None None
is_premium bool | None None
keyboard Any | None None
ad_button_position str "bottom"
first_name str | None None
user_message str | None None

InjectResult

Field Type
message str
has_ad bool
impression_id str | None
ad dict | None
keyboard Any | None

await sidekick.fetch(...) -> FetchResult

Parameter Type Default
user_id int required
language_code str required
is_premium bool | None None
parse_mode str | None None

FetchResult

Field Type
has_ad bool
impression_id str | None
ad FetchAdData | None

FetchAdData

Field Type
ad_text str
ad_url str
button_text str
button_url str
ad_text_formatted str | None

await sidekick.close()

Closes the underlying HTTP connection pool. The client is automatically recreated on the next inject() call if needed.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

sidekick_ads-0.6.0.tar.gz (7.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

sidekick_ads-0.6.0-py3-none-any.whl (7.8 kB view details)

Uploaded Python 3

File details

Details for the file sidekick_ads-0.6.0.tar.gz.

File metadata

  • Download URL: sidekick_ads-0.6.0.tar.gz
  • Upload date:
  • Size: 7.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.6

File hashes

Hashes for sidekick_ads-0.6.0.tar.gz
Algorithm Hash digest
SHA256 b5decf553ea1484eae68c0aff0b2c623c4f2436fb8a7f94b57841c6bd0907060
MD5 d123d16d87d270783ae4373f51b9f54d
BLAKE2b-256 dc27dc01101cc64fda0fe35cb2bdc7d9cf665fb751ec29c688dbcc37593b4d8c

See more details on using hashes here.

File details

Details for the file sidekick_ads-0.6.0-py3-none-any.whl.

File metadata

  • Download URL: sidekick_ads-0.6.0-py3-none-any.whl
  • Upload date:
  • Size: 7.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.6

File hashes

Hashes for sidekick_ads-0.6.0-py3-none-any.whl
Algorithm Hash digest
SHA256 870b7ddfac4273c52e8fbb1ff2c051ba65da62160c64de02b0fc1c9b79044f4f
MD5 04410b7f8600178fe97d5f502223343d
BLAKE2b-256 cc0c09677233c6ff5e6f3866772296dc35330c71a48459c9cbdf67416d771a62

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page