Skip to main content

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

  1. Contact your AM to get API key and API secret emailed to you.
  2. 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/events
  1. 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=true
  1. Check that it passes without errors.
{ "message": "Validation passed without errors." }
  1. 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/reports
  1. 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"
}
}
  1. 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}/status
{
"id": "abc123",
"created_at": "2020-11-15T00:00:00Z",
"state": "completed",
"parameters": {
"start_time": "2019-01-01",
"end_time": "2019-01-03"
}
}
  1. 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}/data

Authentication

Liftoff’s Reporting API supports HTTP basic authentication.

curl --request GET \
--user 'API_KEY:API_SECRET' \
--url https://data.liftoff.io/api/v1/reports

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:

EndpointsHTTP MethodDescription
/reportsGETLook up metadata for recently submitted reports.
/reportsPOSTGenerate a report.
/reports/{ID}/statusGETGet the status of a report.
/reports/{ID}/dataGETDownload the report in CSV or JSON format.
/{ENTITY_TYPE}GETGet 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
EndpointHTTP MethodHourly Rate Limit
/api/v1/reportsGET60
/api/v1/reportsPOST30
/api/v1/reports?test=truePOST60
/api/v1/reports/{ID}/statusGET1000
/api/v1/reports/{ID}/dataGET30
/api/v1/{ENTITY_TYPE}GET60

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:

NameTypeDescription
error_typestringError type (e.g. "BAD REQUEST").
messagestringGeneral message on possible error cause.
errorsarray 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/reports

Response Format:

NameTypeDescription
idstringThe report ID.
created_attimestamp (string)Timestamp in UTC (e.g. "2021-10-01T00:00:00Z").
parametersobjectThe JSON body that was provided by the POST request.
statestring"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/reports

Request Format:

NameTypeDefaultDescription
app_idsarray of strings (nullable)nullOnly include specified app IDs or all.
campaign_idsarray of strings (nullable)nullOnly include specified campaign IDs or all.
event_idsarray of strings (nullable)nullAll events belonging to the account.
start_timetimestamp (string) Required ISO date and time in UTC or ISO date (with time at start of day for given timezone).
end_timetimestamp (string) Required ISO date and time in UTC or ISO date (with time at start of day for given timezone).
group_byarray of strings["apps", "campaigns"]Group metrics by one of the available presets.
cohort_windowint (1 - 90) (nullable)nullNumber of days since install. If specified, events will be counted since the day of install.
formatstring (nullable)"csv""csv" or "json".
callback_urlstring (nullable)nullA URL that will receive a POST request once the report completed or failed.
timezonestring (nullable)"UTC"A TZ database name to use for date groupings.
include_repeat_eventsboolean (nullable)trueCount all instances of post-install events will be counted, instead of just first occurrences.
remove_zero_rowsboolean (nullable)falseDrop all rows whose numeric metrics are equal to 0.
use_two_letter_countryboolean (nullable)falseUse the two letter country code (as opposed to the default three letter code).
include_skan_installs_with_no_conversion_valueboolean (nullable)falseInclude the number of SKAN installs with no conversion value as an additional metric.
include_skan_installs_with_conversion_valueboolean (nullable)falseInclude the number of SKAN installs with a conversion value as an additional metric.
video_play_milestonesboolean (nullable)falseInclude 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. Only available for reports with a group_by of creative or ad_format.
demand_productstring (nullable)nullReturn 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_productboolean (nullable)falseInclude column for demand product. Only available for reports with a group_by of campaigns.

Available Group By Presets:

  • ["apps", "campaigns"]
  • ["apps", "campaigns", "ad_format"]
  • ["apps", "campaigns", "publisher"]
  • ["apps", "campaigns", "country"]
  • ["apps", "campaigns", "country", "publisher"]
  • ["apps", "campaigns", "creatives"]
  • ["apps", "campaigns", "creatives", "country"]
  • ["apps", "campaigns", "creatives", "publisher"]
  • ["apps", "campaigns", "creatives", "country", "publisher"]

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}/status

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}/data

