Ad data
/api/v1/ad/fetchPrivacy-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
| Field | Type | Description |
|---|---|---|
user_idrequired | number | Finite numeric Telegram user ID (ctx.from.id). Telegram User docs → |
platform_idrequired | string | Your platform's public ID (plt_…). platforms dashboard → |
language_coderequired | string | Non-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_premium | boolean | Whether the user has Telegram Premium. Default false. Telegram User docs → |
group_id | number | Telegram 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_count | number | Non-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_name | string | Optional group title. Validated as a string, but currently not used by the ad routes after parsing. Telegram Chat docs → |
parse_mode | string | When set, adds a pre-formatted ad_text_formatted field to the response (escaped text wrapped in a hyperlink for the chosen parse mode). |
accept_formats | string[] | 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. |
platform | string | Surface type. Default: "telegram". |
Required fields.
Example request — multi-format
{"user_id": 12345,"platform_id": "plt_abc","language_code": "en","accept_formats": ["text","image"],"parse_mode": "HTML"}
Response — ad served (text branch)
{"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)
{"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
{ "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
| Status | Meaning |
|---|---|
400 | A 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. |
401 | Invalid or missing API key. |