Migration Guide

This guide provides guidelines to migrate the Core Reporting API v3 to the Analytics Reporting API v4.

Introduction

To take advantage of the new features introduced in the Analytics Reporting API v4, migrate your code to use the API. This guide shows requests in the Core Reporting API v3 and the equivalent requests in the Analytics Reporting API v4 to make your migration easier.

Python migration

If you are a Python developer, use the GAV4 helper library on GitHub to convert Google Analytics Core Reporting API v3 requests to Analytics Reporting API v4 requests

Endpoints

The Core Reporting API v3 and the Analytics Reporting API v4 have different endpoints and HTTP methods:

v3 Endpoint

GET https://www.googleapis.com/analytics/v3/data/ga

v4 Endpoint

POST https://analyticsreporting.googleapis.com/v4/reports:batchGet

The following examples compare a request in v3 and the equivalent request in v4:

v3

v3 reference documentation

GET https://www.googleapis.com/analytics/v3/data/ga?ids=ga:XXXX \
    &start-date=2015-11-01&end-date=2015-11-06 \
    &metrics=ga:users&dimensions=ga:pagePath

v4

v4 reference documentation

POST https://analyticsreporting.googleapis.com/v4/reports:batchGet
{
  "reportRequests":[
  {
    "viewId":"XXXX",
    "dateRanges":[
    {
      "startDate":"2015-11-01",
      "endDate":"2015-11-06"
    }],
    "metrics":[
    {
      "expression":"ga:users"
    }],
    "dimensions": [
    {
      "name":"ga:pagePath"
    }]
  }]
}

Client Libraries and Discovery Service

If you use the Python, JavaScript, or other client library that relies on Google Discovery Service, you need to provide the location of the discovery document for the Reporting API v4.

Python

from apiclient import discovery

...

# Build the Analytics Reporting API v4 authorized service object.
analyticsReporting = discovery.build(
  'analyticsreporting',
  'v4',
  http=http,
  discoveryServiceUrl='https://analyticsreporting.googleapis.com/$discovery/rest')

JavaScript

gapi.client.load(
  'https://analyticsreporting.googleapis.com/$discovery/rest',
  'v4'
).then(...)

The Java and PHP client libraries are pre-built, but you can use the discovery service and the Google APIs generator to generate them.

Requests

The API v4 reference describes in detail the structure of the request body. The following sections describe the migration of v3 request parameters to v4 request parameters.

View IDs

In v3, an ids paramter, which accepts a "table ID", is in the format of ga:XXXX, where XXXX is the view (profile) ID. In v4, a view (profile) ID is specified in the viewId field in the request body.

The following examples compare the ids parameter in a v3 request and the viewId field in a v4 request:

v3

GET https://www.googleapis.com/analytics/v3/data/ga?ids=ga:XXXX

v4

POST https://analyticsreporting.googleapis.com/v4/reports:batchGet
{
  "reportRequests":[
  {
    "viewId":"XXXX",
    ...
  }]
}

Date Ranges

The Analytics Reporting API v4 allows you to specify multiple date ranges in a single request. The dateRanges field takes a list of DateRange objects. In v3, you use the start-date and end-date parameters to specify a date range in a request.

The following examples compare the start-date and end-date parameters in a v3 request and the dateRanges field in a v4 request:

v3

GET https://www.googleapis.com/analytics/v3/data/ga?ids=ga:XXXX \
    &start-date=2015-11-01&end-date=2015-11-06 \
    ...

v4

POST https://analyticsreporting.googleapis.com/v4/reports:batchGet
{
  "reportRequests":[
  {
    "viewId":"XXXX",
    "dateRanges":[
    {
      "startDate":"2015-11-01",
      "endDate":"2015-11-06"
    }],
    ....
  }]
}

Metrics

The v3 metrics parameter corresponds to the v4 metrics field that takes a list of Metric objects.

The following examples compare the metrics parameters in a v3 request and the metrics field in a v4 request:

v3

GET https://www.googleapis.com/analytics/v3/data/ga?ids=ga:XXXX \
    &start-date=2015-11-01&end-date=2015-11-06 \
    &metrics=ga:users,ga:sessions \
    ...

v4

