DocsAPIs
View as MarkdownReporting API
Pull your clicks, conversions, and earnings into your own dashboards, and check every transaction against the postbacks you received.
The Reporting API gives you the numbers from your dashboard as JSON: statistics for any period, grouped the way you need, and the full list of transactions. Use it to build your own reports, to compare traffic sources, and to check that you received a postback for every transaction.
Authentication
Use the same API key as for the Offers API. You find it in your dashboard under Apps, your app, Integration. Send it in the Authorization header:
Authorization: Bearer shk_your_api_key
Call the API from your server only. Each key only returns data of its own app. Reports work for live and paused apps.
Dates and time zone
All dates and times are in the time zone of your account, the same as in your dashboard (Account, Time zone), or in the zone you send as timezone. Every answer names its zone in timezone. from and to are whole days in YYYY-MM-DD format, and both are included. Without them you get the last 30 days up to today. One request can cover up to 92 days: split longer periods into several requests.
A transaction belongs to the day it was credited, even if it was reversed later. Timestamps in responses are ISO 8601 with the offset, for example 2026-09-23T23:59:49+02:00.
Statistics
https://api.moonetix.com/v1/stats| Query parameter | Description |
|---|---|
fromoptionaldate, YYYY-MM-DD | First day, in the report time zone. Defaults to 29 days before to. |
tooptionaldate, YYYY-MM-DD | Last day, in the report time zone. Defaults to today. |
timezoneoptionalstring | An IANA time zone for the dates and times, for example America/New_York, Asia/Kolkata, or UTC. Defaults to the time zone of your account. |
group_byoptionalstring | day (default), country, offer, integration, or sub1 to sub5. integration is offerwall, api, or sdk: how the user reached the offer. With day, every day of the period has a row, even without activity. Other groups are sorted by earnings, highest first. |
Every row has these fields. The totals object has only the counts (clicks, credited, reversed), payout_usd, and reward.
| Field | Description |
|---|---|
day, country, offer, integration, or sub1 to sub5 | The group of the row, named after group_by. It is null for traffic without a value, for example clicks without sub1. With offer, the row also has offer_name. |
clicks | Offers your users started. |
credited, reversed | Transactions in each state right now. A transaction that was credited and later reversed counts as reversed. |
payout_usd | Your earnings from credited transactions, in US dollars. |
reward | What your users received from credited transactions, in your currency. |
conversion_rate | Out of 100 clicks, how many led to at least one credited transaction, or null without clicks. A multi-step offer counts once, however many of its steps were credited, so this never goes above 100. credited still counts every credited step. |
epc_usd | Earnings per click in US dollars, or null without clicks. |
Conversions
https://api.moonetix.com/v1/conversionsEvery transaction of the period, oldest first, with the same values you receive in postbacks.
| Query parameter | Description |
|---|---|
from, tooptionaldate, YYYY-MM-DD | The period, in the report time zone, as for statistics. |
timezoneoptionalstring | An IANA time zone for the dates and times, for example America/New_York, Asia/Kolkata, or UTC. Defaults to the time zone of your account. |
statusoptionalstring | credited or reversed. Leave it out to get both. |
user_idoptionalstring | Only transactions of this user, for example to answer a support request. |
limitoptionalinteger, 1 to 500 | Transactions per page. Defaults to 100. |
afteroptionalstring | The next_cursor of the previous page. Leave it out for the first page. |
{
"ok": true,
"from": "2026-09-01",
"to": "2026-09-07",
"timezone": "Europe/Budapest",
"currency": "Coins",
"next_cursor": "1103",
"conversions": [
{
"transaction_id": "9ca45437948f42022b8118b0",
"user_id": "user_1234",
"status": "credited",
"offer_id": 120,
"offer_name": "Coin Quest",
"reward": 146,
"payout_usd": 0.225,
"country": "DE",
"device": "android",
"sub1": "homepage", "sub2": null, "sub3": null, "sub4": null, "sub5": null,
"credited_at": "2026-09-03T23:59:49+02:00",
"reversed_at": null
}
]
}
When next_cursor is not null, there are more transactions: send the same request again with after set to that value.
Check that you received every postback
Postbacks are retried for hours, but a server outage on your side can still make you miss one. Once a day, compare your records with the API:
- Request the last 7 days with
status=credited, and page through all results. A transaction is listed under the day it was credited, so this also covers days when your check did not run. - For every
transaction_idyou have not credited yet, credit the user withreward. - Do the same with
status=reversedfor transactions you credited but have not taken back. A reversal is listed under the day the transaction was credited and can come weeks later, so check reversals over a longer window, for example 92 days.
Use transaction_id as the unique key, exactly as in your postback handler, so a transaction is never credited twice. You can also resend a failed postback from your dashboard under Reports, Postbacks.
Errors
Errors come as JSON with "ok": false, a stable error code, and a message for your logs.
| HTTP | error | What to do |
|---|---|---|
| 400 | invalid_date, invalid_range, range_too_long, invalid_group_by, invalid_status, invalid_limit, invalid_after, invalid_user_id, invalid_timezone | Fix the parameter named in the code. |
| 401 | invalid_api_key | Send the key in the Authorization header, and copy it again if you replaced it. |
| 403 | app_not_available | Reports are not available for rejected apps or suspended accounts. |
| 405 | method_not_allowed | Use GET. |
| 429 | rate_limited | Wait for the number of seconds in the Retry-After header. |
Limits
- Each app can make 60 reporting requests per minute. This limit is separate from the Offers API.
- Numbers for today change as users complete offers. A credited transaction can still be reversed later, and then counts as reversed.