Documentation sections

Admin API: reports

17 methods for reports: building and CSV, background exports, saved reports, custom metrics, the click log, the cloaking decision and lead data.

The methods on this page build the same reports as the report builder in the panel and return data on individual clicks. Authorization is described on the Admin API: overview and authorization page, and the request format and common error codes on the Request, response and error format page.

Methods and token scopes

  • A Summary reports («Сводные отчёты») token (reports:read) has access to three methods: POST /api/v1/reports, POST /api/v1/reports/export and GET /api/v1/custom-metrics.
  • The other 14 methods need Full access («Полный доступ») (full).

A reports:read token builds a report only with the allowed groupings and filters. Their list is in the Summary reports access limits section. For a report with other fields, the server responds 403 with code token_scope.

Background exports and saved reports belong to the user who created the token. The API also shows the ones this user created in the panel.

Report request

Three methods accept the same body: POST /reports, POST /reports/export and POST /report-exports.

FieldTypeRequiredDescription
fromstring, RFC 3339 dateyesStart of the period. Both boundaries are included in the period
tostring, RFC 3339 dateyesEnd of the period
dimensionsarray of stringnoGroupings, up to 12. Without groupings, the report consists of one row
metricsarray of stringnoMetrics. The default: clicks, conversions, leads, sales, revenue, cost, profit, roi, cr, approval
filtersarray of objectnoConditions combined with AND
filter_groupsarray of objectnoGroups of conditions, up to 16
timezonestringnoTime zone in IANA format, for example Europe/Warsaw. The default is UTC. The value Local is not accepted
attributionstringnoclick, conversion or default. The default is default — the tracker setting
sortobjectno{"field": "...", "direction": "asc"}. The default is clicks in descending order
limitintegernoRows per page, from 1 to 1000. 0 or no field — 1000
offsetintegernoHow many rows to skip, at least 0
comparebooleannoComparison with the previous period

The time zone sets the boundaries of hours, days, weeks and months in groupings, and the spend day. What is counted by the click date and what by the conversion date is explained on the Period, time zone and attribution page.

Groupings

Groupdimensions values
Objectscampaign, stream, offer, lander, source, campaign_group
GEO and networkcountry, region, city, connection, isp, ip
Devicedevice, device_model, os, os_version, browser, browser_version, lang, user_agent
Timehour, day, week, month, year, dow, hour_of_day
Flagsbot, unique, cloak, proxy, empty_referer
Click parametersclick_id, external_id, creative_id, keyword, adset_name, ad_name, referer, sub_id_1 … sub_id_30
  • Grouping values in the response are always strings.
  • campaign, stream, offer, lander and source contain IDs, not names.
  • Flags come as 1 or 0.
  • A week starts on Monday. In dow, Monday is 1 and Sunday is 7.
  • The sub_id_12 value is not revealed: the row contains *** or nothing.

Metrics

Built-in metrics: clicks, unique_clicks, bots, lp_clicks, lp_ctr, leads, sales, rejected, conversions, cost, revenue, revenue_expected, profit, profit_expected, roi, roi_expected, cr, approval, cpa, epc, cpl. The formulas are on the Report metrics and formulas page.

You can also pass in metrics:

Amounts come as numbers in USD, percentages as numbers without the % sign.

Filters

A condition is an object of three strings:

FieldTypeRequiredDescription
fieldstringyesClick field or built-in metric
opstringyesOperator
valuestringyesValue, up to 2048 characters. A number is also passed as a string

The operators depend on the kind of field:

Field kindFieldsOperators
TextGroupings, except time, flags and click_id, plus subid and ad_campaign_ideq, ne, contains, not_contains, starts, ends, regexp, not_regexp, has, not_has
Flagbot, unique, cloak, proxy, empty_referer, ipv4, ipv6eq, ne
MetricBuilt-in metricsgt, gte, lt, lte, eq, ne
  • has and not_has check whether the field is filled in. The value is ignored.
  • For a flag, the values 0, false, no, off, нет and an empty string mean “no”; any other value means “yes”.
  • For a metric, value is a finite number.
  • You cannot filter by time groupings: the period is set by from and to.

A group in filter_groups is an object {"mode": "or", "filters": [...]}. The mode field takes and or or, and the filters array cannot be empty. Groups are combined with each other and with filters using AND. In an or group, you cannot mix click fields and metrics. A request can have no more than 64 conditions in total. The operator values and examples are on the Report filters page.

