Admin API: spend
Four Admin API methods for spend: the state by day, settings, receiving a daily snapshot of an ad account and downloading the extension.
These methods power the Settings → Spend («Настройки → Расходы») tab and the spend extension. You can also use them to send the spend of an ad account to the tracker from your own integration. Authorization is described on the Admin API: overview and authorization page, and the response and error format on the Request, response and error format page.
Access and license
| Method | spend:write | full |
|---|---|---|
GET /api/v1/spend | yes | yes |
POST /api/v1/spend/snapshots | yes | yes |
PUT /api/v1/spend/settings | no | yes |
GET /api/v1/spend/extension | no | yes |
A spend:write token is created in the panel: Settings → Spend, the Connect profile («Подключить профиль») block, the Create key («Создать ключ») button. Such a key is valid for 90 days. A full token is created on the API tab — see Access types.
All four methods work only on a paid license. On a trial license they respond with code 403:
{"code": "spend_paid_required", "message": "Расширение для расходов доступно после оплаты лицензии."}The message means “the spend extension is available after the license is paid for”. When the license is inactive, the response is 402, see Expired license.
Methods
Get the settings and the state of days
GET /api/v1/spendReturns the spend settings and the state of the last 60 days for which snapshots arrived.
Token scope: spend:write or full. There are no parameters and no body.
Response — code 200:
| Field | Type | Description |
|---|---|---|
settings.ad_key | string | The click field that carries the ad ID. Until the settings are saved — sub_id_4 |
settings.timezone | string | The value saved back when the time zone was a setting; without it — UTC. It is not used in the calculation: each account is counted in its own time zone. Extension versions below 1.1.0 check the account's time zone against it |
jobs | array | Days with snapshots, from newest to oldest. If there were no snapshots — [] |
jobs[].day | string, YYYY-MM-DD | Spend date. For each account, it is the day in that account's time zone |
jobs[].pending | boolean | true — the snapshot is accepted and waits to be distributed across campaigns, false — the spend is counted in the reports |
jobs[].error | string | Error text of the last distribution. An empty string if there is no error. The tracker repeats the distribution on its own |
jobs[].campaigns | number | How many campaigns received spend for the day |
jobs[].unmatched_ads | number | How many ads are not matched: there are no clicks with their ID for the day |
jobs[].unmatched_spend | number | Spend of these ads in USD |
jobs[].updated_at | string, RFC 3339 date | Time of the last change |
The same data is shown in the table on the Spend tab — see Days table.
Example response:
{
"settings": {"ad_key": "sub_id_4", "timezone": "Europe/Warsaw"},
"jobs": [
{
"day": "2026-10-03",
"pending": false,
"error": "",
"campaigns": 4,
"unmatched_ads": 1,
"unmatched_spend": 3.2,
"updated_at": "2026-10-04T06:15:22.104512Z"
}
]
}Save the spend settings
PUT /api/v1/spend/settingsSets the click field with the ad ID. In the panel, this is the Ad ID field («Поле с ID объявления») list. The time zone is not configured: each snapshot passes it.
Token scope: full.
| Field | Type | Required | Description |
|---|---|---|---|
ad_key | string | yes | One of the values sub_id_1 … sub_id_30 |
timezone | string | no | An optional field from earlier versions: the value is saved and returned in settings.timezone, but it is not used in the calculation. If passed, it must be a time zone in IANA format; the value Local is not accepted. Without the field, the saved value does not change |
Response — code 200 and the saved values:
{"ad_key": "sub_id_4", "timezone": "UTC"}The field can be changed until the tracker has accepted a single snapshot. After the first snapshot it is locked, and the method responds 409. Check the value before the first spend upload.
Errors:
| Code | code | message | When |
|---|---|---|---|
422 | validation | Выберите sub_id_1…30 с ID объявления (choose the sub_id_1…30 with the ad ID) | The body could not be parsed or ad_key is outside the list |
422 | validation | Укажите IANA timezone рекламного кабинета (specify the IANA timezone of the ad account) | timezone is passed, but it is unknown or equal to Local |
409 | spend_history | После первого импорта поле с ID объявления фиксировано, чтобы не пересчитать историю другим способом (after the first import, the ad ID field is locked so that the history is not recalculated in a different way) | The tracker already has at least one snapshot |
Send a daily spend snapshot
POST /api/v1/spend/snapshotsAccepts the full spend of one ad account for one day: a list of ads and the amount for each of them.
Token scope: spend:write or full. The body is one JSON object up to 2 MiB in size. Fields that are not in the table cause a 422 error.
| Field | Type | Required | Description |
|---|---|---|---|
account_id | string | yes | Ad account ID: digits only, from 1 to 32 characters |
day | string, YYYY-MM-DD | yes | Spend date in the timezone time zone. Today or one of the 30 previous days in that time zone |
currency | string | yes | Account currency: three Latin letters, for example PLN. Case does not matter |
timezone | string | yes | Time zone of the ad account in IANA format, for example America/Los_Angeles. Each account has its own: the spend of a day is distributed across the clicks that arrived on that day in that time zone. The value Local is not accepted |
fetched_at | string, RFC 3339 date | yes | When the data was received from the ad account. No older than 24 hours and no more than 5 minutes in the future |
rows | array | yes | The account's ads for the day, up to 10,000 rows. An empty array resets the account's spend for this day to zero. A request without the field is processed as an empty array |
rows[].ad_id | string | yes | Ad ID: digits only, from 1 to 32 characters. No repeats within a snapshot |
rows[].spend | number | yes | Ad spend for the day in the currency currency, from 0 to 1,000,000,000 |
How the tracker processes a snapshot:
- A snapshot replaces the earlier data of this account for this day rather than adding to it. Send all of the account's ads, not only the ones that changed.
- Of two snapshots of the same account for the same day, the one with the later
fetched_atis kept. A snapshot with an earlier time is not applied. - The amount is converted to USD at the rate for the spend date. The rate is fixed for the account and day with the first snapshot — see Rate for extension spend.
USDdoes not need a rate. - An ad's spend is split between campaigns by the clicks with its ID in the
settings.ad_keyfield — see How spend gets into reports.
Response — code 202: the snapshot is accepted, and the distribution across campaigns runs in the background. Check the result in jobs of the GET /api/v1/spend method.
| Field | Type | Description |
|---|---|---|
accepted | boolean | Always true |
changed | boolean | true — the snapshot is saved, and the day is queued for distribution. false — the tracker already has a snapshot with the same or a later fetched_at, the data did not change |
rows | number | How many rows the request had |
Example request:
curl -X POST https://panel.example.com/api/v1/spend/snapshots \
-H "Api-Key: <token>" \
-H "Content-Type: application/json" \
-d '{
"account_id": "1234567890123456",
"day": "2026-10-03",
"currency": "PLN",
"timezone": "Europe/Warsaw",
"fetched_at": "2026-10-04T06:15:00Z",
"rows": [
{"ad_id": "120210000000000001", "spend": 182.4},
{"ad_id": "120210000000000002", "spend": 57.15}
]
}'Example response:
{"accepted": true, "changed": true, "rows": 2}Errors:
| Code | code | message | When |
|---|---|---|---|
422 | validation | various, see the list below | An error in the body or in the field values |
422 | currency_rate | Нет курса USD на дату расхода; добавьте курс и повторите (there is no USD rate for the spend date; add a rate and repeat) | There is no rate for currency on the day date — see If there is no rate |
422 | snapshot_limit | Лимит дня: 100 аккаунтов, 100000 объявлений и допустимая сумма USD; ad_id должен принадлежать одному аккаунту (daily limit: 100 accounts, 100000 ads and an allowed USD amount; an ad_id must belong to one account) | More than 100 accounts or 100,000 ads for the day, the day's total in USD is greater than 1,000,000,000, or this ad_id has already come from another account for the day |
409 | currency_mismatch | Валюта сохранённого снимка отличается (the currency of the saved snapshot is different) | The account has already been uploaded for this day in a different currency |
409 | snapshot_conflict | Снимки с одинаковым временем содержат разные данные; выполните новый сбор (snapshots with the same time contain different data; collect again) | fetched_at matches the saved one, but the rows are different. Send a snapshot with a new time |
429 | rate_limited | слишком много запросов, попробуйте позже (too many requests, try again later) | More than 120 requests per minute from one IP — see Rate limits |
Messages with the validation code
Некорректный снимок расходов (до 2 МиБ)(invalid spend snapshot, up to 2 MiB) — the body is not JSON, is larger than 2 MiB, has an unknown field or a value of the wrong type.Ожидается один JSON снимок(a single JSON snapshot is expected) — there is more data in the body after the object.Некорректный аккаунт, валюта или размер снимка (до 10000 объявлений)(invalid account, currency or snapshot size, up to 10000 ads) — an error inaccount_idorcurrency, or more than 10,000 rows.Некорректная timezone(invalid timezone) —timezoneis empty, unknown orLocal.Дата должна быть в пределах последних 31 дней(the date must be within the last 31 days) —dayis in a different format, in the future or older than 30 days.Обновите снимок расхода: время получения устарело или находится в будущем(refresh the spend snapshot: the receipt time is out of date or in the future) —fetched_atis not set or is outside the allowed window.Некорректный расход или повтор ID объявления(invalid spend or repeated ad ID) — an error inad_id, a repeatedad_id, a negative or too largespend.Суммарный расход слишком велик(the total spend is too large) — the sum of the snapshot rows in thecurrencycurrency is greater than 1,000,000,000.Недопустимый курс или сумма USD(unacceptable rate or USD amount) — the snapshot amount after conversion to USD is greater than 1,000,000,000.
Repeating the same snapshot with the same fetched_at is safe: the tracker responds 202 with "changed": false.
Download the extension
GET /api/v1/spend/extensionReturns the spend extension archive built for the panel domain, name and logo of this installation. It is the same file that the button on the Spend tab downloads — see Facebook spend extension.
Token scope: full. There are no parameters and no body.
Response — code 200, type application/zip, file name micator-spend.zip.
Example request:
curl https://panel.example.com/api/v1/spend/extension \
-H "Api-Key: <token>" \
-o micator-spend.zipErrors:
| Code | code | message | When |
|---|---|---|---|
503 | extension_config | Проверьте домен панели и логотип в конфигурации установки (check the panel domain and logo in the installation configuration) | The archive was not built — see The archive doesn't download |
Uploading from your own integration
- Save the settings with the
PUT /api/v1/spend/settingsmethod or on the Settings → Spend tab. - Send one
POST /api/v1/spend/snapshotsrequest for each “account — day” pair. - Request
GET /api/v1/spendand wait until the day you need has"pending": falsewith an emptyerror. - If
unmatched_adsis not zero, check that the link in the ad passes the ad ID in thesettings.ad_keyfield — see Source tokens and link parameters.
To distribute the spend across clicks that arrived after the upload, send the snapshot of that day again with a new fetched_at.