The Query Cookbook
GAQL waste finder: four queries for the spend that earns nothing
A pack of read-only Google Ads queries that surface search terms, keywords, placements and whole campaigns taking budget while returning no conversions.
Wasted spend is rarely one big leak. It is a search term nobody reads, a keyword whose quality score quietly collapsed, a display placement on a parked domain, and a campaign whose conversion tag stopped firing. Each hides in a different report, so each usually gets found on a different unlucky day. This pack pulls all four into queries you can run in one sitting.
Every query here reads. None of them changes a bid, a budget or a status. They tell you where to look; the decision, and the pausing, stays with you.
Before you start
- Decide the window you trust. These use
LAST_30_DAYS; shorten it for a high-volume account, lengthen it for a thin one. - A zero-conversion row is a question, not a verdict. A brand-new keyword, a considered-purchase term with a long lag, or an assisted-path click can all show zero last-click conversions and still be pulling weight. Read before you cut.
metrics.cost_microsis in millionths of the account currency: divide by 1,000,000 for a spend figure.
Search terms that spent and never converted
The first place waste accumulates is the search terms report, where broad and phrase match quietly buy queries you never chose. This query lists the terms that cost money over the window and returned no conversions, worst spender first. Look for off-topic queries, job seekers, and the competitor names you meant to exclude.
SELECT
search_term_view.search_term,
campaign.name,
ad_group.name,
metrics.cost_micros,
metrics.clicks,
metrics.conversions
FROM search_term_view
WHERE segments.date DURING LAST_30_DAYS
AND metrics.conversions = 0
AND metrics.cost_micros > 0
ORDER BY metrics.cost_micros DESC
Keywords carrying a weak quality signal
Spend on a low quality score keyword is spend at a premium: you pay more per click for a worse position. This query joins each keyword's cost to its current quality score and the three component scores behind it, so you can separate a landing-page problem from an ad-relevance one. Sort your attention to the keywords that spend the most while scoring the lowest.
SELECT
ad_group_criterion.keyword.text,
campaign.name,
ad_group_criterion.quality_info.quality_score,
ad_group_criterion.quality_info.creative_quality_score,
ad_group_criterion.quality_info.post_click_quality_score,
ad_group_criterion.quality_info.search_predicted_ctr,
metrics.cost_micros
FROM keyword_view
WHERE segments.date DURING LAST_30_DAYS
AND metrics.cost_micros > 0
ORDER BY metrics.cost_micros DESC
A note on honesty here: there is no segments field that gives you a day-by-day historical quality score, so this reads the current value on the criterion. Google does expose metrics.historical_quality_score as a separate metric if you want the aggregate historical figure, but the component scores above (creative_quality_score, post_click_quality_score, search_predicted_ctr) are the ones that actually tell you what to fix, so they are what this query selects.
Display placements that took budget and returned nothing
On Display and video, the equivalent leak is the placement: an app, a channel or a parked domain that serves impressions, takes clicks and converts no one. This query lists the managed-network placements that cost money and drove no conversions over the window. Mobile-game apps and generic content-farm domains are the usual suspects.
SELECT
group_placement_view.display_name,
group_placement_view.target_url,
group_placement_view.placement_type,
campaign.name,
metrics.cost_micros,
metrics.conversions
FROM group_placement_view
WHERE segments.date DURING LAST_30_DAYS
AND metrics.conversions = 0
AND metrics.cost_micros > 0
ORDER BY metrics.cost_micros DESC
Campaigns spending while conversion tracking is silent
The most expensive leak is the one that looks like every other campaign: it spends normally, but its conversions column reads zero, because a tag broke, a goal was unlinked, or the campaign was never tracked at all. This query lists any campaign that spent over the window and recorded no conversions. A single row here is worth checking before any of the line-item waste above.
SELECT
campaign.name,
campaign.advertising_channel_type,
campaign.status,
metrics.cost_micros,
metrics.conversions
FROM campaign
WHERE segments.date DURING LAST_30_DAYS
AND metrics.conversions = 0
AND metrics.cost_micros > 0
ORDER BY metrics.cost_micros DESC
How to run these
Any GAQL runner takes these as-is. Three common routes:
- The Google Ads API: send the query as the
queryfield toGoogleAdsService.SearchStream(orSearchfor smaller result sets), against your customer id. - The official interactive Query Builder in the Google Ads API docs: paste a query in to validate the fields and see the shape of the response before you wire it into anything.
- A Google Ads Script: pass the query string to
AdsApp.search(query)and iterate the rows, for examplewhile (rows.hasNext()) { var row = rows.next(); }. This keeps the whole thing inside the account with no API credentials.
What these queries will not tell you
Zero last-click conversions is not zero value. These queries deliberately use metrics.conversions, the last-click count, so an assisting term or an upper-funnel placement can look like pure waste when it is doing quieter work; cross-check anything material against your attribution and your total conversions before you act. They also read only what the account will report: Performance Max hides most of its search terms and placements, so run these across your Search and Display campaigns and treat PMax as a separate, thinner picture. Finally, GAQL has no COUNT, so the "how many campaigns" style question is answered by counting the rows the query returns, not by the query itself.