Filters

👍

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

  • 5 requests per second
  • 20 requests per minute
  • 150 requests per hour

You will get the following response once you've hit the rate limit:

{   "errors":[      {         "message":"Limit is exceeded for org-525983469347282987,              retry after x seconds",         "error_code":7      }   ]}

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 (data_source = cost, xx_click, or xx_impression):

  • user_data_os + user_data_geo_country_code
  • last_attributed_touch_data_tilde_secondary_publisher + user_data_geo_country_code

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:

  1. In the left-hand navigation, under Setup & Testing, click on Account Settings.
  2. On the Account Settings page, click on the Agencies tab.
  3. 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.
  4. In the Edit Agency Team Member modal:
    1. Under Access Level, check the Export box.
    2. Under Permissions, check the Aggregate Data box.
  5. Optional: add data filters
    1. Under Data Filters, toggle any necessary data filters on/blue. Exported data will be filtered accordingly.
  6. Click Save.
1246

🚧

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:

HeaderDescriptionRequired
Access-TokenKey that encapsulates the user's permission w.r.t an org. Obtained from the Branch Dashboard needed for authentication.Yes
Content-Typeapplication/jsonYes

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.

  • Default Value: 100
  • Max Value: 1000

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.

  • Default Value: 0

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).

  • Default Value: null

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.

  • Cannot be more than 7 days after the startdate
  • Format: An ISO-8601 compliant date-time string. Eg: "2020-01-20"

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:

  • "eo_impression"
  • "eo_click"
  • "xx_impression"
  • "xx_click"
  • "eo_branch_cta_view"
  • "eo_sms_sent"
  • "eo_open"
  • "eo_install"
  • "eo_reinstall"
  • "eo_web_session_start"
  • "eo_pageview"
  • "eo_commerce_event"
  • "eo_custom_event"
  • "eo_content_event"
  • "eo_dismissal"
  • "eo_user_lifecycle_event"
  • "cost"

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:

  • "unique_count"
  • "total_count"
  • "revenue"
  • "cost"
  • "cost_in_local_currency"

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:

{  "filters": {    "last_attributed_touch_data_plus_current_feature": [      "MOBILE_DEEPVIEWS",      "DESKTOP_DEEPVIEWS"    ],    "!user_data_os": [ "iOS" ]  }}

this would count only events where:

  • lastattributed_touch_data_plus_current_feature was equal to "MOBILE_DEEPVIEWS" or "DESKTOP_DEEPVIEWS"
    and
  • userdata_os was not equal to "iOS"

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:

  • "all"
  • "day"

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:

  • "unique_count"
  • "total_count
  • "revenue"

Chronologically Sorted:

  • "timestamp"

Lexicographically Sorted:

  • everything else

No

ordered

String

The direction to order the results.

Possible Values:

  • "ascending"
  • "descending"

Default Value: "descending"

No

zero_fill

Boolean

Whether to return result objects where the result count was 0.

  • If set to false, results with count = 0 will be omitted from the response.
  • If result is empty and zerofill=true, fields will be loaded with null/0 to provide a schema.

See Example Queries for an example of how this flag works.

No

Response Body Parameters:

ParameterTypeDescription
resultsArray of StringsEach 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" }] }
timestampDateDate and time when the data was created.json "timestamp": "2022-03-05T00:00:00.000Z"
pagingIntegerThe 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

DimensionDescription
"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

DimensionDescription
"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

DimensionDescription
"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 SourceDescription
"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

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:

  • Facebook
  • Google Ads
  • Twitter
  • Snap
  • Apple Search Ads
  • TikTok