Skip to content
Documentation menu
HTTP APITelegram

Ad data

POST/api/v1/ad/fetch

Privacy-friendly alternative. Returns the ad as a bare object without requiring you to send the LLM message text. Use this when your bot's reply contains user data you'd prefer not to share with Sidekick.

Supports multi-format ad inventory via the optional accept_formats body field. Pass ["text", "image"] to opt in to image ads. The response ad.format discriminator tells you which branch was returned. Legacy clients that omit accept_formats continue to receive text-only ads — the response shape is unchanged except for the new additive format: "text" field.

Authentication

Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Same Bearer token as /api/v1/ad. Generate keys in your publisher settings.

Request body

FieldTypeDescription
user_idrequirednumberFinite numeric Telegram user ID (ctx.from.id). Telegram User docs →
platform_idrequiredstringYour platform's public ID (plt_…). platforms dashboard →
language_coderequiredstringNon-empty IETF language tag (e.g. "en", "ru"). Required even when Telegram omits language_code; use the language selected in your bot. Telegram User docs →
is_premiumbooleanWhether the user has Telegram Premium. Default false. Telegram User docs →
group_idnumberTelegram chat ID (ctx.chat.id) for group/supergroup placements. When set, the ‘1 ad per N messages’ frequency and the per-day impression cap apply per-chat instead of per-user. Omit for 1:1 (private) chats. Telegram Chat docs →
participants_countnumberNon-negative member count of the group chat. Used with group_id for group-size filters and impression weighting; ignored for private chats. Telegram getChatMemberCount →
group_namestringOptional group title. Validated as a string, but currently not used by the ad routes after parsing. Telegram Chat docs →
parse_modestringWhen set, adds a pre-formatted ad_text_formatted field to the response (escaped text wrapped in a hyperlink for the chosen parse mode).
accept_formatsstring[]Accepted formats: "text" and "image". Omitted or null means ["text"]; an empty array or a non-array value returns 400. Unknown entries are ignored, with ["text"] as the fallback. Rich formats are served only by POST /api/v1/ad.
platformstringSurface type. Default: "telegram".

Required fields.

Example request — multi-format

POST /api/v1/ad/fetch
{
"user_id": 12345,
"platform_id": "plt_abc",
"language_code": "en",
"accept_formats": [
"text",
"image"
],
"parse_mode": "HTML"
}

Response — ad served (text branch)

200 OK
{
"has_ad": true,
"impression_id": "imp_a8f3d2c1e9b7",
"ad": {
"format": "text",
"ad_text": "💡 Try Cursor — AI editor for developers",
"ad_url": "https://sidekick-ads.com/api/v1/click/imp_a8f3d2c1e9b7",
"button_text": "Try Cursor →",
"button_url": "https://sidekick-ads.com/api/v1/click/imp_a8f3d2c1e9b7",
"ad_text_formatted": "<a href=\"https://sidekick-ads.com/api/v1/click/imp_a8f3d2c1e9b7\">💡 Try Cursor — AI editor for developers</a>"
}
}

ad_text is the raw campaign text. ad_text_formatted is present only when parse_mode is in the request and contains the escaped text wrapped in a hyperlink for the chosen parse mode. ad_url and button_url are identical today but kept as separate fields for forward compatibility.

Response — ad served (image branch)

200 OK
{
"has_ad": true,
"impression_id": "imp_xxx",
"ad": {
"format": "image",
"ad_text": "Try Sidekick",
"ad_text_formatted": "<a href=\"https://sidekick-ads.com/api/v1/click/imp_xxx\">Try Sidekick</a>",
"ad_url": "https://sidekick-ads.com/api/v1/click/imp_xxx",
"button_text": "Try",
"button_url": "https://sidekick-ads.com/api/v1/click/imp_xxx",
"media_type": "photo",
"image_url": "https://cdn.example.com/sidekick/ad.jpg",
"image_mime": "image/jpeg"
}
}

Image ads carry the same core fields as text ads — ad_text, ad_text_formatted, ad_url, button_text, button_url. Branch on format === "image" only when you actually want to render the media: read image_url and media_type, then send via sendPhoto (for "photo") or sendAnimation (for "animation", used for GIFs and silent MP4). Use ad_text_formatted as the Telegram caption to get a tappable link alongside the inline button.

Response — no ad available

200 OK
{ "has_ad": false, "impression_id": null, "ad": null }

When no campaign matches or the frequency cap hasn't been hit, both fields are null and you should send your original LLM reply unchanged.

Error responses

StatusMeaning
400A required field is missing, a field value is invalid, or platform_id does not identify a platform owned by this account. accept_formats accepts a non-empty array or null.
401Invalid or missing API key.