> Adsly docs index: https://adsly.pro/docs/llms.txt · every page in one file: https://adsly.pro/docs/llms-full.txt · OpenAPI: https://adsly.pro/docs/api/openapi.yaml

# Campaigns — list and read

The campaign object, its statuses, GET /v1/campaigns with filters by status, bot, website domain and ad type, GET /v1/campaigns/{ad_id} for one campaign with its targeting and schedule.

A campaign is one Telegram ad. It is identified by **`(account_id, ad_id)`** — Telegram reuses `ad_id` across cabinets, so `ad_id` alone is not unique.

## The campaign object

`GET /v1/campaigns/{ad_id}` returns:

```json
{
  "ad_id": 90412,
  "account_id": 53,
  "title": "API test",
  "text": "Join the channel for daily market notes",
  "status": "Active",
  "active": true,
  "promote_url": "https://t.me/yourchannel",
  "tme_path": "yourchannel",
  "target_type": "channels",
  "targets": ["durov"],
  "cpm": "1.50",
  "budget": "3.54100",
  "spent": "1.45900",
  "daily_budget": null,
  "views": 1043,
  "opens": 0,
  "clicks": 12,
  "actions": 4,
  "ctr": "1.15",
  "cvr": "33.33",
  "cpc": "0.1215833333",
  "cpa": "0.3647500000",
  "views_per_user": 1,
  "schedule": {
    "days": { "mon": [9, 10, 11, 12, 13, 14, 15, 16, 17], "tue": [9, 10, 11, 12, 13, 14, 15, 16, 17],
              "wed": [9, 10, 11, 12, 13, 14, 15, 16, 17], "thu": [9, 10, 11, 12, 13, 14, 15, 16, 17],
              "fri": [9, 10, 11, 12, 13, 14, 15, 16, 17], "sat": [], "sun": [] },
    "timezone": "+03:00"
  },
  "has_media": false,
  "media_type": null,
  "button": null,
  "website_name": null,
  "group_id": 7,
  "auto_cpm_enabled": false,
  "date": "1791364800",
  "synced_at": "2026-10-08T10:29:41.000Z",
  "status_changed_at": "2026-10-08T10:31:07.112Z"
}
```