Report Row Format:

NameTypeDescription
datestringDate (YYYY-MM-dd) for the report timezone.
app_idstringID of the app that is being advertised.
campaign_idstringID of the advertising campaign.
creative_idstringID of the creative.
country_codestring (nullable)2 letter country-code of countries in which the ad is being shown.
publisher_app_store_idstring (nullable)Bundle IDs of the publisher app.
publisher_namestring (nullable)Display name of the publisher app.
ad_formatstring (nullable)Format of the ad unit.
video_startsintNumber of times a video started playing for VAST impressions. Only included if video_play_milestones is set.
video_plays_at_25_percentintNumber of times a video played up to 25% for VAST impressions. Only included if video_play_milestones is set.
video_plays_at_50_percentintNumber of times a video played up to 50% for VAST impressions. Only included if video_play_milestones is set.
video_plays_at_75_percentintNumber of times a video played up to 75% for VAST impressions. Only included if video_play_milestones is set.
video_completesintNumber of times a video finished playing for VAST impressions. Only included if video_play_milestones is set.
spendfloatAmount of budget that was spent in that time period.
impressionsintNumber of ad impressions shown.
clicksintNumber of clicks on the ads.
installsintNumber of installs resulting from the ads.
<event_name>intNumber of events resulting from the ads. Note that an event has to be specified.
cpmfloatCost per thousand impressions. Cost is defined as ad spend.
cpcfloatCost per click. Cost is defined as ad spend.
ctrfloatClick through rate. Cost is defined as ad spend.
cpifloatCost per install. Cost is defined as ad spend.
cpafloatCost per action. Cost is defined as ad spend. Action refers to in-app events.
skan-installs-with-no-conversion-valueint (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-valueint (nullable)Number of SKAN installs with a conversion value. Only included if include_skan_installs_with_conversion_value 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/apps

Response Format:

NameTypeDescription
idstringID of the app that is being advertised.
namestringApp title.
app_store_idstringApple/Play store app ID.
bundle_idstringApp bundle/package ID.
titlestringApp title (as appears in creatives).
platformstring"iOS" or "Android".
optimization_eventobject (nullable)Event selected for ML optimization.
statestring"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/campaigns

Response Format:

NameTypeDescription
idstringID of the advertising campaign.
app_idstringID of the app.
namestringName of the advertising campaign.
campaign_typestring"user-acquisition" or "reengagement".
tracker_typestring"MMP", "SKAN" or "NONE".
min_os_versionstring (nullable)Minimum OS version of the campaign.
max_os_versionstring (nullable)Maximum OS version of the campaign.
statestring"enabled" or "paused".
state_last_changed_attimestamp (string)Timestamp in UTC (e.g. "2021-10-01T00:00:00Z").
demand_productstring"accelerate" or "direct".

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"
}
]

GET /creatives

This endpoint allows you to fetch creative details.

curl --user 'API_KEY:API_SECRET' \
--url https://data.liftoff.io/api/v1/creatives

Response Format:

NameTypeDescription
idstringID of the ad creative.
namestring (nullable)Name of the ad creative.
preview_urlstring (nullable)URL to preview the creative.
full_html_preview_urlstring (nullable)URL of the full HTML preview.
widthint (nullable)Width of the creative (pixels).
heightint (nullable)Height of the creative (pixels).
creative_typestringHow the creative is rendered: "native", "html" or "vast".
created_attimestamp (string)Timestamp in UTC (e.g. "2021-10-01T00:00:00Z").
statestringState of the creative: "enabled", "paused", or "deleted".
video_durationint (nullable)Duration of the creative video in seconds.
video_urlstring (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 --user 'API_KEY:API_SECRET' \
--url https://data.liftoff.io/api/v1/customer

Response Format:

NameTypeDescription
idstringID of the company.
companystringName of the company.
timezonestringCustomer 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/events

Response Format:

NameTypeDescription
idstringID of the in-app event.
app_idstringID of the advertising app.
namestringEvent 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!")