Reporting API
Liftoff’s Reporting API enables automated access to campaign reports with groupings and metrics of your choice. 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 the following day. For example, to retrieve a complete report for 9/12/2024, please request on 9/13/2024 at 5am or later.
Getting Started
- Contact your AM to get API key and API secret emailed to you.
- 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
- Python
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/events
requests.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=truequery parameter. This ensures that you don’t run into rate limits while testing.
- cURL
- Python
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=true
requests.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
testquery parameter.
- cURL
- Python
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
requests.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}/statusendpoint once every minute.
- cURL
- Python
curl --user 'API_KEY:API_SECRET' \
--url https://data.liftoff.io/api/v1/reports/{ID}/status
requests.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
- Python
curl --user 'API_KEY:API_SECRET' \
--url https://data.liftoff.io/api/v1/reports/{ID}/data
requests.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
- Python
curl --request GET \
--user 'API_KEY:API_SECRET' \
--url https://data.liftoff.io/api/v1/reports
requests.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:
| Endpoints | 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-maxx-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 |
| /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
- Python
curl --user 'API_KEY:API_SECRET' --url https://data.liftoff.io/api/v1/reports
requests.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
- Python
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/reports
requests.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. |
| include_repeat_events | boolean (nullable) | true | Count 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", or "direct". 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). |
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"]["apps" "campaigns" "video_placement"]["apps" "campaigns" "creatives" "video_placement"]["apps" "campaigns" "publisher" "video_placement"]["apps" "campaigns" "creatives" "publisher" "video_placement"]
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
- Python
curl --user 'API_KEY:API_SECRET' https://data.liftoff.io/api/v1/reports/{ID}/status
requests.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
- Python
curl --user 'API_KEY:API_SECRET' \
--url https://data.liftoff.io/api/v1/reports/{ID}/data
requests.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_placement | string (nullable) | OpenRTB Video.placement of the impression (e.g. in-stream, in-feed, in-banner, interstitial, unknown-placement). Empty for non-video impressions or when the exchange did not populate the field. Only included when grouping by video_placement. |
| 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. |
| revenuefrom<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
- Python
curl --user 'API_KEY:API_SECRET' \
--url https://data.liftoff.io/api/v1/apps
requests.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
- Python
curl --user 'API_KEY:API_SECRET' \
--url https://data.liftoff.io/api/v1/campaigns
requests.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" or "direct". |
| 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
- Python
curl --user 'API_KEY:API_SECRET' \
--url https://data.liftoff.io/api/v1/creatives
requests.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,
"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
- Python
curl --user 'API_KEY:API_SECRET' \
--url https://data.liftoff.io/api/v1/customer
requests.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
- Python
curl --user 'API_KEY:API_SECRET' \
--url https://data.liftoff.io/api/v1/events
requests.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 - enable 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 yourgroup_byto include at least one ofcreatives,ad_format, orcountry— cannot be combined withpublisheroris_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: truealongside yourcohort_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.
Most Frequent Use Case References
| Use Case Inquiry | Supported by the Reporting API? | How To Query |
|---|---|---|
| Which creative has the highest spend this month? | Yes | group_by: apps + campaigns + creatives, filter by date range, sort by spend in your BI |
| What is my D7 ROAS by country? | Yes | group_by: apps + campaigns + country, cohort_window: 7, include_customer_revenue: true. Divide revenue by spend. |
| Which publisher apps 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. |
| What's the video completion rate per creative? | Yes | group_by must include creatives (not publisher or is_interstitial), set video_play_milestones: true |
| What's my D30 ROAS by campaign? | Yes | cohort_window: 30, include_customer_revenue: true, group_by: apps + campaigns |
| Which ad format performs best? | 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. |
| Why does my Liftoff data differ from my MMP? | Partial | Pull spend + installs from the API; compare using the same timezone (GET /customer). Contact your AM for an analysis. |
| Hourly spend breakdown within a day | Not available | API granularity is daily only. Contact your AM for intra-day data requests. |
| Age / gender distribution | Not available | Not supported in the API. Contact your AM for additional reporting. |