Admin API: request, response and error format
Common rules for all Admin API methods: request body, response codes, error shape, list pages, dates, time zones and money.
The rules on this page apply to all Admin API methods. The address, token and access types are described on the Admin API: overview and authorization page, and the parameters of individual methods on the pages of their sections.
Request body
- The body is a single JSON object in UTF-8 encoding. An array at the top level or several objects in a row are not accepted.
- Body size — up to 1 MiB.
- Unknown fields are skipped without an error. Exceptions:
POST /api/v1/spend/snapshotsand thedefinitionfield of a saved report — there an extra field gives a422error. - The
Content-Typeheader is not checked for JSON requests. Files and archives are uploaded asmultipart/form-data.
An empty body, a body larger than 1 MiB, invalid JSON and a value of the wrong type give code 400 in most methods:
{"code": "bad_request", "message": "invalid json"}Some methods return a different message, or respond with code 422 and code = validation.
Methods with a different size limit
POST /api/v1/spend/snapshots— up to 2 MiB.- Writing a text file of an offer or landing page (
PUT …/file) — content up to 5 MB. - Uploading an archive and files of an offer or landing page — up to 1 GB.
A file or archive over the limit gives code 413 with code = too_large.
Example of a request with a body:
curl -X POST https://panel.example.com/api/v1/affiliate-networks \
-H "Api-Key: <token>" \
-H "Content-Type: application/json" \
-d '{"name":"Example Network"}'Response — code 201 and the created record.
Identifiers
Record IDs are UUIDs, for example 3f2b8c1e-5a47-4d1b-9c0e-7a6d2f4b8e10. They come in the id field on creation and in lists, and are inserted into the address: /api/v1/campaigns/{id}. A click is identified by its subid value.
An ID in the wrong format gives code 400:
{"code": "bad_request", "message": "некорректный идентификатор или значение"}The message means “invalid ID or value”. A record with a correctly formed but non-existent ID gives 404 with code = not_found.
Success codes and response shapes
The response is JSON with the Content-Type: application/json; charset=utf-8 header.
| Code | When |
|---|---|
200 | Reading and updating |
201 | Creating a record |
202 | The task is queued: POST /api/v1/report-exports, POST /api/v1/spend/snapshots, POST /api/v1/ip2proxy/update |
204 | Deletion and some actions. No body |
A record comes as an object without a wrapper. Lists come in one of these shapes:
| Shape | Where |
|---|---|
Array […] | Offers, landing pages, sources, networks, domains, users, tokens, streams and most reference lists |
{"items": […], "total": N} | Campaigns |
{"items": […], "total": N, "limit": N} | A campaign's click and conversion logs |
{"items": […], "total": N, "page": N, "limit": N} | GET /api/v1/logs/audit |
{"items": […], "next_cursor": "…"} | GET /api/v1/logs/clicks, GET /api/v1/deliveries/search. logs/clicks additionally returns a names object |
{"items": […]} | GET /api/v1/reports/clicks, GET /api/v1/campaigns/{id}/stream-stats |
{"files": […]} | Offer and landing page files |
An empty list is [], not null.
Non-JSON responses come from the report export (text/csv) and from downloading a page archive or the spend extension (application/zip).
Errors
The error body is the same for all methods:
{"code": "validation", "message": "name is required"}code— the code to handle in your integration.message— an explanation for a human, in Russian or English.
There are no other fields. Check the HTTP code and code together: some methods return validation with code 400.
| HTTP | code | When |
|---|---|---|
400 | bad_request | The body was not parsed; invalid ID or value format |
401 | unauthorized | No token, or the token is invalid, revoked or expired |
402 | license_required | The license is inactive |
403 | token_scope | The method or report fields are outside the token's access |
403 | forbidden | The method is unavailable for the token or role |
403 | spend_paid_required | Spend is available only on a paid license |
404 | not_found | Record not found |
409 | conflict | Such a record already exists |
413 | too_large | The file or archive exceeds the limit |
422 | validation | Error in field values |
429 | rate_limited | Rate limit exceeded |
500 | internal | Internal error |
503 | unavailable | Temporary failure; the request can be repeated |
Some methods have their own codes, for example export_quota or currency_rate. They are listed in the method descriptions. Codes 401, 402, 403 and 429 are covered on the Admin API: overview and authorization page.
Updating with PUT
PUT behaves differently depending on the resource.
| Resource | Fields not in the body |
|---|---|
| Campaign, stream, offer, landing page | Kept. A field you pass is replaced as a whole, including nested objects. For a campaign, s2s_postbacks changes only if an array is passed |
| Source | The record is replaced as a whole. Not changed: capi_token and postback_secret if they are empty or equal to ***; cost_model and postback_mode if they are empty; postbacks if the field is missing or null |
| Affiliate network | name and postback_template are written. Without postback_template, the network's postback parameters are cleared |
| Conversion type, custom metric, blocklist registry | The record is replaced as a whole: fields not passed are cleared or get their default value |
| Domain | Only the fields you pass change. The group and the campaign on the domain root are removed with the clear_group and clear_index flags |
| Settings | Only the keys you pass change. An unknown key gives a 422 error with the message Неизвестная настройка (unknown setting) and the key name |
For a source, affiliate network, conversion type, custom metric and blocklist registry, a PUT without a field resets its value. Read the record and send all fields back.
Pagination
Page parameters depend on the method.
| Method | Parameters | Defaults and limits |
|---|---|---|
GET /api/v1/campaigns | limit, offset | limit 50. With limit=0, all records are returned |
POST /api/v1/reports | limit, offset in the body | limit from 1 to 1000, 0 means 1000. In the response, total_rows is the number of report rows, has_more tells whether there is a next page |
GET /api/v1/campaigns/{id}/clicks, …/conversions | limit, page | limit 100, maximum 1000. page starts from 1 |
GET /api/v1/reports/clicks | limit | 200, maximum 1000 |
GET /api/v1/logs/clicks, GET /api/v1/deliveries/search | limit, cursor | limit from 1 to 200, default 100. Period — up to 93 days |
GET /api/v1/logs/audit | page, limit | limit 100, maximum 500. page starts from 1 |
GET /api/v1/logs/{stream} | limit | 500, maximum 2000 |
GET /api/v1/domain-replacements | limit | 100, maximum 500 |
Do not set limit above the maximum. In campaign logs, reports/clicks, logs/audit, logs/{stream} and domain-replacements, a value above the maximum gives no error: it is replaced with the default value. In a report, such a value gives code 422; in logs/clicks and deliveries/search, code 400 with code = validation.
To go through a log with a cursor, pass cursor in the next request with the next_cursor value from the previous response. An empty next_cursor means there are no more records.
curl "https://panel.example.com/api/v1/logs/clicks?from=2026-10-01T00:00:00Z&to=2026-10-02T00:00:00Z&limit=200&cursor=<next_cursor>" \
-H "Api-Key: <token>"Other lists come in full. GET /api/v1/deliveries returns the last 200 records, GET /api/v1/report-exports up to 20, GET /api/v1/currency-rates up to 1000.
Sorting
Sorting can be set only in a report request — with the sort field:
{"sort": {"field": "profit", "direction": "desc"}}field— one of the selected groupings or a metric.direction—ascordesc.- Without
sort, the report is sorted byclicksin descending order.
The order of other lists cannot be configured:
- campaigns, offers, landing pages, sources, networks, domains, tokens — newest first;
- users, conversion types, custom metrics, blocklist registries — oldest first;
- streams — by
position; - domain groups — by name;
- saved reports — by modification date, newest first.
Dates and time zone
- Date and time — an RFC 3339 string:
2026-10-04T12:30:00Zor with an offset2026-10-04T15:30:00+03:00. In a query string, encode the+sign as%2B. - Calendar date —
YYYY-MM-DD: thedayfield in spend and currency rates. - The
fromandtoperiod boundaries are exact moments in time. The time zone is taken from the string itself. - Unix time: in seconds —
server_time,expires_atandlease_untilinGET /api/v1/license; in milliseconds —tsinGET /api/v1/logs/{stream}.
Without from and to, campaign logs, reports/clicks and logs/clicks return records for the last 24 hours. A date that could not be parsed is treated as not passed in campaign logs and reports/clicks, with no error. In logs/clicks and deliveries/search it gives code 400, in logs/audit — 422.
The report's time zone is set with the timezone field — a name from the IANA database, for example Europe/Moscow. An empty value means UTC. The time zone affects the calendar groupings hour, day, week, month, year, dow, hour_of_day. The week starts on Monday. More on the Period, time zone and attribution page.
The offer's daily cap, the stream's click caps and the stream schedule have their own time zone fields: cap_timezone, click_limits.timezone, schedule.tz. An empty value means UTC. In a spend snapshot, the timezone field is required: it is the ad account's time zone.
Money and currencies
- Amounts are JSON numbers, for example
12.5. - The report currency is USD. The
currencysetting accepts onlyUSD. - A campaign's
cost_valueis set in USD, from 0 to 1,000,000,000. For therevsharemodel, it is a percentage from 0 to 100. - A currency rate is set with the
usd_ratefield — how many USD one unit of the currency is worth.
A postback payout in another currency is converted to USD at the rate for the event date, and the rate is saved in the conversion. If there is no rate, the response is 422 with code = currency_rate. More on the Currencies and rates page.
Retrying requests
Code 503 with code = unavailable means a temporary failure:
{"code": "unavailable", "message": "temporary error, retry later"}If the response has a Retry-After header, wait the number of seconds it specifies and repeat the same request. The usual value is 5. A report responds to a temporary failure with 503 and the report_unavailable code: repeat such a request later too.
A 503 response with the plain text Tracker maintenance in progress comes during maintenance — see the Base URL section.
The server allows 90 seconds for a response. For uploading files and page archives and for downloading a page archive, the limits are longer: 10 minutes to receive the request body and 15 minutes for the response. Get large reports with a background export: Admin API: reports.