Reports
POST /publisher/reports/ and POST /advertiser/reports/ return your statistics: clicks, conversions and money, grouped by day, month, offer, website and more. Each request returns one report. type picks the grouping, fields picks the numbers, and filters limits the period and what is counted.
Reports are requested with POST, so they need a token with the write scope. A token with only read gets 403 Forbidden.
A first report
A publisher's clicks, conversions and earnings for each day of October 2026:
curl -X POST "https://api.convertiser.com/publisher/reports/"
-H "Authorization: Token your-personal-secret-token-here"
-H "Content-Type: application/json"
-d '{
"type": "date",
"fields": ["clicks", "conversions", "approved_conversions", "approved_publisher_income"],
"filters": [
{"created": {"lookup": "gte", "value": "2026-10-01"}},
{"created": {"lookup": "lte", "value": "2026-10-31"}}
],
"currency": "PLN",
"ordering": "date",
"page_size": 31
}'
{
"count": 31,
"next": null,
"previous": null,
"results": [
{
"date": "2026-10-01",
"gross_clicks": 1520,
"conversions_cnt_created": 14,
"conversions_cnt_approved": 9,
"publisher_payout_approved": 412.5
},
...
]
}
Each row is one group, here one day. The keys in a row are not the names you asked for in fields. The fields table pairs them up.
The period
Filter on created, the day a click or conversion was recorded. gte sets the first day and lte the last, and both days count in full:
"filters": [
{"created": {"lookup": "gte", "value": "2026-10-01"}},
{"created": {"lookup": "lte", "value": "2026-10-31"}}
]
Dates are ISO 8601 and are read in your account's timezone, which GET /my/timezone_preferences/ shows.
A date, month or year report needs both filters. It has one row for every day, month or year from gte to lte, zeros included. Without the filters it comes back empty, and with only one of them it has a single row. For the current month so far, set lte to today.
The other report types cover all your history when you leave out created.
Use gte and lte only. lt and gt also count the whole day you give them.
Report types
type is required and takes one grouping per request.
type | One row per | The row names it with |
|---|---|---|
date | Day | date |
month | Month | month and month_year |
year | Year | year |
hour | Hour of the day, 0 to 23, summed over the period | hour |
offer | Offer | offer_id and offer_title |
goal | Offer goal | goal_id and goal_title |
website | Website | website_id and website_url |
ctag1 | Traffic source | ctag1 and traffic_source |
Publishers can also group by subaccount and by keyword. Advertisers can group by atag1, the discount code a conversion came with.
A goal report, and an advertiser's atag1 report, count conversions only. Asking one of them for a click field returns 400 Bad Request.
Fields
fields is the list of numbers to return. Send it with every request, because a request without it fails. A field your account type may not ask for returns 400 Bad Request.
Counts, for both audiences:
In fields | In the row | What it counts |
|---|---|---|
clicks | gross_clicks | Clicks |
unique_clicks | unique_clicks | Clicks from different visitors |
conversions | conversions_cnt_created | Conversions recorded |
pending_conversions | conversions_cnt_pending | Conversions waiting for approval |
approved_conversions | conversions_cnt_approved | Approved conversions |
rejected_conversions | conversions_cnt_rejected | Rejected conversions |
approved_sale_amount | sale_amount_approved | The order value of approved conversions |
cr | cr | Approved conversions per 100 unique clicks |
Money for publishers:
In fields | In the row | What it is |
|---|---|---|
pending_publisher_income | publisher_payout_pending | Your commission on pending conversions |
approved_publisher_income | publisher_payout_approved | Your commission on approved conversions |
rejected_publisher_income | publisher_payout_rejected | Your commission on rejected conversions |
cpa | cpa | Approved commission per approved conversion |
cpc | cpc | Approved commission per unique click |
Money for advertisers:
In fields | In the row | What it is |
|---|---|---|
pending_advertiser_expense | advertiser_revenue_pending | What pending conversions cost you |
approved_advertiser_expense | advertiser_revenue_approved | What approved conversions cost you |
rejected_advertiser_expense | advertiser_revenue_rejected | What rejected conversions would have cost |
rpa | rpa | Approved cost per approved conversion |
rpc | rpc | Approved cost per unique click |
The reference for publishers and advertisers lists the rest.
Money comes back as a JSON number rounded to two decimal places, in the currency you ask for with currency: EUR, USD or PLN, in capitals. Without currency, it is your account's default currency.
Filters
Besides created, filters takes:
| Filter | Value |
|---|---|
offer | An offer id, such as offer_id from an offer report |
goal | A goal id, such as goal_id from a goal report |
website | A website id, such as website_id from a website report |
A count from fields, such as approved_conversions | A number |
For publishers, a money field such as approved_publisher_income | A number |
The ids take exact. A list as value matches any of its ids, and ~exact leaves them out. An id the API does not recognise is ignored without an error, and the report then covers every offer, goal or website. If a report is larger than you expect, check the ids.
The counts and money take exact, lt, lte, gt and gte, and apply to each row of the report, after the grouping. {"approved_conversions": {"lookup": "gt", "value": 0}} drops the rows with no approved conversions.
Every filter has to hold.
Ordering and pages
ordering takes one name from fields, or the type's own name such as date. A - in front sorts from the highest.
page_size is 20 by default and at most 100. next and previous are URLs with a page number in the query string, but a POST reads page from the body. For the next page, send the same body again with "page": 2. A page past the last one returns 404 Not Found.
Publisher examples
This month's earnings per offer, the best first:
{
"type": "offer",
"fields": ["clicks", "approved_conversions", "pending_publisher_income", "approved_publisher_income"],
"filters": [
{"created": {"lookup": "gte", "value": "2026-10-01"}}
],
"ordering": "-approved_publisher_income"
}
One offer, day by day, for the first half of October:
{
"type": "date",
"fields": ["clicks", "unique_clicks", "conversions", "cr"],
"filters": [
{"created": {"lookup": "gte", "value": "2026-10-01"}},
{"created": {"lookup": "lte", "value": "2026-10-15"}},
{"offer": {"lookup": "exact", "value": "your-offer-id"}}
],
"ordering": "date"
}
Your websites in 2026 that brought at least one approved conversion, in euro:
{
"type": "website",
"fields": ["clicks", "approved_conversions", "approved_publisher_income", "cpc"],
"filters": [
{"created": {"lookup": "gte", "value": "2026-01-01"}},
{"created": {"lookup": "lte", "value": "2026-12-31"}},
{"approved_conversions": {"lookup": "gt", "value": 0}}
],
"currency": "EUR",
"ordering": "-approved_conversions"
}
Advertiser examples
Each month of 2026, with the order value and what it cost:
curl -X POST "https://api.convertiser.com/advertiser/reports/"
-H "Authorization: Token your-personal-secret-token-here"
-H "Content-Type: application/json"
-d '{
"type": "month",
"fields": ["clicks", "conversions", "approved_conversions", "approved_sale_amount", "approved_advertiser_expense"],
"filters": [
{"created": {"lookup": "gte", "value": "2026-01-01"}},
{"created": {"lookup": "lte", "value": "2026-12-31"}}
],
"ordering": "month"
}'
The publishers' websites that sent the most approved conversions to one offer in September:
{
"type": "website",
"fields": ["clicks", "approved_conversions", "approved_advertiser_expense", "rpa"],
"filters": [
{"created": {"lookup": "gte", "value": "2026-09-01"}},
{"created": {"lookup": "lte", "value": "2026-09-30"}},
{"offer": {"lookup": "exact", "value": "your-offer-id"}}
],
"ordering": "-approved_conversions",
"page_size": 100
}
Conversions waiting for your approval, per goal:
{
"type": "goal",
"fields": ["pending_conversions", "pending_advertiser_expense"],
"filters": [
{"pending_conversions": {"lookup": "gt", "value": 0}}
],
"ordering": "-pending_conversions"
}
When a report is empty
If the statistics service cannot answer, the API still answers 200 OK, with "count": 0 and no rows. A report that should have rows and comes back like that is worth requesting again a little later.