POST https://analyticsreporting.googleapis.com/v4/reports:batchGet
{
  "reportRequests":[
  {
    "viewId":"XXXX",
    "dateRanges":[
    {
      "startDate":"2015-11-01",
      "endDate":"2015-11-06"
    }],
    "metrics":[
    {
      "expression":"ga:users"
    },{
      "expression":"ga:sessions"
    }],
    ...
  }]
}

Dimensions

The v3 dimensions parameter corresponds to the v4 dimensions field that takes a list of Dimension objects.

The following examples compare the dimensions parameters in a v3 request and the dimensions field in a v4 request:

v3

GET https://www.googleapis.com/analytics/v3/data/ga?ids=ga:XXXX \
  &dimensions=ga:country,ga:browser&... \

v4

POST https://analyticsreporting.googleapis.com/v4/reports:batchGet
{
  "reportRequests":[
  {
    ...
    "dimensions": [
    {
      "name":"ga:country"
    },{
      "name":"ga:browser"
    }],
    ...
  }]
}

Sorting

The v3 sort parameter is equivalent to the v4 orderBys field that takes a list of OrderBy objects.

In v4, to sort the results by a dimension or metric value:

  • Supply its name or alias through the fieldName field.
  • Specify the sort order (ASCENDING or DESCENDING) through the sortOrder field.

The following examples compare the sort parameter in a v3 request and the orderBy field in a v4 request; they both sort the users in descending order and sources alphabetically:

v3

GET https://www.googleapis.com/analytics/v3/data/ga?ids=ga:XXXX \
  &sort=-ga:users,ga:source

v4

POST https://analyticsreporting.googleapis.com/v4/reports:batchGet
{
  "reportRequests":[
  {
    ...
    "orderBys": [
    {
      "fieldName": "ga:users",
      "sortOrder": "DESCENDING"
    },{
      "fieldName": "ga:source"
    }],
  }]
}

Sampling Level

The v3 samplingLevel parameter corresponds to the v4 samplingLevel field. In v3, the accepted samplingLevel values are FASTER, HIGHER_PRECISION, and DEFAULT; and in v4, the accepted samplingLevel values are SMALL, LARGE, and DEFAULT. Note that FASTER in v3 has changed to SMALL in v4, HIGHER_PRECISION to LARGE.

The following examples compare the samplingLevel parameter in a v3 request and the samplingLevel field in a v4 request:

v3

https://www.googleapis.com/analytics/v3/data/ga?ids=ga:XXXX ...\
samplingLevel=HIGHER_PRECISION

v4

POST https://analyticsreporting.googleapis.com/v4/reports:batchGet
{
  "reportRequests":[
  {
    ...
    "samplingLevel": "LARGE"
  }]
}

Segments

The v3 segment parameter corresponds to the v4 segments field that takes a list of Segment objects.

Segment IDs

In v4, to request a segment by segment ID, construct a Segment object and supply its ID through the segmentId field.

The following examples compare the segment parameter in a v3 request and the segments field in a v4 request:

v3

GET https://www.googleapis.com/analytics/v3/data/ga?ids=ga:XXXX \
&segment=gaid::-11

v4

POST https://analyticsreporting.googleapis.com/v4/reports:batchGet
{
  "reportRequests": [{
    ...
    "viewId": "XXXX",
    "segments": [{
      "segmentId": "gaid::-11"
    }]
  }]
}

Dynamic Segments

In v4, to express more complicated segment definitions, use the segments field that includes a DynamicSegment object.

The following examples compare the segment parameter in a v3 request and the segments field containing a DynamicSegment object in a v4 request:

v3

GET https://www.googleapis.com/analytics/v3/data/ga?ids=ga:XXXX \
&segment=sessions::condition::ga:medium==referral

v4

POST https://analyticsreporting.googleapis.com/v4/reports:batchGet
{
  "reportRequests": [{
    ...
    "segments": [{
      "dynamicSegment": {
        "name": "segment_name",
        "sessionSegment": {
          "segmentFilters": [{
            "simpleSegment": {
              "orFiltersForSegment": [{
                "segmentFilterClauses": [{
                  "dimensionFilter": {
                    "dimensionName": "ga:medium",
                    "operator": "EXACT",
                    "expressions": [ "referral" ]
                  }
                }]
              }]
            }
          }]
        }
      }
    }]
  }]
}

You can combine conditions and sequences in a segment:

v3

GET https://www.googleapis.com/analytics/v3/data/ga?ids=ga:XXXX \
&segment=users::condition::ga:revenue>10;sequence::ga:deviceCategory==desktop->>ga:deviceCategory==mobile

v4

  "reportRequests": [{
      "dateRanges": [
            { "endDate": "2014-11-30", "startDate": "2014-11-01" }
      ],
      "metrics": [
          {"expression": "ga:pageviews"},
          {"expression": "ga:sessions"}
      ],
      "viewId": "XXXX",
      "dimensions":[{"name":"ga:medium"}, {"name":"ga:segment"}],
      "segments": [{
        "dynamicSegment": {
        "name": "segment_name",
        "userSegment": {
          "segmentFilters": [{
            "simpleSegment": {
              "orFiltersForSegment": [{
                "segmentFilterClauses": [{
                  "metricFilter": {
                    "metricName": "ga:sessions",
                    "operator": "GREATER_THAN",
                    "comparisonValue": "10"
                  }
                }]
              }]
            }
          },
          {
            "sequenceSegment": {
              "segmentSequenceSteps": [{
                "orFiltersForSegment": [{
                  "segmentFilterClauses": [{
                    "dimensionFilter": {
                      "dimensionName": "ga:deviceCategory",
                      "operator": "EXACT",
                      "expressions": ["desktop"]
                    }
                  }]
                }],
                "matchType": "PRECEDES"
              },{
                "orFiltersForSegment": [{
                  "segmentFilterClauses": [{
                    "dimensionFilter": {
                      "dimensionName": "ga:deviceCategory",
                      "operator": "EXACT",
                      "expressions": ["mobile"]
                    }
                  }]
                }]
              }]
            }
          }]
        }
      }
    }]
  }]

v3 Segments Syntax in v4

The segmentId field in the v4 API supports the segment syntax in the v3 API.

The following examples show how the segment parameter in a v3 request is supported by the segmentId field in the equivalent request in v4:

v3

GET https://www.googleapis.com/analytics/v3/data/ga?ids=ga:XXXX \
&segment=sessions::condition::ga:medium==referral

v4

POST https://analyticsreporting.googleapis.com/v4/reports:batchGet
{
  "reportRequests": [{
    ...
    "viewId": "XXXX",
    "segments": [{
      "segmentId": "sessions::condition::ga:medium==referral"
    }]
  }]
}

Filters

v4 uses dimensionFilterClauses to filter dimensions and metricFilterClauses to filter metrics. A dimensionFilterClauses contains a list of DimensionFilter objects; and a metricFilterClauses contains a list of MetricFilter objects.

The following examples compare the filters parameter in a v3 request and the dimensionFilterClauses field in a v4 request:

v3

GET https://www.googleapis.com/analytics/v3/data/ga?ids=ga:XXXX \
  &start-date=2015-06-01&end-date=2015-07-31&metrics=ga:users& \
  dimensions=ga:browser&filters=ga:browser==Firefox

v4

  "reportRequests": [{
      "dateRanges": [
            { "endDate": "2014-11-30", "startDate": "2014-11-01" }
      ],
      "metrics": [
          {"expression": "ga:pageviews"},
          {"expression": "ga:sessions"}
      ],
      "viewId": "XXXX",
      "dimensions":[{"name":"ga:browser"}, {"name":"ga:country"}],
      "dimensionFilterClauses": [{
           "filters": [{
                "dimension_name": "ga:browser",
                "operator": "EXACT",
                "expressions": ["Firefox"]
            }]
      }]
  }]

v3 filters syntax in v4

Although the filtersExpression field in v4 supports the filters syntax in v3, use dimensionFilterClauses and metricFilterClauses to filter dimensions and metrics.

The following examples show how the filters parameter in a v3 request is supported by the filtersExpression field in the equivalent request in v4:

v3

GET https://www.googleapis.com/analytics/v3/data/ga?ids=ga%XXXX \
&filters=ga:browser==Firefox

v4

POST https://analyticsreporting.googleapis.com/v4/reports:batchGet
{
  "reportRequests": [{
    ...
    "filtersExpression": "ga:browser==Firefox"
  }]
}

Empty rows