Sorting

sort.field is one of the selected groupings or a metric, built-in or custom. sort.direction is asc or desc. The other groupings follow in ascending order.

Building a report

Build a report

http
POST /api/v1/reports

Returns a page of the report and the totals for all rows. Token scope: reports:read or full. The body is a report request. A report is built in no more than 70 seconds.

Response — code 200:

FieldTypeDescription
columnsarray of stringColumn keys: groupings first, then metrics in the order of the request
rowsarray of arrayRows. The values follow the order of columns
totalsobject or nullTotals for all rows of the report, not for the page. The keys are the requested metrics. If there are no rows — null
total_rowsintegerHow many rows the report has in total
limit, offset, row_limitintegerThe applied page size and offset. row_limit equals limit
truncatedbooleantrue if not all rows of the report got into rows
has_morebooleantrue if there are more rows after this page
attributionstringThe applied attribution: click or conversion
timezonestringThe time zone the report was calculated in

Compare timezone in the response with the requested one: if the time zone is not available on the server, the report is calculated in UTC.

To get the next page, repeat the request with offset increased by limit while has_more is true.

Example request:

bash
curl -X POST https://panel.example.com/api/v1/reports \
  -H "Api-Key: <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "from": "2026-10-01T00:00:00Z",
    "to": "2026-10-07T23:59:59Z",
    "timezone": "Europe/Warsaw",
    "dimensions": ["campaign", "country"],
    "metrics": ["clicks", "leads", "cost", "revenue", "roi"],
    "filters": [
      {"field": "bot", "op": "eq", "value": "0"},
      {"field": "clicks", "op": "gte", "value": "100"}
    ],
    "sort": {"field": "roi", "direction": "desc"},
    "limit": 100
  }'

Example response:

json
{
  "columns": ["campaign", "country", "clicks", "leads", "cost", "revenue", "roi"],
  "rows": [
    ["c41a9e07-52bd-4f13-8a60-9d7e6f5a4b3c", "PL", 1520, 42, 310.5, 504, 62.318840579710144],
    ["7b0c1d52-3f4e-4a6b-9c8d-1e2f3a4b5c6d", "CZ", 830, 17, 190, 204, 7.368421052631579]
  ],
  "totals": {"clicks": 2350, "cost": 500.5, "leads": 59, "revenue": 708, "roi": 41.45854145854146},
  "truncated": false,
  "row_limit": 100,
  "total_rows": 2,
  "limit": 100,
  "offset": 0,
  "has_more": false,
  "attribution": "click",
  "timezone": "Europe/Warsaw"
}
Comparison with the previous period

With "compare": true, two fields are added to the response:

  • previous_rows — rows of the previous period. The row with the same index in rows belongs to the same group. If a group has no data in one of the periods, its metrics are 0.
  • previous_totals — totals of the previous period.

The previous period has the same length and ends before from. Conditions:

  • the period is no longer than 366 days;
  • there is no more than one of hour, day, week, month, year among the groupings;
  • each of the two periods has no more than 100,000 rows.

A sort metric that is not in metrics is added to columns as the last column. Without sort, it is clicks. How to read the comparison is explained on the Period comparison and daily trend page.

Errors:

CodecodeWhen
403token_scopeA reports:read token, and the groupings or filters contain a field outside the allowed list
422validationfrom and to are not specified; an unknown grouping, metric, filter field or operator; more than 12 groupings; limit out of range; invalid sort, attribution or timezone; the comparison conditions are not met. The reason is in message
503unavailableTemporary failure. The response has the Retry-After: 5 header
503report_unavailableThe report was not built: an analytics failure or the time limit was exceeded. Repeat the request later or narrow the report

Get a report as a CSV file

http
POST /api/v1/reports/export

Builds the same report and returns it as a file right away. Token scope: reports:read or full. The body is a report request. The limit and offset fields do not affect the contents of the file: all rows go into it.

Response — code 200, type text/csv; charset=utf-8, a file name like report-20261007-101500.csv. The first line contains the column keys; there is no totals row. The file contents are described in the What's inside the CSV section, and the columns of a comparison file in the Comparison CSV section.

Limits: 100,000 rows and 32 MiB.

The errors are the same as for POST /reports, plus two more:

