Create campaigns
POST /v1/campaigns — create one or up to 100 Telegram ads in a call, targeting channels, bots, search queries or audiences by country, language and topic; upload images and video with POST /v1/media.
POST /v1/campaigns creates campaigns in one cabinet. It needs a key with Manage campaigns, an Idempotency-Key, and returns 202 with a task — the campaigns appear when the task completes.
A first example
curl -s -X POST https://app.adsly.pro/api/v1/campaigns \
-H "X-API-Key: $ADSLY_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"account_id": 53,
"title": "Crypto channels — notes",
"text": "Daily market notes in 3 minutes. No noise.",
"promote_url": "https://t.me/yourchannel",
"cpm": 1.5,
"budget": 20,
"target_type": "channels",
"channels": ["@durov", "t.me/telegram"]
}'
{ "success": true, "data": { "id": 81234, "type": "create", "status": "pending", "account_id": 53, "total": 1, "ad_ids": [] } }
Many at once
Put the campaigns in campaigns (up to 100). account_id and the group fields stay at the top level and apply to all of them.
{
"account_id": 53,
"new_group_name": "October test",
"campaigns": [
{ "title": "A", "text": "Variant A", "promote_url": "https://t.me/yourchannel", "cpm": 1.5, "budget": 10,
"target_type": "channels", "channels": ["@durov"] },
{ "title": "B", "text": "Variant B", "promote_url": "https://t.me/yourchannel", "cpm": 1.5, "budget": 10,
"target_type": "channels", "channels": ["@telegram"] }
]
}
Campaigns are created one after another. If the first one fails, the task stops — fix the input rather than repeat the same error 100 times. Each failure is listed in the task’s errors with its index in your array.
Fields
| Field | Required | Meaning |
|---|---|---|
title | yes | Internal name, shown in the panel. |
text | yes | Ad text people see. Telegram’s own length and content rules apply — a refusal comes back in the task’s errors. |
promote_url | yes | What the ad opens: https://t.me/channel, t.me/bot?start=ref, @channel or a website URL. |
cpm | yes | Bid per 1,000 views in the cabinet’s currency, greater than 0. Too low for the targeting → Telegram’s minimum comes back in the error. |
budget | yes | Money moved from the cabinet balance to the campaign: 0, or at least the cabinet’s min_budget (Cabinets). With 0 the ad is created but can’t run until you add budget. |
target_type | yes | channels, bots, search or users — see below. |
active | no | false creates it switched off. Default true. |
daily_budget | no | Daily spending cap. |
views_per_user | no | How many times one person can see the ad, 1–4. |
media | no | Image or video token from POST /v1/media. |
picture | no | true shows the promoted channel’s avatar in the ad. |
button | no | Button under the ad: subscribe, view, read, learn_more, download, open, sign_up, buy, order, play, try, leave_request. Only where the cabinet’s features.ad_button is true. |
website_name | no | Brand name shown above a website ad. |
schedule | no | Show the ad only on chosen weekdays and hours, e.g. weekdays 9:00–18:00. Format: Schedule. Without it the ad runs around the clock. |
Top level only: account_id (needed when the key reaches several cabinets), group_id (an existing group) or new_group_name (creates the group, or reuses one with that name).
Unknown fields are refused with 400 VALIDATION_ERROR and the field name in param — a typo never silently drops a setting.
Targeting
channels — show the ad in specific channels
{ "target_type": "channels", "channels": ["@durov", "t.me/telegram"],
"languages": ["en"], "topics": [7, 26], "exclude_topics": [2], "add_similar_channels": true }
| Field | Meaning |
|---|---|
channels | 1–100 public channels: @name, name or t.me/name. Required. |
languages, topics, exclude_topics | Narrow by channel language and topic (codes). |
add_similar_channels | Also add channels similar to these. |
bots — show the ad in bots
{ "target_type": "bots", "bots": ["@somebot"] }
bots: 1–100 public bots. Required.
search — show the ad for Telegram search queries
{ "target_type": "search", "search_keywords": ["crypto wallet", "bitcoin"] }
search_keywords: 1–100 phrases, up to 64 characters each. Required.
users — show the ad to an audience
{ "target_type": "users", "countries": ["DE", "AT"], "languages": ["de"],
"topics": [26], "exclude_topics": [2], "audience_channels": ["@somechannel"] }
| Field | Meaning |
|---|---|
countries | ISO country codes. |
languages | Language codes. |
topics, exclude_topics | Interests by topic id. |
audience_channels | People who read these channels. |
At least one of these is required. Codes: Targeting codes.
Channels, bots and keywords are resolved to Telegram’s own ids when the task runs. Ones Telegram can’t find are skipped; if none are found, that campaign fails — No targets found: … for channels and bots, No valid search queries found for keywords — and the task’s errors says so.
After creating
- Poll
GET /v1/tasks/{id}untilcompleted.ad_idsholds the new campaigns. - Telegram reviews each new ad: it starts
In Review, then becomesActive(orDeclined). A webhook tells you when. - Change, fund or pause them with Manage campaigns.
Upload media
POST /v1/media uploads an image or video into a cabinet and returns a media token for POST /v1/campaigns, PATCH and copy. The token belongs to that cabinet — upload into the cabinet you’ll create in.
curl -s -X POST "https://app.adsly.pro/api/v1/media?account_id=53" \
-H "X-API-Key: $ADSLY_API_KEY" \
-F "file=@banner.jpg"
{ "success": true, "data": { "media": "BQACAgIAAx0…", "account_id": 53 } }
- JPG, PNG, GIF, WEBP or MP4, up to 10 MB, sent as
multipart/form-datain the fieldfile. - Some cabinets accept only 16:9 images at least 640 px wide; if a file doesn’t fit, Telegram’s reason comes back in
error.
Errors
| Code | When |
|---|---|
VALIDATION_ERROR | A field is missing, unknown, of the wrong type, or doesn’t belong to this target_type — param says which. |
IDEMPOTENCY_KEY_REQUIRED | No Idempotency-Key. |
ACCOUNT_ID_REQUIRED / ACCOUNT_NOT_FOUND | Say which cabinet / that cabinet isn’t reachable. |
VIEW_ONLY_ACCESS | You have Viewer access to this cabinet. |
QUEUE_FULL | 5 creates already queued in this cabinet. |
CABINET_LOCKED, SUBSCRIPTION_REQUIRED | The cabinet can’t take manual changes right now. |
Individual campaign failures (Telegram refused a text, a bid, a link) don’t fail the request — they appear in the task’s errors.
Updated 2026-10-08