PMax N-Gram Lens is the Performance Max specialist of the suite. It reads the real search terms of PMax campaigns — with cost — from campaign_search_term_view, breaks them into n-grams, splits brand from generic traffic and ranks negative candidates you can paste as campaign-level phrase negatives.

How it works

Enabled (optionally also paused) PMax campaigns with impressions in the window are selected. Terms are queried in chunks of 200 campaigns with impressions, clicks, cost, conversions and value; there are no keyword-related segments on this view, so nothing is mixed in. Negatives are checked at campaign level, on attached shared lists (by ID) and at account level. If brand_terms is set, every term is classified as brand or generic and the click share is reported. The optional category roll-up from campaign_search_term_insight (include_categories) writes category totals only and is never merged with term rows because it needs one extra query per campaign. Negative candidates are ranked by wasted clicks, conversion-rate ratio and CPA multiple.

What it writes to the Sheet

  • Summary — PMax campaigns in scope, unique (campaign, term) rows analysed and excluded by negatives, n-gram sizes, totals, brand vs generic click share, negative candidate count, cost sitting on negative candidates, notes and ⚠ warnings.
  • Account n-grams, Campaign n-grams — phrase-level metrics account-wide and per campaign.
  • Categories — 8-column category totals, only when include_categories is true.
  • Brand split — brand vs generic segment metrics.
  • Suggestions — negative candidates with campaign, “Paste as”, wasted clicks (est.) and “Why”.
  • Terms — raw (campaign, term) rows with a “Blocked by negative” column, when write_raw_terms is true.

Setup

  1. Create a new, empty Google Sheet with the Google account you will authorise the script with and copy its URL. Do not reuse a Sheet from another script — each Sheet is bound to one script and one Google Ads account.
  2. In Google Ads open Tools → Scripts, create a new script, paste the whole code and give it a name.
  3. Set CONFIG.spreadsheet_url to the Sheet URL. If you run the script from a manager (MCC) account, also set CONFIG.account_id to the client account ID (e.g. 123-456-7890); without it the script stops with a clear error.
  4. Optional: fill CONFIG.email (comma-separated recipients) and/or CONFIG.slack_webhook. Keep the webhook in the code only — it is never written to the Sheet.
  5. Authorise the script, run Preview once to check the log, then Run. The first run creates the tabs, the header rows and the _settings tab.
  6. Schedule it: weekly, Monday at 04:00 account time or later.
  7. Optional: download the Excel dashboard (button above), open its Read me tab and paste the Sheet tabs as described — the column layout matches the script output one-to-one.

Parameters

ParameterDescriptionDefault
spreadsheet_urlURL of the empty Google Sheet the script writes to.'YOUR_SPREADSHEET_URL'
account_idClient account ID; required only when running from a manager (MCC) account.''
daysLength of the analysis window in days.30
min_ngram / max_ngramSmallest and largest n-gram size (1 = single words, 3 = three-word phrases).1 / 3
campaign_containsOnly campaigns whose name contains this text.''
campaign_not_containsExclude campaigns whose name contains this text.''
enabled_campaigns_onlytrue = enabled campaigns only; false = enabled and paused.true
min_query_impressionsMinimum impressions for a search term to be read.1
check_negativesMatch terms against existing negatives (ad group, campaign, shared list, account level).true
brand_termsBrand words or phrases; empty = brand split disabled.[]
languageTokeniser language; defaults to the build language (en / tr / de).L.text_lang
stopwordsExtra words to ignore when building n-grams.[]
include_categoriesCategory roll-up from campaign_search_term_insight; one extra query per campaign, can hit RESOURCE_EXHAUSTED on large accounts.false
write_raw_termsWrite the Terms tab with raw rows.true
max_rows_writtenRow cap per table; when exceeded a truncation note is added.20000
thresholdsMinimum query count / impressions / clicks / cost for an n-gram to be listed.1 / 20 / 0 / 0
suggestionsnegative_min_clicks, negative_max_cr_ratio, negative_max_cpa_multiple, list_size.20 / 0.25 / 3 / 100
All values live in the CONFIG block at the top of the script; keys that also exist in the _settings tab can be changed there.

The _settings tab

On the first run the script creates a _settings tab (Key / Value / Notes). Values there override the matching CONFIG entries, so day-to-day tuning happens in the Sheet without editing code. Numbers must be written without thousands separators and with at most two decimals (0.5 or 0,5). The rows script and account mark the Sheet’s owner: if another script or another account opens the same Sheet, it stops before writing anything. Internal tabs starting with an underscore (_settings, _history, _snapshot, _state, _baseline) are language-independent, so switching between the EN/TR/DE builds keeps the stored state.

Notifications

This script has no e-mail or Slack option; the Sheet and the dashboard are the output.

Notes and limits

  • Negatives are never applied automatically — review the Suggestions tab, then add them as phrase negatives to the campaign.
  • If the term query fails or hits the read limit, the Summary shows a ⚠ line instead of silently returning less data.
  • Every report tab carries a small attribution cell (H1:K1, warning-only protection) and the code header keeps a copyright line; both may be removed by anyone who edits the source.