CodecodeWhen
422validationThe report has more than 100,000 rows: export exceeds 100000 rows; narrow the report filters. Narrow the period or the filters
500internalThe file is larger than 32 MiB. Narrow the report or remove some of the columns

Background CSV exports

A background export builds the same file without waiting for a response: the request puts the report in a queue, and the finished file is kept for 24 hours from its creation. All methods need full access. The same feature in the panel is described on the Saved reports and CSV export page.

Export object:

FieldTypeDescription
iduuidExport ID
statestringpending — in the queue, running — being built, ready — the file is ready, failed — error
error_codestringReason for the error; an empty string in other states
row_countintegerNumber of rows in the finished file
comparebooleantrue for a period comparison export
created_atstring, RFC 3339 dateCreation time
expires_atstring, RFC 3339 dateTime after which the export is deleted
Values of error_code
  • report_limit_or_validation — the report has more than 100,000 rows, or the report settings no longer pass validation;
  • file_too_large — the file is larger than 32 MiB;
  • scope_changed — the role or campaign access of the token's creator has changed;
  • interrupted — the build was interrupted several times;
  • report_failed — the report was not built.

Create an export

http
POST /api/v1/report-exports

Puts a report in the queue. The body is a report request. The offset field is ignored, and limit does not affect the contents of the file. The default attribution is fixed at the moment of the request.

Response — code 202 and an export object in the pending state.

CodecodeWhen
422validationУкажите корректный период отчёта (specify a valid report period) — there is no from or to, or to is earlier than from. The other reasons are the same as for POST /reports
429export_quotaThe user already has 2 exports in the pending and running states, or the team has 20 unexpired exports

List exports

http
GET /api/v1/report-exports

Returns the unexpired exports of the token's creator: an array of objects, up to 20, newest first. There are no parameters. Poll the list until the state of the export you need becomes ready or failed.

Download the file

http
GET /api/v1/report-exports/{id}/file

Returns the finished file: code 200, type text/csv; charset=utf-8, name report-<id>.csv. The id path parameter is the export ID.

CodecodeWhen
403scope_changedThe permissions of the export's owner changed after it was created. Create a new export
404not_foundThe token's creator has no export with this id: it has expired or was deleted
409not_readyThe state is not ready: Выгрузка ещё не готова (the export is not ready yet)
bash
curl https://panel.example.com/api/v1/report-exports/5e2f8a10-9b3c-4d7e-8f21-0a1b2c3d4e5f/file \
  -H "Api-Key: <token>" \
  -o report.csv

Delete an export

http
DELETE /api/v1/report-exports/{id}

Deletes an export in any state and frees a place in the quota. Response — code 204 with no body. Error: 404, code not_found.

Saved reports

A saved report is a name and a set of report builder settings: what opens in the panel from the list of saved reports. All methods need full access. Object:

FieldTypeDescription
iduuidID
namestringName, from 1 to 120 characters
definitionobjectReport settings
created_at, updated_atstring, RFC 3339 dateCreation and last change time

The definition fields. The object takes no more than 32 KiB, and a field outside this list gives a 422 error:

FieldTypeRequiredDescription
versionintegeryesAlways 1
modestringyesflat — a table, drilldown — drill-down
dimensionsarray of stringnoFrom 0 to 12 groupings without repeats
metricsarray of stringyesFrom 1 to 64 metrics without repeats
filtersarray of objectnoUp to 64 conditions, in the same format as in the report request
filter_groupsarray of objectnoGroups of conditions
attributionstringyesdefault, click or conversion
sortobjectyesfield and direction
page_sizeintegeryesFrom 1 to 1000
date_rangeobjectyespreset, tz, and for a range — from and to
Period and mode in definition
  • date_range.preset: today, yesterday, this_week, last_7, this_month, prev_month, last_30, this_year, all, range, range_time.
  • For range, the from and to dates are written as 2026-10-01, and for range_time as 2026-10-01T09:30. For the other values, from and to are not saved.
  • date_range.tz is an IANA time zone. An empty value is saved as UTC.
  • The drilldown mode needs at least one grouping. The year, month, week, day, hour, dow, hour_of_day and sub_id_12 groupings are available only in the flat mode.

List saved reports

http
GET /api/v1/report-views

Returns an array of the saved reports of the token's creator. Recently changed ones come first. There are no parameters.

Save a report

http
POST /api/v1/report-views

Creates a saved report. The body has two required fields: name and definition. Response — code 201 and the object.

