Liftoff's Reporting API enables automated access to campaign reports with groupings and metrics of your choice. It gives your BI team direct, programmatic access to campaign data. Discover insights on campaign performance by analyzing funnel metrics: impressions, clicks, installs and in-app events across different groupings (e.g. country, publisher app, ad format, creatives, etc). Perform cohort analysis through the API by specifying the look-back window relative to the install.
Note: Python examples assume that you have Requests installed.
Data refresh frequency: All reports are continuously updated with a delay of several hours for certain fields. The latest full day's report will be available by 5am UTC the following day. For example, to retrieve a complete report for September 12th, 2024, please request on September 13th, 2024 at 5am or later. The max date range per request is one year.
Quick navigation
- Getting started
- Authentication
- API structure
- Errors
- Endpoints
- Sample workflow
- What you can do with the Reporting API
- Common use cases
- Frequently asked questions
Getting started
- Contact your Account Manager to get API key and API secret sent to you.
- Call GET /customer to retrieve your billing timezone. Use this value in all subsequent report requests.
- Explore the various entities associated with your account (i.e. apps, creatives, campaigns, events) via the following endpoints. You will be prompted for a password.
curl --user 'API_KEY:API_SECRET' --url https://data.liftoff.io/api/v1/apps
curl --user 'API_KEY:API_SECRET' --url https://data.liftoff.io/api/v1/creatives
curl --user 'API_KEY:API_SECRET' --url https://data.liftoff.io/api/v1/campaigns
curl --user 'API_KEY:API_SECRET' --url https://data.liftoff.io/api/v1/eventsrequests.get(
"https://data.liftoff.io/api/v1/apps",
auth=("API_KEY", "API_SECRET"),
)
requests.get(
"https://data.liftoff.io/api/v1/creatives",
auth=("API_KEY", "API_SECRET"),
)
requests.get(
"https://data.liftoff.io/api/v1/campaigns",
auth=("API_KEY", "API_SECRET"),
)
requests.get(
"https://data.liftoff.io/api/v1/events",
auth=("API_KEY", "API_SECRET"),
)Create a report request and validate it with the test=true query parameter. This ensures that you don't run into rate limits while testing.
curl --header "Content-Type: application/json" \
--request POST \
--data '{"start_time":"2019-01-01","end_time":"2019-01-03"}' \
--user 'API_KEY:API_SECRET' \
--url https://data.liftoff.io/api/v1/reports?test=truerequests.post(
"https://data.liftoff.io/api/v1/reports",
json={
"start_time": "2019-01-01",
"end_time": "2019-01-03",
},
params={"test": "true"},
auth=("API_KEY", "API_SECRET"),
headers={"Content-Type": "application/json"},
)Check that it passes without errors.
{ "message": "Validation passed without errors." }Generate the report without the test query parameter.
curl --header "Content-Type: application/json" \
--request POST \
--data '{"start_time":"2019-01-01","end_time":"2019-01-03"}' \
--user 'API_KEY:API_SECRET' \
--url https://data.liftoff.io/api/v1/reportsrequests.post(
"https://data.liftoff.io/api/v1/reports",
json={
"start_time": "2019-01-01",
"end_time": "2019-01-03",
},
auth=("API_KEY", "API_SECRET"),
headers={"Content-Type": "application/json"},
)The report response will contain a report ID if it was successfully created.
{
"id": "abc123",
"created_at": "2020-11-15T00:00:00Z",
"state": "queued",
"parameters": {
"start_time": "2019-01-01",
"end_time": "2019-01-03"
}
}The status of the report can be polled via the following:
- Recommended: Provide a callback URL with the report request. When the report is completed/failed/cancelled, a message will be sent to the provided callback URL. An OPTIONS request is made to the endpoint before report creation to verify the host is reachable.
- Poll the /reports/{ID}/status endpoint once every minute.
curl --user 'API_KEY:API_SECRET' \
--url https://data.liftoff.io/api/v1/reports/{ID}/statusrequests.get(
"https://data.liftoff.io/api/v1/reports/{ID}/status",
auth=("API_KEY", "API_SECRET"),
){
"id": "abc123",
"created_at": "2020-11-15T00:00:00Z",
"state": "completed",
"parameters": {
"start_time": "2019-01-01",
"end_time": "2019-01-03"
}
}When the report is generated, its state will be marked as "completed", and the report data can be obtained via the following:
curl --user 'API_KEY:API_SECRET' \
--url https://data.liftoff.io/api/v1/reports/{ID}/datarequests.get(
"https://data.liftoff.io/api/v1/reports/{ID}/data",
auth=("API_KEY", "API_SECRET"),
)Authentication
Liftoff's Reporting API supports HTTP basic authentication.
curl --request GET \
--user 'API_KEY:API_SECRET' \
--url https://data.liftoff.io/api/v1/reportsrequests.get(
"https://data.liftoff.io/api/v1/reports",
auth=("API_KEY", "API_SECRET"),
)API structure
All API URLs start with https://data.liftoff.io/api/v1. Different endpoints can be called by appending to the base url. All available end points are described in the table below:
| Endpoint | HTTP Method | Description |
|---|---|---|
| /reports | GET | Look up metadata for recently submitted reports. |
| /reports | POST | Generate a report. |
| /reports/{ID}/status | GET | Get the status of a report. |
| /reports/{ID}/data | GET | Download the report in CSV or JSON format. |
| /{ENTITY_TYPE} | GET | Get entity details (apps, campaigns, creatives or events). |
Rate limits
All valid endpoints are rate-limited and will return the following headers:
- x-rate-limit-max
- x-rate-limit-remaining
| Endpoint | HTTP Method | Hourly Rate Limit |
|---|---|---|
| /api/v1/reports | GET | 60 |
| /api/v1/reports | POST | 30 |
| /api/v1/reports?test=true | POST | 60 ← separate, higher limit for testing |
| /api/v1/reports/{ID}/status | GET | 1000 |
| /api/v1/reports/{ID}/data | GET | 30 |
| /api/v1/{ENTITY_TYPE} | GET | 60 |
Errors
The following errors can be returned by the API:
- 400 "BAD REQUEST"
- 403 "ACCESS DENIED"
- 404 "NOT FOUND"
- 429 "TOO MANY REQUESTS"
- 500 "INTERNAL ERROR"
Error Format:
| Name | Type | Description |
|---|---|---|
| error_type | string | Error type (e.g. "BAD REQUEST"). |
| message | string | General message on possible error cause. |
| errors | array of strings (nullable) | List of issues with request. |
Sample Error:
{
"error_type": "BAD REQUEST",
"message": "Please correct the following issues with the request.",
"errors": [
"start_time should be before end_time.",
"Max allowable date range is one year."
]
}Endpoints
GET /reports
This endpoint allows you to look up metadata for recently submitted reports.
curl --user 'API_KEY:API_SECRET' --url https://data.liftoff.io/api/v1/reportsrequests.get(
"https://data.liftoff.io/api/v1/reports",
auth=("API_KEY", "API_SECRET"),
)Response Format:
| Name | Type | Description |
|---|---|---|
| id | string | The report ID. |
| created_at | timestamp (string) | Timestamp in UTC (e.g. "2021-10-01T00:00:00Z"). |
| parameters | object | The JSON body that was provided by the POST request. |
| state | string | "queued", "cancelled", "completed" or "failed". |
Sample Response:
[
{
"id": "abc123",
"created_at": "2021-11-15T00:00:00Z",
"state": "completed",
"parameters": {
"start_time": "2021-10-01",
"end_time": "2021-11-01"
}
}
]POST /reports
This endpoint allows you to generate a report. When the test query parameter is set, no report is generated, but the request will still get validated. This also has a separate rate-limit compared to requests without the query parameter.
curl --header "Content-Type: application/json" \
--request POST \
--data '{"group_by":["apps","campaigns"],"start_time":"2020-10-01","end_time":"2020-11-01"}' \
--user 'API_KEY:API_SECRET' \
--url https://data.liftoff.io/api/v1/reportsrequests.post(
"https://data.liftoff.io/api/v1/reports",
json={
"group_by": ["apps", "campaigns"],
"start_time": "2020-10-01",
"end_time": "2020-11-01",
},
auth=("API_KEY", "API_SECRET"),
headers={"Content-Type": "application/json"},
)Request Format:
| Name | Type | Default | Description |
|---|---|---|---|
| app_ids | array of strings (nullable) | null | Only include specified app IDs or all. |
| campaign_ids | array of strings (nullable) | null | Only include specified campaign IDs or all. |
| event_ids | array of strings (nullable) | null | All events belonging to the account. |
| start_time | timestamp (string) | Required | ISO date and time in UTC or ISO date (with time at start of day for given timezone). |
| end_time | timestamp (string) | Required | ISO date and time in UTC or ISO date (with time at start of day for given timezone). |
| group_by | array of strings | ["apps", "campaigns"] | Group metrics by one of the available presets. |
| cohort_window | int (1 - 90) (nullable) | null | Number of days since install. If specified, events will be counted since the day of install. |
| format | string (nullable) | "csv" | "csv" or "json". |
| callback_url | string (nullable) | null | A URL that will receive a POST request once the report completed or failed. |
| timezone | string (nullable) | "UTC" | A TZ database name to use for date groupings. Tip: Call GET /customer to get your billing timezone and use it here to align with your dashboard and invoices. |
| include_repeat_events | boolean (nullable) | true | Counts all instances of post-install events will be counted, instead of just first occurrences. |
| remove_zero_rows | boolean (nullable) | false | Drop all rows whose numeric metrics are equal to 0. |
| use_two_letter_country | boolean (nullable) | false | Use the two letter country code (as opposed to the default three letter code). |
| include_skan_installs_with_no_conversion_value | boolean (nullable) | false | Include the number of SKAN installs with no conversion value as an additional metric. |
| include_skan_installs_with_conversion_value | boolean (nullable) | false | Include the number of SKAN installs with a conversion value as an additional metric. |
| video_play_milestones | boolean (nullable) | false | Include 4 video play milestone metrics for VAST impressions: video_starts, video_plays_at_25_percent, video_plays_at_50_percent, video_plays_at_75_percent, and video_completes. Available only for reports grouped by at least one of creative, ad_format, or country, and not by publisher or is_interstitial. |
| demand_product | string (nullable) | null | Return only data for a specific demand product. Can be "accelerate" (Liftoff's managed programmatic product), or "direct" (direct deals). If not provided returns all data according to API user's permissions. |
| include_demand_product | boolean (nullable) | false | Include column for demand product. Only available for reports with a group_by of campaigns. |
| include_customer_revenue | boolean (nullable) | false | Include customer revenue columns (total_customer_revenue, plus a revenue_from_<event_name>_event column for each specified event). Use alongside cohort_window to calculate ROAS (divide revenue by spend in your BI tool). |
Available Group By Presets:
["apps" "campaigns"]["apps" "campaigns" "ad_format"]["apps" "campaigns" "country"]["apps" "campaigns" "country" "publisher"]["apps" "campaigns" "creatives"]["apps" "campaigns" "creatives" "country"]["apps" "campaigns" "creatives" "country" "publisher"]["apps" "campaigns" "creatives" "is_interstitial"]["apps" "campaigns" "creatives" "publisher"]["apps" "campaigns" "creatives" "publisher" "is_interstitial"]["apps" "campaigns" "is_interstitial"]["apps" "campaigns" "publisher"]["apps" "campaigns" "publisher" "is_interstitial"]
Sample request body:
{
"app_ids": ["abc456", "abc123"],
"cohort_window": 1,
"group_by": ["apps", "campaigns", "creatives"]
}Sample response:
{
"id": "abc123",
"created_at": "2021-11-15T00:00:00Z",
"state": "queued",
"parameters": {
"group_by": ["apps", "campaigns"],
"start_time": "2021-10-01",
"end_time": "2021-11-01"
}
}GET /reports/{ID}/status
This endpoint allows you to fetch the status of a single report. Similar to /reports endpoint but with a higher rate limit.
curl --user 'API_KEY:API_SECRET' https://data.liftoff.io/api/v1/reports/{ID}/statusrequests.get(
"https://data.liftoff.io/api/v1/reports/{ID}/status",
auth=("API_KEY", "API_SECRET"),
)Sample Response:
{
"id": "abc123",
"created_at": "2021-11-15T00:00:00Z",
"state": "failed",
"parameters": {
"group_by": ["apps", "campaigns"],
"start_time": "2021-10-01",
"end_time": "2021-11-01"
}
}GET /reports/{ID}/data
This endpoint allows you to download the completed report. The report can either be JSON/CSV depending on the format provided at report creation.
curl --user 'API_KEY:API_SECRET' \
--url https://data.liftoff.io/api/v1/reports/{ID}/datarequests.get(
"https://data.liftoff.io/api/v1/reports/{ID}/data",
auth=("API_KEY", "API_SECRET"),
)Report row format:
| Name | Type | Description |
|---|---|---|
| date | string | Date (YYYY-MM-dd) for the report timezone. |
| app_id | string | ID of the app that is being advertised. |
| campaign_id | string | ID of the advertising campaign. |
| creative_id | string | ID of the creative. |
| country_code | string (nullable) | 2 letter country-code of countries in which the ad is being shown. |
| publisher_app_store_id | string (nullable) | Bundle IDs of the publisher app. |
| publisher_name | string (nullable) | Display name of the publisher app. |
| ad_format | string (nullable) | Format of the ad unit. |
| is_interstitial | boolean | The ad slot supports interstitial creatives, those larger than MREC (300x250). |
| video_starts | int | Number of times a video started playing for VAST impressions. Only included if video_play_milestones is set. |
| video_plays_at_25_percent | int | Number of times a video played up to 25% for VAST impressions. Only included if video_play_milestones is set. |
| video_plays_at_50_percent | int | Number of times a video played up to 50% for VAST impressions. Only included if video_play_milestones is set. |
| video_plays_at_75_percent | int | Number of times a video played up to 75% for VAST impressions. Only included if video_play_milestones is set. |
| video_completes | int | Number of times a video finished playing for VAST impressions. Only included if video_play_milestones is set. |
| spend | float | Amount of budget that was spent in that time period. |
| impressions | int | Number of ad impressions shown. |
| clicks | int | Number of clicks on the ads. |
| installs | int | Number of installs resulting from the ads. |
| <event_name> | int | Number of events resulting from the ads. Note that an event has to be specified. |
| cpm | float | Cost per thousand impressions. Cost is defined as ad spend. |
| cpc | float | Cost per click. Cost is defined as ad spend. |
| ctr | float | Click through rate. Cost is defined as ad spend. |
| cpi | float | Cost per install. Cost is defined as ad spend. |
| cpa | float | Cost per action. Cost is defined as ad spend. Action refers to in-app events. |
| skan-installs-with-no-conversion-value | int (nullable) | Number of SKAN installs without a conversion value. Only included if include_skan_installs_with_no_conversion_value is set. |
| skan-installs-with-conversion-value | int (nullable) | Number of SKAN installs with a conversion value. Only included if include_skan_installs_with_conversion_value is set. |
| total_customer_revenue | float | Total customer revenue across all specified events. Only included if include_customer_revenue is set. |
| revenue_from_<event_name>_event | float | Customer revenue attributed to an event. One column per specified event. Only included if include_customer_revenue is set. |
Sample response:
{
"columns": ["date", "app_id", "campaign_id", "spend", "impressions", "..."],
"rows": [
["2019-02-08", "abc123", "zxy123", 123.12, 12, "..."]
]
}GET /apps
This endpoint allows you to fetch app details.
curl --user 'API_KEY:API_SECRET' \
--url https://data.liftoff.io/api/v1/appsrequests.get(
"https://data.liftoff.io/api/v1/apps",
auth=("API_KEY", "API_SECRET"),
)Response format:
| Name | Type | Description |
|---|---|---|
| id | string | ID of the app that is being advertised. |
| name | string | App title. |
| app_store_id | string | Apple/Play store app ID. |
| bundle_id | string | App bundle/package ID. |
| title | string | App title (as appears in creatives). |
| platform | string | "iOS" or "Android". |
| optimization_event | object (nullable) | Event selected for ML optimization. |
| state | string | "enabled" or "paused". |
Sample response:
[
{
"id": "abc123",
"name": "Sample",
"app_store_id": "io.liftoff.sample",
"bundle_id": "io.liftoff.sample",
"title": "Sample",
"platform": "Android",
"optimization_event": {
"id": "xyz123",
"name": "Tutorial"
},
"state": "enabled"
}
]GET /campaigns
This endpoint allows you to fetch campaign details.
curl --user 'API_KEY:API_SECRET' \
--url https://data.liftoff.io/api/v1/campaignsrequests.get(
"https://data.liftoff.io/api/v1/campaigns",
auth=("API_KEY", "API_SECRET"),
)Response format:
| Name | Type | Description |
|---|---|---|
| id | string | ID of the advertising campaign. |
| app_id | string | ID of the app. |
| name | string | Name of the advertising campaign. |
| campaign_type | string | "user-acquisition" or "reengagement". |
| tracker_type | string | "MMP", "SKAN" or "NONE". |
| min_os_version | string (nullable) | Minimum OS version of the campaign. |
| max_os_version | string (nullable) | Maximum OS version of the campaign. |
| state | string | "enabled" or "paused". |
| state_last_changed_at | timestamp (string) | Timestamp in UTC (e.g. "2021-10-01T00:00:00Z"). |
| demand_product | string | "accelerate" (Liftoff's managed programmatic product) or "direct" (direct deals). |
| campaign_model | string | "CPI", "CPA", "CPR", "LTV", "CPC" or "N/A". |
| targeted_language_codes | array of strings or "all" | "all" or lists specific device language targeting of the campaign. |
Sample response:
[
{
"id": "fff123",
"app_id": "abc123",
"name": "App - iOS - JP",
"campaign_type": "user-acquisition",
"tracker_type": "SKAN",
"min_os_version": "14.5",
"max_os_version": null,
"state": "enabled",
"state_last_changed_at": "2021-10-01T00:00:00Z",
"demand_product": "accelerate",
"campaign_model": "CPI",
"targeted_language_codes": ["en", "jp"]
}
]GET /creatives
This endpoint allows you to fetch creative details.
curl --user 'API_KEY:API_SECRET' \
--url https://data.liftoff.io/api/v1/creativesrequests.get(
"https://data.liftoff.io/api/v1/creatives",
auth=("API_KEY", "API_SECRET"),
)Response format:
| Name | Type | Description |
|---|---|---|
| id | string | ID of the ad creative. |
| name | string (nullable) | Name of the ad creative. |
| preview_url | string (nullable) | URL to preview the creative. |
| full_html_preview_url | string (nullable) | URL of the full HTML preview. |
| width | int (nullable) | Width of the creative (pixels). |
| height | int (nullable) | Height of the creative (pixels). |
| creative_type | string | How the creative is rendered: "native", "html" or "vast". |
| created_at | timestamp (string) | Timestamp in UTC (e.g. "2021-10-01T00:00:00Z"). |
| state | string | State of the creative: "enabled", "paused", or "deleted". |
| video_duration | int (nullable) | Duration of the creative video in seconds. |
| video_url | string (nullable) | CDN URL of the MP4 video creative. |
Sample response:
[
{
"id": "zzz123",
"name": "320x480_sample_html",
"width": 320,
"height": 480,
"preview_url": null,
"full_html_preview_url": null,
"creative_type": "html",
"created_at": "2021-10-01T00:00:00Z",
"state": "enabled",
"video_duration": 16,
"video_url": "https://cdn.liftoff.io/customers/1234abcd/videos/originals/1234.mp4"
}
]GET /customer
This endpoint allows you to fetch customer details.
curl --user 'API_KEY:API_SECRET' \
--url https://data.liftoff.io/api/v1/customerrequests.get(
"https://data.liftoff.io/api/v1/customer",
auth=("API_KEY", "API_SECRET"),
)Response format:
| Name | Type | Description |
|---|---|---|
| id | string | ID of the company. |
| company | string | Name of the company. |
| timezone | string | Customer timezone used for billing. Use this value on reporting requests to better align your pulls with Liftoff's dashboards, scheduled reports, and invoices. |
Sample response:
[
{
"id": "xyz123",
"company": "Liftoff",
"timezone": "US/Pacific"
}
]GET /events
This endpoint allows you to fetch event details.
curl --user 'API_KEY:API_SECRET' \
--url https://data.liftoff.io/api/v1/eventsrequests.get(
"https://data.liftoff.io/api/v1/events",
auth=("API_KEY", "API_SECRET"),
)Response format:
| Name | Type | Description |
|---|---|---|
| id | string | ID of the in-app event. |
| app_id | string | ID of the advertising app. |
| name | string | Event name. |
Sample response:
[
{
"id": "xyz123",
"app_id": "abc123",
"name": "Tutorial"
}
]Sample workflow
The following is a sample Python script for requesting and downloading reports.
import requests
import time
api_key = "API_KEY"
api_secret = "API_SECRET"
# Submit request to generate a new report.
report_response = requests.post(
"https://data.liftoff.io/api/v1/reports",
json={
"start_time": "2022-01-01",
"end_time": "2022-02-01",
},
auth=(api_key, api_secret),
headers={"Content-Type": "application/json"},
)
report_id = report_response.json()["id"]
print(f"Report requested. ID: {report_id}")
# Poll for report status until in a finished state.
state = ""
while state not in {"completed", "failed", "cancelled"}:
print("Waiting 30 seconds to check status...")
time.sleep(30)
status_response = requests.get(
f"https://data.liftoff.io/api/v1/reports/{report_id}/status",
auth=(api_key, api_secret),
)
state = status_response.json()["state"]
print(f"Current state: {state}")
# Download the completed report.
if state == "completed":
data_response = requests.get(
f"https://data.liftoff.io/api/v1/reports/{report_id}/data",
auth=(api_key, api_secret),
)
data_response.raise_for_status()
# Handle your raw report data here.
with open("report_response.csv", "w") as f:
f.write(data_response.text)
print("Report downloaded successfully!")What you can do with the Reporting API
Available dimensions
Reports are grouped by one of 13 fixed presets built from the following dimensions (see Group By Presets below for the full list):
- App
- Campaign
- Creative
- Country
- Publisher App
- Ad Format
- Interstitial (Y/N)
Every report automatically includes a daily date column - no need to specify it in your group_by.
You can also filter by demand product (Accelerate or Direct) or include a demand_product column (campaigns group_by only).
Creative-level spend: include creatives in your group_by to answer "which creative is spending the most?".
Available metrics
Core funnel metrics - returned in every report:
- Spend
- Impressions, Clicks, Installs
- CPM, CPC, CTR, CPI, CPA
- In-app events (any events configured on your account)
Optional metrics - enabled per request:
- Customer Revenue: total revenue across all events, plus revenue broken out per event. Set include_customer_revenue: true.
- SKAN Installs: reported separately as installs with or without a conversion value. Enable via the corresponding request flags.
- Video milestones: Starts, 25% / 50% / 75% plays, and Completes for VAST impressions. Set video_play_milestones: true. Requires your group_by to include at least one of creatives, ad_format, or country — cannot be combined with publisher or is_interstitial.
Cohort analysis
Use cohort_window to count in-app events relative to install date, from D1 up to D90. This lets you run day-level performance analysis (e.g. D7 ROAS, D30 event rates) in a single report pull. Cohort reports are limited to a 90-day date range.
- ROAS: Set include_customer_revenue: true alongside your cohort_window. The API returns revenue values; divide by spend in your BI tool to calculate ROAS.
- Retention: Only available if session or login events are configured as postbacks to Liftoff. Please check with your AM.
Timezone alignment
Call GET /customer to retrieve your billing timezone. Use this value in all report requests so your data aligns with your Liftoff dashboard and invoices.
Common use cases
| Use case | Supported? | How to query |
|---|---|---|
| Creative with the highest spend for a specific time frame | Yes | group_by: apps + campaigns + creatives, filter by date range, sort by spend in your BI |
| D7 ROAS by country | Yes | group_by: apps + campaigns + country, cohort_window: 7, include_customer_revenue: true. Divide revenue by spend. |
| Publisher apps that drive the most installs | Yes |
group_by: apps + campaigns + publisher, sort by installs Publisher reports are limited to a 7-day range. For longer ranges, make separate calls of 7 days each, or pull the data more regularly to store it. |
| Video completion rate per creative | Yes | group_by must include creatives (not publisher or is_interstitial), set video_play_milestones: true |
| D30 ROAS by campaign | Yes | cohort_window: 30, include_customer_revenue: true, group_by: apps + campaigns |
| Best performing ad format | Yes | group_by: apps + campaigns + ad_format |
| D1 / D7 retention rate | Conditional | Requires session or login events configured as postbacks. If set up: use cohort_window: 7, then divide event count by installs. Confirm with your AM. |
| Hourly spend breakdown within a day | Not available | API granularity is daily only. Contact your Account Manager for intra-day data requests. |
| Age / gender distribution | Not available | Not supported in the API. Contact your Account Manager for additional reporting. |
Frequently asked questions
Why do some of my apps show 0 purchases even though revenue looks correct?
If you request data across a large number of apps in a single call, the auto-selected events may not include the purchase event you're looking for. Apps where that happens will return 0 purchases, even though their revenue still rolls up into your totals. The request itself succeeds, so nothing in the response flags the gap.
To avoid it, pass the purchase events you care about explicitly using event_ids rather than relying on auto-selection. You can also split very large requests into smaller batches. You can retrieve your available event IDs via GET /events, or reach out to your Account Manager for help.
Why does my Liftoff data differ from my MMP?
Start by making sure you’re comparing the same time window. Call GET /customer to retrieve your billing timezone, set it as the timezone in your report request, and compare spend and install against your MMP data for the same dates. If the numbers still differ, contact your Account Manager for an analysis.
Comments
0 comments
Article is closed for comments.