Tasks (async operations)
Creating, copying and bulk changes run in the background. How to follow a task with GET /v1/tasks/{id}, what its statuses mean and how to read its result.
Some operations talk to Telegram once per campaign, so they can take longer than an HTTP request should wait. These return 202 Accepted with a task instead of the final result:
POST /v1/campaigns— createPOST /v1/campaigns/{ad_id}/copy— copyPOST /v1/campaigns/bulk— pause, resume, budget, CPM or delete many
Single-campaign changes (PATCH, pause, resume, budget, delete) answer directly — no task.
The 202 response
HTTP/1.1 202 Accepted
Location: /api/v1/tasks/81234
Retry-After: 2
{
"success": true,
"data": {
"id": 81234,
"type": "create",
"status": "pending",
"account_id": 53,
"progress": 0,
"total": 3,
"succeeded": null,
"failed": null,
"ad_ids": [],
"errors": [],
"paused_reason": null,
"error": null,
"created_at": "2026-10-08T10:15:02.411Z",
"started_at": null,
"completed_at": null
}
}
GET /v1/tasks/{id}
curl -s https://app.adsly.pro/api/v1/tasks/81234 -H "X-API-Key: $ADSLY_API_KEY"
Same shape as above. Poll it until status is final; while a task is pending or running the response carries Retry-After: 2 — wait that long between polls. Any key of the same owner can read the task.
| Field | Meaning |
|---|---|
type | create, copy, status (bulk pause/resume), budget, cpm, delete |
status | see below |
progress / total | campaigns processed so far / in total |
succeeded / failed | counts, once known |
ad_ids | campaigns the task created (create, copy) or changed (bulk) |
errors[] | up to 50 failures: { ad_id, index, error } — index is the position in your campaigns array for creates |
paused_reason | why a task stopped to wait, e.g. the cabinet ran out of balance |
error | why the whole task failed |
Statuses
| Status | Meaning | Final? |
|---|---|---|
pending | queued — the cabinet runs one create and one other operation at a time | no |
running | in progress | no |
paused | stopped to wait — usually the cabinet balance ran out mid-way. Top up the cabinet; resume it from the panel | no |
completed | finished. Check failed and errors — a completed task can contain individual failures | yes |
failed | the task as a whole failed — see error | yes |
cancelled | cancelled from the panel | yes |
Things to know about creates
- New campaign ids appear in
ad_idswhen the task completes (they are found by syncing the cabinet after the last campaign is created). - If the first campaign of a batch fails, the task stops there rather than repeat the same mistake 100 times. Fix the input and send again.
- If Telegram can’t resolve any of a campaign’s channels or bots, that campaign fails with
No targets found: …listing them. - Telegram reviews every new ad. A created campaign usually starts
In Review; a webhook tells you when that changes.
A polling loop
async function waitForTask(id) {
for (;;) {
const res = await fetch(`https://app.adsly.pro/api/v1/tasks/${id}`, {
headers: { 'X-API-Key': process.env.ADSLY_API_KEY },
});
const { data } = await res.json();
if (['completed', 'failed', 'cancelled'].includes(data.status)) return data;
if (data.status === 'paused') throw new Error(`Task paused: ${data.paused_reason}`);
await new Promise(r => setTimeout(r, Number(res.headers.get('Retry-After') || 2) * 1000));
}
}
import time, requests
def wait_for_task(task_id):
while True:
r = requests.get(f"https://app.adsly.pro/api/v1/tasks/{task_id}",
headers={"X-API-Key": API_KEY}, timeout=30)
task = r.json()["data"]
if task["status"] in ("completed", "failed", "cancelled"):
return task
if task["status"] == "paused":
raise RuntimeError(f"Task paused: {task['paused_reason']}")
time.sleep(int(r.headers.get("Retry-After", 2))) Updated 2026-10-08