Error 422 with code validation: an invalid name, settings larger than 32 KiB, an unknown field in definition, an unknown grouping, metric or operator. The reason is in message.

Edit a saved report

http
PUT /api/v1/report-views/{id}

Replaces the name and settings entirely. The body and checks are the same as when creating. Response — code 200 and the object. Errors: 422 with code validation, 404 with code not_found.

Delete a saved report

http
DELETE /api/v1/report-views/{id}

Response — code 204 with no body. Error: 404, code not_found.

Custom metrics

A custom metric is a report column calculated by a formula from built-in metrics. Metrics are shared by the whole team. The list is available to reports:read and full tokens; creating, editing and deleting — only to full. Object:

FieldTypeRequiredDescription
iduuid—ID, set by the tracker
namestringyesMetric key for metrics and sort.field. Latin letters, digits and _. Does not match a built-in metric and does not start with cv_
formulastringyesFormula, for example revenue / clicks * 100. The rules are in the Formula section
titlestringnoColumn name in the panel
formatstringnonumber, currency or percent. The default is number
decimalsintegernoDecimal places. The default is 0

List custom metrics

http
GET /api/v1/custom-metrics

Returns an array of metrics. There are no parameters.

Create a metric

http
POST /api/v1/custom-metrics

The body contains the object fields without id. Response — code 201 and the object.

Error 422 with code validation: name и formula обязательны (name and formula are required), имя совпадает с базовой метрикой (the name matches a base metric), имя — только латиница, цифры и _ (the name — only Latin letters, digits and _) or a message that starts with ошибка в формуле: (formula error:). The messages are explained in the Error messages section.

Edit a metric

http
PUT /api/v1/custom-metrics/{id}

Pass all fields: name, formula, title and decimals are replaced with the values from the body. An empty format keeps the previous format. Response — code 200 and the object. Errors: 422 with code validation, 404 with code not_found.

Delete a metric

http
DELETE /api/v1/custom-metrics/{id}

Response — code 204 with no body. Error: 404, code not_found.

Clicks

Three methods return data on individual clicks. All of them need full access. Searching for a click by IP, subid or external_id and the full click card are described on the Admin API: logs and diagnostics page.

Click log

http
GET /api/v1/reports/clicks

Returns the latest clicks across all campaigns for the period, newest first.

ParameterWhereTypeRequiredDescription
fromquerystring, RFC 3339 datenoStart of the period. The default is 24 hours ago
toquerystring, RFC 3339 datenoEnd of the period. The default is the current time
limitqueryintegernoNumber of clicks, from 1 to 1000. By default and for a value out of range — 200

A date that could not be parsed is replaced with the default value. Encode the + sign in a time zone offset as %2B, or pass the time in UTC with the letter Z.

Response — code 200 and an object {"items": [...]}. The click fields: time, click_id, campaign_id, source_id, offer_id, country, city, device, os, browser, ip, is_bot, cost, external_id, sub_id_1.

Errors: 503 with code no_analytics — analytics is unavailable, 400 with code report_error — the query failed.

Cloaking decision for a click

http
GET /api/v1/reports/click-cloak?subid=<subid>

Returns the cloaking decision for a click. The subid query parameter is required — it is the click_id from the click log.

Response — code 200. If no decision is saved for the click, you get {"found": false}. Otherwise — {"found": true, "verdict": {...}}:

verdict fieldTypeDescription
resultstringmoney — passed to the target stream, white — filtered out, challenge — waiting for the JS check
stagestringThe stage at which the decision was made: server or challenge
scoreintegerScore of the JS challenge
humanbooleanWhether the visitor was recognized as a human at the challenge stage
reasonsarray of stringReasons for the decision. They are explained on the Diagnostics: why a click went to “white” page
detailsstringExplanation as text
tsintegerTime of the decision, Unix time in milliseconds

Errors: 400 with code bad_request — there is no subid parameter; 404 with code not_found and the message click not found — there is no such click.

Lead data for a click

http
GET /api/v1/reports/click-lead?subid=<subid>

Returns the contact details that a landing page form bound to the click. The subid query parameter is required.

Response — code 200 and an object {"found": true, "lead": {...}}. The lead fields: email, phone, first_name, last_name. If there is no data for the click, found is false and the lead fields are empty.

The errors are the same as for the cloaking decision.

Updated Need help? ↗