Google Ads Product Performance
Fetch product-level performance metrics from Google Ads Shopping and Performance Max campaigns
What’s New — May 2026 — Output fields are now user-selectable via two checkbox groups in Settings: Core Fields (which columns appear on each row — defaults to productItemId, impressions, clicks, cost, conversions, roas) and Segment Fields (which segments to project when Include Segments is on — defaults to productBrand only). Trimming the projection drops the column count directly and the row count indirectly (fewer segment columns → coarser GAQL GROUP BY).
What does the Google Ads Product Performance node do?
The Google Ads Product Performance node fetches product-level performance from Google Ads — one row per (campaign, product) over the selected period — for Shopping and Performance Max campaigns. It returns impressions, clicks, cost (in account currency, not micros), conversions, conversion value, and the derived metrics CTR / CVR / ROAS, alongside product identifiers (item ID, title) and, optionally, the full taxonomy (brand, product type L1-L5, the 5 Merchant Center custom labels).
The node is sourced from GAQL’s shopping_performance_view. Aggregation is always at the (campaign, product) level — segments.date is intentionally never projected, which collapses the result set N× compared to a per-day breakdown.
Common uses:
- Feed a product scoring / labelization pipeline (Product Hero-style)
- Rank top / bottom SKUs by ROAS or CVR
- Join performance with the merchant catalog downstream
- Investigate why a specific custom-label cohort (e.g.
custom_label_1 = "heroes") is over- or under-performing
For campaign-level or keyword-level reports, use Google Ads Campaign Reports instead.
Quick setup
Connect a Google Ads integration
Builder → Integrations → Google Ads. Authenticate the OAuth client for the Merchant Center account whose products you want to query.
Add the node to the canvas
Integrations → Google → Google Ads Product Performance.
Pick the customer and campaign
Inline: select one Google Ads customer from the dropdown, then one Shopping / Performance Max campaign (searchable). The selector restricts to a single campaign per node — Product Performance returns a lot of rows and one campaign keeps the payload manageable.
Runtime: enable the Use variables for customer ID and campaign ID toggle and wire the customer/campaign anchors from an upstream Google Ads Campaigns input node.
Pick a date range
Standard presets (Last 7 days, Last 30 days, Month-to-date, …). Conventions match Google Ads’ UI — “Last N days” excludes today (yesterday and the N-1 days before).
(Optional) Include segments + filters
Tick Include Segments to add brand, product type L1-L5 and the 5 custom labels to each output row. Once segments is on, the Custom label filters sub-section becomes editable so you can restrict the result set to specific Merchant Center custom-label values (e.g. only the SKUs labelled heroes).
(Optional) Pick output fields
Two checkbox groups at the bottom of Settings let you control payload shape: Core Fields (always available, 12 keys) and Segment Fields (gated by Include Segments, 11 keys). Uncheck what you don’t need to shrink the response — useful when piping into NATS-backed downstream nodes.
Configuration parameters
Required fields
integration_id string required The Google Ads integration to authenticate the API call. Either picked inline, or externalized via the Use a variable for integration toggle.
customer_id string required Google Ads customer ID (without dashes), e.g. 1234567890. Inline pick or externalized via the Use variables toggle.
campaign_ids string required Exactly one campaign ID (Shopping or Performance Max). Stored as a single-element list internally. To query several campaigns, add one node per campaign and merge downstream.
date_range string required default: last_7_days Predefined date range. Same presets as Google Ads Campaign Reports and aligned with the Google Ads UI — last_N_days ends yesterday, month_to_date / year_to_date include today.
Optional fields
include_segments boolean default: false When enabled, the GAQL SELECT projects product taxonomy (productBrand, productTypeL1..L5) and the 5 Merchant Center custom labels (customAttribute0..4). These extra columns appear on every output row.
custom_label_filters list default: [] Server-side equality filters on Merchant Center custom labels. Each entry has {slot: 0..4, value: "string"} where slot matches one of the 5 custom-label columns of the Merchant Center feed. The filters are applied in the GAQL WHERE clause — only matching rows are returned, which drastically reduces payload on large catalogs.
Gated by include_segments: the filter input is disabled until segments are enabled (the filtered slot must be present in SELECT).
custom_label_filter_combinator string default: AND How multiple custom-label filters are combined: AND (every filter must match) or OR (any filter matches).
GAQL limitation — OR is only valid when every filter targets the same slot (the node translates this into IN (...)). Cross-slot OR is not expressible in GAQL: the save button is blocked with a toast error if you configure it.
core_fields list default: ["productItemId", "impressions", "clicks", "cost", "conversions", "roas"] Subset of the 12 core output keys to project on each row. Available keys: campaignId, campaignName, productItemId, productTitle, impressions, clicks, cost, conversions, conversionsValue, ctr, cvr, roas. Empty list = include all (legacy back-compat).
The selection drives both the GAQL SELECT and the JSON projection — unchecked keys aren’t fetched from Google Ads in the first place. A few anchors stay in the SELECT even when their key is unchecked (campaign.id, segments.product_item_id, customer.currency_code) because the runner needs them for logging and row-skip. Unique counts (uniqueCampaigns, uniqueProducts) are computed from the raw API rows, so they stay accurate even if you deselect campaignId / productItemId.
segment_fields list default: ["productBrand"] Subset of the 11 segment keys to project on each row when include_segments is on. Available keys: productBrand, productTypeL1..L5, customAttribute0..4. Empty list = include all (legacy back-compat).
Each checked segment is a GAQL GROUP BY dimension — picking more segments means finer aggregation and more output rows, not just more columns. Conversely, deselecting segments coarsens the GROUP BY and collapses rows server-side.
Externalization toggles
use_variables boolean default: false Atomic toggle: customer ID and campaign ID both become variables together, fed by upstream input handles.
use_integration_variable boolean default: false Externalizes the integration. Forces use_variables to ON because the settings panel can’t load customer/campaign lists without a concrete integration.
What does the node output?
A JSON object with a rows array. Each row is one (campaign, product) combination summed over the period.
{
"customerId": "1234567890",
"startDate": "2026-01-01",
"endDate": "2026-01-31",
"rows": [
{
"campaignId": "1111111111",
"campaignName": "Shopping FR",
"productItemId": "sku-123",
"productTitle": "Red Sneakers",
"impressions": 1000,
"clicks": 100,
"cost": 150.5,
"conversions": 10,
"conversionsValue": 500.0,
"ctr": 0.1,
"cvr": 0.1,
"roas": 3.32
}
],
"meta": { "currencyCode": "EUR", "rowCount": 1 }
}
With include_segments: true
Each row gains the product taxonomy and the 5 Merchant Center custom labels:
{
"productBrand": "Nike",
"productTypeL1": "Apparel",
"productTypeL2": "Footwear",
"productTypeL3": "Sneakers",
"productTypeL4": "",
"productTypeL5": "",
"customAttribute0": "season-spring",
"customAttribute1": "heroes",
"customAttribute2": "",
"customAttribute3": "",
"customAttribute4": ""
}
Cost is returned in account currency, not in Google Ads micros. Derived metrics (ctr, cvr, roas) are computed from the aggregated totals with zero-division guards (fallback to 0). Rows without a productItemId are dropped — product joins require a non-empty ID.
Usage examples
Example 1: Product scoring pipeline
Google Ads Campaigns → Google Ads Product Performance → LLM (rank/label) → Google Sheets Writer
Configuration:
{
"customer_id": "{{customerId}}",
"campaign_ids": "{{campaignIds}}",
"date_range": "last_30_days",
"include_segments": true,
"use_variables": true
}
Example 2: Drill down on a custom-label cohort
Restrict the result to the SKUs your Merchant Center feed has tagged as “heroes” in custom_label_1, and pipe the rows into a Sheet for inspection.
Google Ads Product Performance → JSON Path Extractor → Google Sheets
Configuration:
{
"customer_id": "1234567890",
"campaign_ids": "9999999999",
"date_range": "last_30_days",
"include_segments": true,
"custom_label_filters": [
{ "slot": 1, "value": "heroes" }
],
"custom_label_filter_combinator": "AND"
}
Example 3: Multi-value OR on the same slot
Same as above, but pull both “heroes” and “villains” together. Same-slot OR is rewritten to GAQL IN (...).
Configuration:
{
"custom_label_filters": [
{ "slot": 1, "value": "heroes" },
{ "slot": 1, "value": "villains" }
],
"custom_label_filter_combinator": "OR"
}
Best practices
Payload size — Product Performance can return tens of thousands of rows on large Shopping accounts. Use custom_label_filters to narrow the result set before piping into NATS-backed downstream nodes; the filter is server-side, so it directly reduces the bytes shipped over the wire.
Product dimensions (brand, product type, custom labels) depend on what’s in the merchant feed. Empty strings are normal for accounts that don’t fill every Merchant Center column.
The five custom_label_0..4 slots are free-text columns in the Merchant Center feed. Different merchants use them differently. Inspect your own feed to know what each slot contains (typical patterns: season, gender, margin tier, promo, performance status).
Payload-shape lever — Use Core Fields to drop columns you don’t need (linear column-count win) and Segment Fields to coarsen the GAQL GROUP BY (non-linear row-count win on accounts with deep merchant taxonomies). The two are independent — you can keep all segments for filtering but project only productBrand on output.
Common issues
Empty rows array even though the customer + campaign look right
The selected campaign is probably not a Shopping or Performance Max campaign (shopping_performance_view only returns rows for those). Pick a Shopping / PMax campaign or check the campaign type in the Google Ads UI.
`OR` toggle disabled with a red hint
GAQL can only express OR when every filter targets the same custom-label slot. Either switch to AND, or keep all filter rows on a single slot. The save button is blocked with a toast otherwise.
Custom label filters section greyed out
The filter sub-section is gated by Include Segments — tick the segments checkbox to enable it. Reason: filtering on a custom_attribute* slot forces it into the GAQL SELECT, which only makes sense once you’ve opted into segments.
A SKU appears on multiple rows when segments is on
This indicates that one of the segment dimensions (brand, product type, a custom label) changed value mid-period for that SKU in the merchant feed. GAQL groups by every projected segment, so the SKU is split across rows — one per attribute combination. The aggregated totals are still correct when summed; this is a feed-volatility signal worth investigating.
The selected campaign disappears from the dropdown
Campaign was archived or renamed in Google Ads after the workflow was saved. Re-open the settings, the validation effect clears the stale ID and you can re-pick a valid one.
A column I expected is missing from the output rows
The column is unchecked in Core Fields (or Segment Fields if it’s a segment). Re-open Settings and tick it. Workflows saved before May 2026 default to the 6-key core projection on first re-open — re-tick the columns you used to rely on.
Related nodes
Runtime selector for customer + campaign — pairs with this node when use_variables is on
Campaign / keyword-oriented sibling — use when product granularity isn’t needed
Score, label or summarize the product rows
Export the product performance table for inspection or dashboarding
Project specific columns from the rows array before piping further