The v3 include-empty-rows parameter corresponds to the includeEmptyRows field in v4. The v3 parameter defaults to true, while in v4 the field defaults to false. If you do not have the value set in v3 you will need to set the value to true in v4.

The following examples compare the include-empty-rows parameter in a v3 request to the includeEmptyRows field in a v4 request:

v3

https://www.googleapis.com/analytics/v3/data/ga? ...\
    &include-empty-rows=true

v4

POST https://analyticsreporting.googleapis.com/v4/reports:batchGet
{
  "reportRequests":[
  {
    ...
    "includeEmptyRows": "true",
  }]
}

Pagination

v4 uses the pageToken and pageSize fields to paginate through a large number of results. The pageToken is obtained from the nextPageToken property of a response object.

The following examples compare the start-index and max-results parameters in a v3 request to the pageToken and pageSize fields in a v4 request:

v3

https://www.googleapis.com/analytics/v3/data/ga? ...\
    &start-index=10001&max-results=10000

v4

POST https://analyticsreporting.googleapis.com/v4/reports:batchGet
{
  "reportRequests":[
  {
    ...
    # Taken from `nextPageToken` of a previous response.
    "pageToken": "10000",
    "pageSize": "10000",
  }]
}

Standard Parameters

The Analytics Reporting API v4 supports most of the standard query parameters in the v3 API, except for the userIp and callback parameters.

The following examples compare the quotaUser parameter in a v3 request to that in a v4 request:

v3 Endpoint

GET https://www.googleapis.com/analytics/v3/data/ga?quotaUser=1X3F2F2

v4 Endpoint

POST https://analyticsreporting.googleapis.com/v4/reports:batchGet?quotaUser=1X3F2F2

Responses

Because v4 allows you to submit multiple ReportRequest objects in a single HTTP request, you get an array of report objects in the response. For every submitted ReportRequest, you get a corresponding Report in the response.

Reports

A v4 Report has three top-level fields: columnHeader, data, and nextPageToken.

Because the v4 response body doesn't include responses to all query parameters as the v3 response does, to get a response to a specific query parameter, the client application must add that parameter to the ReportRequest.

We've already addressed nextPageToken in the Pagination section, so lets first look at the columnHeader object.

Column Header

The column header contains a list of named dimensions and a MetricHeader object, which contains a list of MetricHeaderEntry objects. Each MetricHeaderEntry object specifies the metric name and its type (currency, percent, etc.) , which helps you format the output.

The following examples compare the columnHeaders field in a v3 response to the columnHeader field in a v4 response:

v3

"columnHeaders": [
    {
        "name":"ga:source",
        "columnType":"DIMENSION",
        "dataType":"STRING"
    },{
        "name":"ga:city",
        "columnType":"DIMENSION",
        "dataType":"STRING"
    },{
        "name":"ga:sessions",
        "columnType":"METRIC",
        "dataType":"INTEGER"
    },{
        "name":"ga:pageviews",
        "columnType":
        "METRIC",
        "dataType":"INTEGER"
    }
]

v4

"columnHeader": {
    "dimensions": [
        "ga:source",
        "ga:city"
    ],
    "metricHeader": {
        "metricHeaderEntries": [
            {
                "name": "ga:pageviews",
                "type": "INTEGER"
            },
            {
                "name": "ga:sessions",
                "type": "INTEGER"
            }
        ]
    }
},

Report Rows

The Core Reporting API v3 returns the report data in the rows array, which contains the requested dimensions and metrics.

The Analytics Reporting API v4 returns the report data in a ReportRow object, which contains an array of dimensions and an array of DateRangeValues objects, each of which contains one or two date ranges, as the following diagram depicts:

Report Rows

Rows

v3

"rows": [
    [
        "google",
        "Philadelphia",
        "60",
        "5"
    ],
    [
        "google",
        "Johnstown",
        "21",
        "1"
    ],
    [
        "google",
        "Progress",
        "7",
        "1"
    ]
],

v4

"rows": [
    {
        "dimensions": [
            "google",
            "Philadelphia"
        ],
        "metrics": [
            {
                "values": [
                    "60",
                    "5"
                ]
            }
        ]
    },
    {
        "dimensions": [
            "google",
            "Johnstown"
        ],
        "metrics": [
            {
                "values": [
                    "21",
                    "1"
                ]
            }
        ]
    },
    {
        "dimensions": [
            "google",
            "Progress"
        ],
        "metrics": [
            {
                "values": [
                    "7",
                    "1"
                ]
            }
        ]
    }
],

