Table of Contents
Introduction
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.
Getting started
- Contact your CSM to get API username and password 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 --user 'username' 'https://data.liftoff.io/api/v1/apps'
curl --user 'username' 'https://data.liftoff.io/api/v1/creatives'
curl --user 'username' 'https://data.liftoff.io/api/v1/campaigns'
curl --user 'username' 'https://data.liftoff.io/api/v1/events' - Create a report request and validate it with a ?test=true 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-01T00:00:00Z","end_time":"2019-01-03T00:00:00Z"}' \
--user 'username' https://data.liftoff.io/api/v1/reports?test=true - Check that it passes without errors
{"message":"Validation passed without errors."}
- Generate the report without the test parameter
curl --header "Content-Type: application/json" \
--request POST \
--data
'{"start_time":"2019-01-01T00:00:00Z","end_time":"2019-01-03T00:00:00Z"}' \
--user 'username' https://data.liftoff.io/api/v1/reports - The report response will contain a report id if it was successfully created
{
"id": "abc123",
"created_at": "2019-04-08T21:54:35Z",
"state": "queued",
"parameters": {
"start_time": "2019-01-01T00:00:00Z",
"end_time": "2019-01-03T00:00:00Z"}} - 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 /status end point with an interval of 1 per 10 min.
curl --user 'username'
'https://data.liftoff.io/api/v1/reports/abc123/status'{
"id": "abc123",
"created_at": "2019-04-08T21:54:35Z",
"state": "queued",
"parameters": {
"start_time": "2019-01-01T00:00:00Z",
"end_time": "2019-01-03T00:00:00Z"}} - When the report is generated, its state will be marked as “completed”, and the report data
can be obtained at the following:curl --user 'username' 'https://data.liftoff.io/api/v1/reports/abc123/data'
Authentication
Liftoff’s reporting API supports HTTP Basic authentication. Here’s a quick example curl:
curl --request GET \
--url 'https://data.liftoff.io/api/v1/reports' \
--user 'username:password'
API structure
Base url
All API URLs start with https://data.liftoff.io/api/v1. Different end-points can be called by appending to the base url. All available end points are described in the table below:
End points | HTTP Request Method | Usage Description |
---|---|---|
/reports | GET | Lookup metadata around recently submitted reports |
POST | Generate a report | |
/reports/<id>/status | GET | Get the status of the report. |
/reports/<id>/data | GET | Download the report in CSV or JSON |
/<entity_type> Entity type = apps, campaigns, creatives, events | GET | Get the descriptions around relevant entities (apps, campaigns, creatives, events) |
Throttling
All endpoints are rate-limited:
- /api/v1/reports (GET): 60/hour
- /api/v1/reports (POST): 30/hour
- /api/v1/reports?test=true (POST): 60/hour
- /api/v1/reports/<id>/status: 1000/hour
- /api/v1/reports/<id>/data: 10/hour
- /api/v1/<entity_type> (grouped together): 60/hour
- Not found endpoints (404): ignored
Note: responses to any rate-limited endpoints will have x-rate-limit-max and x-rate-limit-remaining
headers
Endpoints
/reports
GET
- Use case: look up metadata around recently submitted reports.
- Response: Returns a list of latest 100 report ids along with the following metadata:
Name | Type | Description |
---|---|---|
id | String | ID of the report |
created_at | Timestamp (string) | UTC timestamp. |
parameters | Report Parameters (object) | JSON body posted during initial request populated with defaults. |
state | String | Queued, cancelled, completed, failed. |
Sample curl
curl --user 'username' 'https://data.liftoff.io/api/v1/reports'
Sample response header
…
content-type: application/json;charset=utf-8
x-rate-limit-max: 60
x-rate-limit-remaining: 52
Sample response body
[{
"id": "abc123",
"created_at": "2019-02-08T21:54:35Z,
"state": "completed",
"parameters": {
"app_ids": ["fgh456","xyz789"],
"start_time": "2019-02-01T21:54:35Z",
"end_time": "2019-02-02T21:54:35Z"}},
...]
POST
- Content Type: application/json
- Query Parameters:
- test (boolean): 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 then the requests without this query parameter
- Body:
Name | Type | Description | When value is not specified |
---|---|---|---|
app_ids | Array of strings (nullable) | Filters the report to only app external_ids provided | All app_ids belonging to the account |
campaign_ids | Array of strings (nullable) | Filters the report to only campaign external_ids provided. | All campaign_ids belonging to the account |
event_ids | Array of Strings (nullable) | All events belonging to the account | Filters for the app event selected for optimization |
start_time | Timestamp (string) | ISO date and time in UTC, or ISO date (with time at start of day for given timezone). | Throws an exception (400), since this is a required parameter |
end_time | Timestamp (string) | ISO date and time in UTC, or ISO date (with time at start of day for given timezone). | Throws an exception (400), since this is a required parameter |
group_by | Array of preset string combinations | Group metrics by the following combinations of entities:
| [apps, campaigns] |
cohort_window | Integer (1 - 90) (nullable) | Number of days since install. If this is specified, events will be counted since the day of install | Null |
format | String (nullable) | Output format of the report. Either CSV or JSON | CSV |
callback_url | String (nullable) | Specify an endpoint to receive a status message of the report | Null |
timezone | String (nullable) | Specify a TZ database name to use for date groupings. | UTC |
include_repeat_events | Boolean (nullable) | When enabled, all instances of post-install events will be counted, instead of just first occurrences. | true |
remove_zero_rows | Boolean (nullable) | When enabled, all rows whose numeric metrics are equal to 0 will be excluded from output. | false |
use_two_letter_country | Boolean (nullable) | When enabled, reports will use the two letter country code (as opposed to the default three letter code). | false |
Sample request body
{
"app_ids": ["xyz123","abc123"],
"campaign_ids": ["jkl123"],
"cohort_window": 1,
"group_by": ["apps","campaigns", "creatives"]
}
- Response:
Name | Type | Description |
---|---|---|
id | String | ID of the report |
created_at | Timestamp (string) | UTC timestamp of report generation |
parameters | Report Parameters (object) | JSON body posted during initial request |
state | String | queued, cancelled, completed, failed |
Sample curl
curl --header "Content-Type: application/json" \
--request POST \
--data
'{"start_time":"2019-01-01T00:00:00Z","end_time":"2019-01-03T00:00:00Z"}' \
--user 'username' https://data.liftoff.io/api/v1/reports
Sample response: header
…
content-type: application/json;charset=utf-8
x-rate-limit-max: 5
x-rate-limit-remaining: 0
Sample response: body
{
"id": "abc123",
"created_at": "2019-02-08T21:54:35Z",
"state": "queued",
"parameters": {
"app_ids": ["xyz123","abc123"],
"campaign_ids": ["jkl123"],
"cohort_by": "install",
"cohort_window": 1,
"group_by": ["apps","campaigns", "creatives"],
"start_time": "2019-02-01T21:54:35Z",
"end_time": "2019-02-02T21:54:35Z"
}
}
- Exception (Bad Request):
- 400 bad request (https required)
- 403 unauthorized (bad credentials)
- 429 rate limit exceeded
- 500 internal error
Name | Type | Description |
---|---|---|
error_type | string | Error type (i.e. “BAD REQUEST”) |
message | string | General message on possible error cause. |
errors | Array of Strings (Nullable) | List of issues with request |
Sample error: body
{
"error_type": "NOT FOUND",
"message": "Please correct the following issues with the request.",
"errors": [
"start_time should be before end_time.",
"Max allowable date range is one year."
],
}
/reports/<id>/status
This endpoint allows you to get the status of the report.
- Request Method: GET
- Content Type: application/json
- Similar to /reports, but returns a single report’s status and metadata but with a higher rate limit.
Sample curl
curl --user 'username'
'https://data.liftoff.io/api/v1/reports/abc123/status'
Sample response
{
"id": "abc123",
"created_at": "2019-02-08T21:54:35Z",
"state": "failed",
}
/reports/<id>/data
This endpoint allows you to download the report
- Request Method: GET
- Content Type: application/json or text/csv
- Response:
Name | Type | Description |
---|---|---|
date | Date | yyyy-MM-dd (in UTC 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. Only available if group by (apps, campaigns, country) is selected |
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 |
spend | Float | Amount of budget that was spent in that time period |
impressions | Integer | Ad impressions shown |
clicks | Integer | Number of clicks on the ads |
installs | Integer | Number of installs resulting from the ads |
<event_name> | Integer | 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. |
Sample response (JSON)
[
"columns": ["date","app_id","campaign_id","spend","impressions", ...],
"rows": [
["2019-02-08","abc123","zxy123",123.12,12, ...],
...
]
]
Sample response (CSV)
date, app_id, campaign_id, spend, impressions, clicks, installs, cpm, cpc, ctr, cpi, cpa, Tutorial (event), Login (event)
2019-02-08, abc123, zxy123, 123.12, 123, 12, 2, 0.2, 0.2, 0.2, 0.2, 0.2, 12, 12
- Exception (Bad Request):
- 400 bad request (https required)
- 403 unauthorized (bad credentials)
- 429 rate limit exceeded
- 500 internal error
Name | Type | Description |
---|---|---|
error_type | string | Error type (i.e. “BAD REQUEST”) |
message | string | General message on possible error cause. |
Sample error (body)
{
"error_type": "NOT FOUND",
"message": "No app found with id abc123."
}
/<entity_type>
This endpoint allows you to query the metadata around relevant entities (apps, campaigns,
creatives, events)
- Request Method: GET
- Content Type: application/json
- Response: Supported entity types are apps, events, campaigns, or creatives. Returns list of entities belonging to that type.
Entity Models
Apps
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 | Event (nullable) | Event selected for ML optimization |
Sample curl
curl --user 'username' 'https://data.liftoff.io/api/v1/apps'
Sample response body
[
{
"id": "abc123",
"name": "Sample",
"app_store_id": "io.liftoff.sample",
"bundle_id": "io.liftoff.sample",
"title": "Sample",
"platform": "Android",
"optimization_event": {
"id": "abc123",
"name": "Tutorial"
}
},
...
]
Campaigns
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 | Type of campaign (UA: User acquisition, RE: Re-engagement campaigns) |
Sample curl
curl --user 'username' 'https://data.liftoff.io/api/v1/campaigns'
Sample response body
[
{
"id": "abc123",
"app_id": "fgh456",
"name": "App - iOS - JP",
"campaign_type": "reengagement"
},
...
]
Creatives
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 |
width | Integer (nullable) | Width of the creative, in pixels |
height | Integer (nullable) | Height of the creative, in pixels |
creative_type | String | The technology used to render the creative: native, native_video, html, vast |
Sample curl
curl --user 'username' 'https://data.liftoff.io/api/v1/creatives'
Sample response body
[
{
"id": "abc123",
"name": "320x480_sample_html",
"width": 480,
"height": 320,
"preview_url": "https://...",
"creative_type": "html"
},
...
]
Events
Name | Type | Description |
---|---|---|
id | String | ID of the in-app event |
app_id | String | ID of the advertising app |
name | String | Name of the event |
Sample curl
curl --user 'username' 'https://data.liftoff.io/api/v1/events'
Sample response body
[
{
"id": "abc123",
"app_id": "fgh456",
"name": "Tutorial"
},
...
]