Segmentation

Segmentation, available in the Google Ads UI as a separate menu, can be implemented in the Google Ads API by just adding the proper field to a query. For example, adding segments.device to a query, results in a report with a row for each combination of device and the specified resource in the FROM clause, and the statistical values (impressions, clicks, conversions, etc.) split between them.

While in the Google Ads UI only one segment at a time can be used, with the API you can specify multiple segments in the same query.

SELECT
  campaign.name,
  campaign.status,
  segments.device,
  metrics.impressions
FROM campaign

The results from sending this query to GoogleAdsService.SearchStream would look something like this JSON string:

{
  "results":[
    {
      "campaign":{
        "resourceName":"customers/1234567890/campaigns/111111111",
        "name":"Test campaign",
        "status":"ENABLED"
      },
      "metrics":{
        "impressions":"10922"
      },
      "segments":{
        "device":"MOBILE"
      }
    },
    {
      "campaign":{
        "resourceName":"customers/1234567890/campaigns/111111111",
        "name":"Test campaign",
        "status":"ENABLED"
      },
      "metrics":{
        "impressions":"28297"
      },
      "segments":{
        "device":"DESKTOP"
      }
    },
    ...
  ]
}

Note that in the above sample result, attributes for the first and second objects including the resource name are the same. The impressions are segmented by device and thus two or more objects can be returned for the same campaign.

Starting in v4, segments.date, segments.week, segments.month, segments.quarter, and segments.year function as follows:

  • These segments can be filtered in the WHERE clause without appearing in the SELECT clause.

  • If any of these segments are in the SELECT clause, at least one date range must be specified for a date segment in the WHERE clause (the date segment does not need to be the same one specified in the SELECT).

Examples

Invalid: Since segments.date is in the SELECT clause, you need to specify a finite date range in the WHERE clause for a segments.date, segments.week, segments.month, segments.quarter, or segments.year.

SELECT
  campaign.name,
  metrics.clicks,
  segments.date
FROM campaign

Valid: This query returns campaign names and clicks accrued during the date range. Note that segments.date doesn't need to appear in the SELECT clause.

SELECT
  campaign.name,
  metrics.clicks
FROM campaign
WHERE segments.date > '2020-01-01'
  AND segments.date < '2020-02-01'

Valid: This query returns campaign names and clicks segmented by date for all days in the date range.

SELECT
  campaign.name,
  metrics.clicks,
  segments.date
FROM campaign
WHERE segments.date > '2020-01-01'
  AND segments.date < '2020-02-01'

Valid: This query returns campaign names and clicks segmented by month for all days in the date range.

SELECT
  campaign.name,
  metrics.clicks,
  segments.month
FROM campaign
WHERE segments.date > '2020-01-01'
  AND segments.date < '2020-02-01'

Valid: This query returns campaign names and clicks segmented by quarter and then by month for all months in the year range.

SELECT
  campaign.name,
  metrics.clicks,
  segments.quarter,
  segments.month
FROM campaign
WHERE segments.year > 2015
  AND segments.year < 2020

Implicit segmentation

Every report is segmented by the resource specified in the FROM clause. For example, when you specify ad_group as the resource in the FROM clause, it will implicitly segment data by the resource name of ad group, even though the ad_group.resource_name field is not included in the query.

So for this query,

SELECT metrics.impressions
FROM ad_group

you would get a JSON string like this:

{
  "results":[
    {
      "adGroup":{
        "resourceName":"customers/1234567890/adGroups/2222222222"
      },
      "metrics":{
        "impressions":"237"
      }
    },
    {
      "adGroup":{
        "resourceName":"customers/1234567890/adGroups/33333333333"
      },
      "metrics":{
        "impressions":"15"
      }
    },
    {
      "adGroup":{
        "resourceName":"customers/1234567890/adGroups/44444444444"
      },
      "metrics":{
        "impressions":"0"
      }
    }
  ]
}

Note that the resource_name field of adGroup is always returned because ad_group was specified as a resource in the FROM clause.

search_term_view

Note that for the search_term_view resource, it's also implicitly segmented by ad group, not just a search term, as reflected by the structure of its resource name, which also includes ad group as well. Therefore, you will get some seemingly duplicate rows with the same search terms appearing in your results when in actuality, the rows would belong to a different ad group:

{
  "results":[
    {
      "searchTermView":{
        "resourceName":"customers/1234567890/searchTermViews/111111111~2222222222~Z29vZ2xlIHBob3RvcyBpb3M",
        "searchTerm":"google photos"
      },
      "metrics":{
        "impressions":"3"
      },
      "segments":{
        "date":"2015-06-15"
      }
    },
    {
      "searchTermView":{
        "resourceName":"customers/1234567890/searchTermViews/111111111~33333333333~Z29vZ2xlIHBob3RvcyBpb3M",
        "searchTerm":"google photos"
      },
      "metrics":{
        "impressions":"2"
      },
      "segments":{
        "date":"2015-06-15"
      }
    }
  ]
}

Although the two returned objects in this example seem to be duplicates, their resource names are actually different, particularly in the "ad group" part. This means that the search term "google photos" is attributed to the two ad groups (ID 2222222222 and 33333333333) on the same date (2015-06-15). Thus, we can conclude that the API worked as intended and didn't return duplicate objects in this case.