Sampled data

If the results are sampled the Core Reporting API v3 returns the boolean field containsSampledData, which is set to true.

The Analytics Reporting API v4 does not return a boolean if the data is sampled; rather the API returns the samplesReadCounts and the samplingSpaceSizes fields. If the results are not sampled these fields will not be defined. The following Python example shows how to calculate if a report contains sampled data:

def ContainsSampledData(report):
  """Determines if the report contains sampled data.

   Args:
       report (Report): An Analytics Reporting API v4 response report.

  Returns:
      bool: True if the report contains sampled data.
  """
  report_data = report.get('data', {})
  sample_sizes = report_data.get('samplesReadCounts', [])
  sample_spaces = report_data.get('samplingSpaceSizes', [])
  if sample_sizes and sample_spaces:
    return True
  else:
    return False

Below is an example response which contains sampled data from a request with two date ranges. The results were calculated from almost 500k samples of a sampling space size of approximately 15 million sessions:

{
  "reports":
  [
    {
      "columnHeader": {
        ...
      },
      "data": {
        ...
        "samplesReadCounts": [ "499630","499630"],
        "samplingSpaceSizes": ["15328013","15328013"],
      }
    }
  ]
}

Parsing the v4 response

The following sample code parses and prints the Analytics Reporting API v4 response:

Python

def printResponse(self, response):
  """Parses and prints the Analytics Reporting API v4 response"""

  for report in response.get('reports', []):
    columnHeader = report.get('columnHeader', {})
    dimensionHeaders = columnHeader.get('dimensions', [])
    metricHeaders = columnHeader.get('metricHeader', {}).get('metricHeaderEntries', [])
    rows = report.get('data', {}).get('rows', [])

    for row in rows:
      dimensions = row.get('dimensions', [])
      dateRangeValues = row.get('metrics', [])

      for header, dimension in zip(dimensionHeaders, dimensions):
        print header + ': ' + dimension

      for i, values in enumerate(dateRangeValues):
        print 'Date range (' + str(i) + ')'
        for metricHeader, value in zip(metricHeaders, values.get('values')):
          print metricHeader.get('name') + ': ' + value

Java

public static void printResponse(GetReportsResponse response) {

  for (Report report: response.getReports()) {
    ColumnHeader header = report.getColumnHeader();
    List<String> dimensionHeaders = header.getDimensions();
    List<MetricHeaderEntry> metricHeaders = header.getMetricHeader().getMetricHeaderEntries();
    List<ReportRow> rows = report.getData().getRows();

    for (ReportRow row: rows) {
      List<String> dimensions = row.getDimensions();
      List<DateRangeValues> metrics = row.getMetrics();
      for (int i = 0; i < dimensionHeaders.size() && i < dimensions.size(); i++) {
        System.out.println(dimensionHeaders.get(i) + ": " + dimensions.get(i));
      }

      for (int j = 0; j < metrics.size(); j++) {
        System.out.print("Date Range (" + j + "): ");
        DateRangeValues values = metrics.get(j);
        for (int k = 0; k < values.size() && k < metricHeaders.size(); k++) {
          System.out.println(metricHeaders.get(k).getName() + ": " + values.get(k));
        }
      }
    }
  }
}

Error handling

Because the error response format in v4 is different from that in v3, update your code to handle v4 error responses.

The following examples compare an error response in v3 and the equivalent error response in v4:

v3

{
 "error": {
  "errors": [
   {
    "domain": "global",
    "reason": "insufficientPermissions",
    "message": "User does not have sufficient permissions for this profile.",

   }
  ],
  "code": 403,
  "message": "User does not have sufficient permissions for this profile."
 }
}

v4

{
 "error": {
  "code": 403,
  "message": "User does not have sufficient permissions for this profile.",
  "status": "PERMISSION_DENIED",
  "details": [
   {
    "@type": "type.googleapis.com/google.rpc.DebugInfo",
    "detail": "[ORIGINAL ERROR] generic::permission_denied: User does not have sufficient permissions for this profile. [google.rpc.error_details_ext] { message: \"User does not have sufficient permissions for this profile.\" }"
   }
  ]
 }
}