Try it!
Try out the Query API using your Branch data via the API reference here
Overview
Benefits of Query API
The Query API is an HTTP API that can be used for programmatically querying pre-aggregated analytics. It can be used to fetch the same data displayed in your Branch dashboard without accessing the Dashboard itself.
An individual query is constructed from three types of parameters:
- Authentication parameters that control the access to the data
- Data selection keys define which events are eligible to be counted in the results (e.g., filters)
- Result format specifiers that define which results are included in the HTTP response and how the result is returned (e.g., sorting)
Getting Started
For newcomers to this API, we strongly suggest you check out our Query Recipe Book. It has screenshots of Dashboard visualizations, accompanied by what queries you need to make to pull the same data. It's a quick way to get up and running with this API.
API Limitations
Limitation | Details |
---|---|
Rate Limits |
You will get the following response once you've hit the rate limit:
|
Max number of rows returned from API | 50,000 |
Max retrievable unique values for a single dimension (e.g., campaigns, ad names, etc.) | 40,000 |
Max number of days that can be queried at a time | 7 days If more records are required, please make multiple requests with smaller time intervals to pull the necessary data in "batches". |
Export Window | Rolling 2 year window |
Specific Dimension Combinations | Combining the following dimensions together will result in an error being returned when pulling SAN cost, click, and impression data (
|
Prerequisites & API Access
Prerequisites
A user will need to have both Aggregate Data and Export access to access the Query API.
Providing Agencies API Access
If you work with an agency that runs your advertising campaigns and want to give them access to export the subsequent data, you can provide them with access to the Export API.
To provide an agency team member with access to the Export API:
- In the left-hand navigation, under Setup & Testing, click on Account Settings.
- On the Account Settings page, click on the Agencies tab.
- Expand the agency in question, find the agency team member you want to give access to, hover on the button in the Actions column, and click Edit.
- In the Edit Agency Team Member modal:
- Under Access Level, check the Export box.
- Under Permissions, check the Aggregate Data box.
- Optional: add data filters
- Under Data Filters, toggle any necessary data filters on/blue. Exported data will be filtered accordingly.
- Click Save.

Agency-Tagged Data
If you do not enable the Only Show Agency-Tagged Data data filter, the Agency Team Member will be able to export aggregate data associated with all of your campaigns, regardless if they are associated with them or not.
Authentication
Calls to the Query API require a branch_key and branch_secret String parameter to be passed with each request.
API Usage
Query Request
POST /v1/query/analytics
Content-Type: application/json
Host: api2.branch.io
Request Headers:
Header | Description | Required |
---|---|---|
Access-Token | Key that encapsulates the user's permission w.r.t an org. Obtained from the Branch Dashboard needed for authentication. | Yes |
Content-Type | application/json | Yes |
Request Query Parameters:
Parameter | Type | Description | Required |
---|---|---|---|
branch_key | String | The Branch key of the app analytics information is being pulled for. | Yes |
branch_secret | String | The Branch secret of the app used for authentication. | Yes |
limit | Integer | The maximum number of results to return in the response. If the granularity is set to day, Branch will pull results up to the limit for each day. So if the limit is set to 1000 and 5 days' worth of data is queried, with granularity=day, then this API will return up to 5000 results.
| No |
after | Integer | A pagination parameter indicates the index of the first result to return in the response. E.g., with 100 results returned, setting "after" to 50 would return elements 51-100.
| No |
query_id | String | Returned as a query parameter on the "paging" object nexturl and previous_url. Locks the last event to count for a query, so new events that occur between queries are not added to the results (prevents count change over time).
Note: The query_id should be treated as temporary and should only be used when retrieving pages of an existing query where the pagination URLs already have query_id set as a query parameter. You should not attempt to change the id between requests or include a query_id with a different query request. | No |
Request Body Parameters:
Parameter | Type | Description | Required |
---|---|---|---|
start_date | Date | A timestamp representing the oldest date to return data for. Timezone of the timestamp is set in your Branch Dashboard configuration. | Yes |
end_date | Date | The last timestamp (exclusive) to return data for. No events that triggered after the enddate will be counted in the query results. Timezone of the timestamp is set in your Branch Dashboard configuration.
| Yes |
data_source | String | The type of event to query for, prefixed with the source (eg 'eo' + 'open' pulls Branch app opens). Valid Branch Data Source Values:
| Yes |
aggregation | String | How to count events towards the final result count. When using unique_count, each event is only counted if an event by that user has not already been seen. E.g., if 10 users each trigger 3 opens, only 10 open events will be counted. Possible Values:
| Yes |
dimensions | Array of Strings | List of event fields to use as splits for the query. Results counts are returned and grouped with other events with matching values for each key provided in "dimensions". See Dimensions for a complete list. | Yes |
filters | Array of Strings | An object where each key is a valid "dimension", and each value is an array of string values. If a key is prefixed with a '!', then any event with a dimension value contained in the value of that key is excluded. Otherwise, only events with dimension values matching the filter will be counted. Example:
this would count only events where:
Possible Keys: See Dimensions for valid key values. Any key may also be used with a "!" prefix | No |
enable_install_recalculation | Boolean | De-dupe unattributed installs caused by duplicate events from non-opt-in users coming from paid ads (result from iOS 14.5 privacy changes). | No |
granularity | String | Range of time to roll multiple events into a single result count. E.g., with a value of "day" the counts for each day are returned independently, whereas "all" would return a single count for the entire time range. Possible Values:
Default Values: "all" Note: When you set "all" for this key, Branch will return the data grouped by "start_date". The "start_date" shown is for the start of the range, and it does not mean that data is limited to just that day. | No |
ordered_by | String | The result key to sort results by. Only 1 sort key is supported. Possible Values: Any element of query "dimensions" or the value of "aggregation" in the query. Note: Sorts and the ordered_by parameter: It is not possible to provide an explicit sort method to the query, so the sort type is chosen automatically based on the value of "ordered_by". Behavior for comparison of equal values is left undefined, and as such, the sort is not considered order stable for identical values. Values for "ordered_by" Sort: Numerically Sorted:
Chronologically Sorted:
Lexicographically Sorted:
| No |
ordered | String | The direction to order the results. Possible Values:
Default Value: "descending" | No |
zero_fill | Boolean | Whether to return result objects where the result count was 0.
See Example Queries for an example of how this flag works. | No |
Response Body Parameters:
Parameter | Type | Description |
---|---|---|
results | Array of Strings | Each result object contains a result and a timestamp. The result will contain the queried values, e.g., "attributed" and "total_count"json { "results": [{ "result": { "attributed": "false", "total_count": 44 }, "timestamp": "2022-03-05T00:00:00.000Z" }, { "result": { "attributed": "false", "total_count": 23 }, "timestamp": "2022-03-07T00:00:00.000Z" }, { "result": { "attributed": "true", "total_count": 14 }, "timestamp": "2022-03-05T00:00:00.000Z" }] } |
timestamp | Date | Date and time when the data was created.json "timestamp": "2022-03-05T00:00:00.000Z" |
paging | Integer | The total number of results and the number that the results are limited to (if included in the request). See limit and after in the Request Query Parameters for further information.json "paging": { "next_url": "/v1/query/analytics?limit=5&after=5", "total_count": 13 } |
Example - Total Installs Request/Response:
A basic query for pulling total installs per day, split by attributed vs not, limited to 5 results:
curl -X POST -H "Content-Type: application/json" -d '{
"branch_key":"<YOUR_BRANCH_KEY>",
"branch_secret":"<YOUR_BRANCH_SECRET>",
"start_date": "2022-03-01",
"end_date": "2022-03-07",
"data_source": "eo_install",
"dimensions": [
"attributed"
],
"enable_install_recalculation": true,
"granularity": "day",
"aggregation": "total_count"
}' "https://api2.branch.io/v1/query/analytics?limit=5"
{
"results": [{
"result": {
"attributed": "false",
"total_count": 44
},
"timestamp": "2022-03-05T00:00:00.000Z"
}, {
"result": {
"attributed": "false",
"total_count": 23
},
"timestamp": "2022-03-07T00:00:00.000Z"
}, {
"result": {
"attributed": "true",
"total_count": 14
},
"timestamp": "2022-03-05T00:00:00.000Z"
}, {
"result": {
"attributed": "false",
"total_count": 12
},
"timestamp": "2022-03-03T00:00:00.000Z"
}, {
"result": {
"attributed": "false",
"total_count": 10
},
"timestamp": "2022-03-02T00:00:00.000Z"
}],
"paging": {
"next_url": "/v1/query/analytics?limit=5&after=5",
"total_count": 13
}
}
See Example Queries for more examples.
Appendix
Dimensions
General Information
Dimension | Description |
---|---|
"name" | Name. |
"origin" | Origin of the data. |
"timestamp" | Timestamp of data. |
"deep_linked" | Is the data deep linked? True/False. |
"from_desktop" | Is data from Desktop? True/False. |
"attributed" | Is data attributed? True/False. |
User Information
Dimension | Description |
---|---|
"user_data_app_store" | User's App Store. Not applicable to the following data source options: "eo_impression", "eo_click", "xx_impression", "xx_click", "eo_branch_cta_view", "eo_sms_sent", "eo_web_session_start", "eo_pageview", "eo_dismissal", and "cost". |
"user_data_os" | User's Operating System. |
"user_data_language" | User's Language. |
"user_data_platform" | User's Platform. |
"user_data_environment" | User's Environment. |
"user_data_geo_dma_code" | User's GEO Designated Market Area Code. |
"user_data_geo_country_code" | User's Country Code. |
"user_data_country" | User's Country. |
"user_data_geo_region_en" | User's Region. |
"user_data_opted_in" | Is User Opted In? |
"user_data_opted_in_status" | User Opted In Status. |
Custom Event Information
Dimension | Description |
---|---|
"event_data_custom_param_1" "event_data_custom_param_2" "event_data_custom_param_3" | Available dimensions for custom event data. Not applicable to the following data source options: "eo_impression", "eo_click", "xx_impression", "xx_click", "eo_branch_cta_view", "eo_sms_sent", "eo_pageview", "eo_dismissal", and "cost". |
Click/Impression Source Definitions
Data Source | Description |
---|---|
"eo_impression" | Real-Time user-device level impressions/ad-views triggered via Impression Tracking Ad Links. SAN Ad Partner's impressions are not included in this data source. |
"eo_click" | Real-Time user-device level Link clicks are recorded through User Clicks on the Branch Links. SAN Ad Partner's clicks are not included in this bucket. |
"xx_impression" | Combined Aggregated Data Source containing both Real-Time user-device level impressions and the SAN Ad Partner's Impressions passed to Branch Systems from SAN servers. |
"xx_click" | Combined Aggregated Data source containing both Real-Time user-device level Clicks and the SAN Ad Partner's Clicks passed to Branch systems from SAN servers. |
Example Queries
Installs Per Day Per OS
A basic query for pulling installs per day split by OS of the device the user installed on, limited to 5 results:
curl -X POST -H "Content-Type: application/json" -d '{
"branch_key":"<YOUR_BRANCH_KEY>",
"branch_secret":"<YOUR_BRANCH_SECRET>",
"start_date": "2017-12-12",
"end_date": "2017-12-18",
"data_source": "eo_install",
"dimensions": [
"user_data_os"
],
"enable_install_recalculation": true,
"granularity": "day",
"aggregation": "total_count"
}' "https://api2.branch.io/v1/query/analytics?limit=5"
{
"results": [
{
"result": {
"user_data_os": "ANDROID",
"total_count": 144
},
"timestamp": "2017-12-18T00:00:00.000Z"
},
{
"result": {
"user_data_os": "IOS",
"total_count": 142
},
"timestamp": "2017-12-18T00:00:00.000Z"
},
{
"result": {
"user_data_os": "IOS",
"total_count": 191
},
"timestamp": "2017-12-17T00:00:00.000Z"
},
{
"result": {
"user_data_os": "ANDROID",
"total_count": 194
},
"timestamp": "2017-12-17T00:00:00.000Z"
},
{
"result": {
"user_data_os": "ANDROID",
"total_count": 246
},
"timestamp": "2017-12-16T00:00:00.000Z"
}
],
"paging": {
"next_url": "/v1/query/analytics?query_id=CqdBOb&limit=5&after=5",
"total_count": 14
}
}
Unique Click Counts Per Channel/Campaign/Feature
A more complex query for pulling unique click counts split by the last touch channel, campaign, feature, and the +via_current_features values.
It has a filter specified to filter out any clicks where last_attributed_touch_data_plus_current_feature was MOBILE_DEEPVIEWS or DESKTOP_DEEPVIEWS.
A maximum of 5 results should be returned, in descending order of unique_count, with days that had 0 clicks returned (not filtered out):
curl -X POST -H "Content-Type: application/json" -d '{
"branch_key":"<YOUR_BRANCH_KEY>",
"branch_secret":"<YOUR_BRANCH_SECRET>",
"start_date": "2017-12-12",
"end_date": "2017-12-18",
"data_source": "eo_click",
"dimensions": [
"last_attributed_touch_data_tilde_feature",
"last_attributed_touch_data_tilde_channel",
"last_attributed_touch_data_tilde_campaign",
"last_attributed_touch_data_plus_current_feature"
],
"filters": {
"!last_attributed_touch_data_plus_current_feature": [
"MOBILE_DEEPVIEWS",
"DESKTOP_DEEPVIEWS"
]
},
"ordered": "descending",
"ordered_by": "unique_count",
"aggregation": "unique_count",
"zero_fill": true
}' "https://api2.branch.io/v1/query/analytics?limit=5"
{
"results": [
{
"timestamp": "2017-12-12T00:00:00.000Z",
"result": {
"last_attributed_touch_data_tilde_channel": "ads",
"last_attributed_touch_data_tilde_campaign": "Xmas",
"last_attributed_touch_data_tilde_feature": "paid advertising",
"last_attributed_touch_data_plus_current_feature": "ADS",
"unique_count": 750
}
},
{
"timestamp": "2017-12-12T00:00:00.000Z",
"result": {
"last_attributed_touch_data_tilde_channel": "taptica#1",
"last_attributed_touch_data_tilde_campaign": "taptica#1",
"last_attributed_touch_data_tilde_feature": "paid advertising",
"last_attributed_touch_data_plus_current_feature": "ADS",
"unique_count": 723
}
},
{
"timestamp": "2017-12-12T00:00:00.000Z",
"result": {
"last_attributed_touch_data_tilde_channel": "Journeys",
"last_attributed_touch_data_tilde_campaign": "Default Banner",
"last_attributed_touch_data_tilde_feature": "journeys",
"last_attributed_touch_data_plus_current_feature": "MOBILE_JOURNEYS",
"unique_count": 553
}
},
{
"timestamp": "2017-12-12T00:00:00.000Z",
"result": {
"last_attributed_touch_data_tilde_channel": "Apple App Store",
"last_attributed_touch_data_tilde_campaign": null,
"last_attributed_touch_data_tilde_feature": "paid advertising",
"last_attributed_touch_data_plus_current_feature": "ADS",
"unique_count": 432
}
},
{
"timestamp": "2017-12-12T00:00:00.000Z",
"result": {
"last_attributed_touch_data_tilde_channel": null,
"last_attributed_touch_data_tilde_campaign": null,
"last_attributed_touch_data_tilde_feature": "marketing",
"last_attributed_touch_data_plus_current_feature": "QUICK_LINKS",
"unique_count": 201
}
}
],
"paging": {
"next_url": "/v1/query/analytics?query_id=EDdBOb&limit=5&after=5",
"total_count": 143
}
}
Include or Omit Response Objects Using zero_fill
zero_fill
When zero_fill
is set to true
, response objects with a total_count
equal to 0 will be returned alongside other results.
{
"branch_key":"<YOUR BRANCH KEY>",
"branch_secret":"<YOUR BRANCH SECRET>",
"start_date": "2022-02-01",
"end_date": "2022-02-01",
"data_source": "xx_click",
"dimensions": [
"last_attributed_touch_data_tilde_advertising_partner_name",
"last_attributed_touch_data_tilde_campaign_id"
],
"ordered": "descending",
"aggregation": "total_count",
"zero_fill": true
}
{
"result": {
"last_attributed_touch_data_tilde_advertising_partner_name": "Google AdWords",
"last_attributed_touch_data_tilde_campaign_id": "78726498102",
"total_count": 1
},
"timestamp": "2022-02-01T00:00:00.000+08:00"
},
{
"result": {
"last_attributed_touch_data_tilde_advertising_partner_name": "Google AdWords",
"last_attributed_touch_data_tilde_campaign_id": "87192837612",
"total_count": 1
},
"timestamp": "2022-02-01T00:00:00.000+08:00"
}
{
"result": {
"last_attributed_touch_data_tilde_advertising_partner_name": "Google AdWords",
"last_attributed_touch_data_tilde_campaign_id": "17384628491",
"total_count": 0
},
"timestamp": "2022-02-01T00:00:00.000+08:00"
}
When zero_fill
is set to false
, response objects with a total_count
equal to 0 are omitted.
{
"branch_key":"<YOUR BRANCH KEY>",
"branch_secret":"<YOUR BRANCH SECRET>",
"start_date": "2022-02-01",
"end_date": "2022-02-01",
"data_source": "xx_click",
"dimensions": [
"last_attributed_touch_data_tilde_advertising_partner_name",
"last_attributed_touch_data_tilde_campaign_id"
],
"ordered": "descending",
"aggregation": "total_count",
"zero_fill": false
}
{
"result": {
"last_attributed_touch_data_tilde_advertising_partner_name": "Google AdWords",
"last_attributed_touch_data_tilde_campaign_id": "78726498102",
"total_count": 1
},
"timestamp": "2022-02-01T00:00:00.000+08:00"
},
{
"result": {
"last_attributed_touch_data_tilde_advertising_partner_name": "Google AdWords",
"last_attributed_touch_data_tilde_campaign_id": "87192837612",
"total_count": 1
},
"timestamp": "2022-02-01T00:00:00.000+08:00"
}
Switching Data Sources
You can switch data sources using
"xx_click"
to include SAN Ad Partner's Click Data as well as Real-Time user-device level Clicks.SAN Ad Partners are:
- Google Ads
- Snap
- Apple Search Ads
- TikTok