| Field | Meaning |
|---|---|
| `title` | Your internal name for the ad. |
| `text` | The ad text people see. |
| `status` | What Telegram is doing with the ad — see [Statuses](#statuses). |
| `active` | What you asked for: `true` = should run. A campaign with `active: true` and status `Stopped` resumes as soon as it has budget again. |
| `promote_url` / `tme_path` | What the ad leads to: a website URL, or a `t.me/<tme_path>` link (channel, bot, mini-app, post). |
| `target_type` | `channels`, `bots`, `search` or `users` — see [Create campaigns](https://adsly.pro/docs/api/create-campaigns.md). |
| `targets` | Usernames the ad targets (channels or bots), when known. |
| `cpm` | Bid per 1,000 views, in the cabinet's currency. |
| `budget` | Money **left** on the campaign. `budget + spent` is everything it was given. |
| `spent` | Money spent so far. |
| `daily_budget` | Daily spending cap, if one is set. |
| `views`, `clicks`, `actions`, `opens` | Lifetime counters. `actions` are platform actions (joins, bot starts) — not your conversions; for those see [Conversions](https://adsly.pro/docs/api/conversions.md). |
| `ctr`, `cvr` | Percentages: `"1.15"` means 1.15 %. |
| `cpc`, `cpa` | Cost per click and per action. |
| `views_per_user` | How many times one person can see the ad (1–4). |
| `schedule` | Weekdays and hours the ad is shown, or `null` — it runs around the clock. See [Schedule](https://adsly.pro/docs/api/manage-campaigns.md). |
| `group_id` | The campaign's group in the panel, if any. |
| `auto_cpm_enabled` | Adsly's Auto CPM manages the bid. |
| `date` | When the ad was created: unix seconds, as a string. |
| `status_changed_at` | Last status change. Not touched by stats updates — good for "what changed since". |
| `synced_at` | When the ad's own settings — `schedule`, `text`, `views_per_user`, targeting — were last read from Telegram. See [Freshness](#freshness). |

**Money fields are decimal strings** (`cpm`, `budget`, `spent`, `ctr`, `cvr`, `cpc`, `cpa`) so decimals stay exact. Counters (`views`, `clicks`, `actions`, `opens`) are numbers. All money is in the cabinet's currency (see [Cabinets](https://adsly.pro/docs/api/accounts.md)).

## Statuses

| Status | Meaning | What you can do |
|---|---|---|
| `Active` | Running. | Pause, change bid, add budget. |
| `In Review` | Waiting for Telegram's review; not shown yet. | Wait — a webhook tells you when it changes. |
| `Declined` | Telegram rejected the ad. | Edit the text or link (resubmits), or create a new one. |
| `On Hold` | Switched off — by you or an automation rule. | Resume. |
| `Stopped` | Budget used up. | Add budget; it runs again if `active` is `true`. |
| `Paused` | Paused by Telegram for the whole cabinet (rare). | Contact [@adsly_pro](https://t.me/adsly_pro). |
| `Deleted` | Removed. Kept in history, hidden from lists. | — |

## GET /v1/campaigns

Campaigns of every cabinet the key reaches.

```bash
curl -s "https://app.adsly.pro/api/v1/campaigns?account_id=53&status=Active,On%20Hold&limit=100&cursor=" \
  -H "X-API-Key: $ADSLY_API_KEY"
```

| Parameter | Default | Meaning |
|---|---|---|
| `account_id` | — | Only this cabinet. (`accountId` also works.) |
| `status` | all but Deleted | One status or several, comma-separated. |
| `title` | — | Title contains this text. |
| `bot_username` | — | Ads of one bot or channel: matches the username in `tme_path`, case-insensitive, no `@`. `foo` does not match `foobar`. |
| `promote_domain` | — | Website ads whose link is on this host. Accepts `example.com` or a full URL; `www.`, scheme, path and port are ignored. |
| `promote_domain_mode` | `exact` | `suffix` also matches subdomains (`x.example.com`). |
| `type` | — | `bot` (bots and mini-apps), `channel` (channels, groups, posts) or `site` (external websites). |
| `limit` | 100 | 1–500. |
| `cursor` | — | Pagination — see [Pagination](https://adsly.pro/docs/api/pagination.md). Recommended. |
| `offset`, `sortBy`, `sortOrder` | — | Offset pagination (≤ 500 per cabinet). |

Filters combine with AND. An unknown bot or domain returns an empty list, not an error.

```json
{
  "success": true,
  "data": [
    { "ad_id": 90412, "account_id": 53, "title": "API test", "text": "…", "status": "Active",
      "tme_path": "yourchannel", "promote_url": null, "views": 1043, "opens": 0, "clicks": 12,
      "actions": 4, "spent": "1.45900", "budget": "3.54100", "cpm": "1.50", "cpc": "0.12",
      "cpa": "0.36", "ctr": "1.15", "cvr": "33.33", "date": "1791364800", "group_id": 7,
      "status_changed_at": "2026-10-08T10:31:07.112Z" }
  ],
  "meta": { "limit": 100, "cursor": null, "next_cursor": null, "hasMore": false }
}
```

## Get one campaign

`GET /v1/campaigns/{ad_id}` — the full object above, including targeting, creative fields and `schedule` (the list endpoint leaves those out to stay fast). Deleted campaigns are returned too, with `status: "Deleted"`.

```bash
curl -s "https://app.adsly.pro/api/v1/campaigns/90412?account_id=53" -H "X-API-Key: $ADSLY_API_KEY"
```

`account_id` is needed only when the same `ad_id` exists in several of your cabinets (`400 ACCOUNT_ID_REQUIRED` tells you).

### Freshness

Counters and status are refreshed for every campaign every hour. The ad's own settings — `schedule`, `text`, `views_per_user`, targeting — come from reading the ad itself, so:

- `GET /v1/campaigns/{ad_id}` re-reads the ad from Telegram when our copy is older than **5 minutes**, before answering. A change someone made directly in Telegram's ad interface shows up here within that window.
- Running campaigns (`Active`, `In Review`, `On Hold`) are also re-read in the background about every 3 hours.
- Changes made through Adsly — the panel or this API — are in the copy at once.

If Telegram doesn't answer in time, you get the stored copy; `synced_at` tells you how old it is.

## GET /v1/campaigns/{ad_id}/history

One campaign's totals over a period — a single aggregate, not hourly points.

```bash
curl -s "https://app.adsly.pro/api/v1/campaigns/90412/history?account_id=53&period=7d" -H "X-API-Key: $ADSLY_API_KEY"
```

```json
{ "success": true,
  "data": { "views": 640, "clicks": 7, "spent": 0.96, "ctr": 1.09, "cpm": 1.5, "cpa": 0.32 },
  "meta": { "adId": 90412, "accountId": 53, "period": "7d" } }
```

`period`: `24h` (default), `7d`, `30d` or `custom` with `startDate` / `endDate` (`YYYY-MM-DD`, UTC) and optional `startHour` / `endHour` (0–23). No data for the period → `404 NOT_FOUND`.

## Keeping a local copy in sync

1. Page through `GET /v1/campaigns?cursor=` and upsert by `(account_id, ad_id)`.
2. Subscribe to [webhooks](https://adsly.pro/docs/api/webhooks.md) for status changes and hourly stats instead of polling every minute.
3. Re-sync fully once a day to catch anything a webhook missed.

---
Page: https://adsly.pro/docs/api/campaigns/ · Updated: 2026-10-08
