Skip to content
Engineering · 8 min read

Five things the Google Ads API will not tell you

Queries that look right and return nothing, a metric that defaults to zero instead of null, and an entire report that does not exist in the API.

By the AuditRoger team · Published

Each of these cost us an afternoon, produced a report that looked fine and was wrong, or both. They are written down here because the error messages do not say any of it and neither, in most cases, do the docs.

1. Keyword metrics do not live where keywords live

ad_group_criterion holds the keyword. It will not give you metrics with it. Ask for clicks or cost and the request fails with PROHIBITED_METRIC_IN_SELECT_OR_WHERE_CLAUSE and a message about not supporting the requested resource.

Metrics come from keyword_view, which exposes the same ad_group_criterion attributes and implies the KEYWORD criterion type, so you do not need a type filter either. The fix is one word in the FROM clause and it is not discoverable from the error.

2. Auction Insights does not exist in the API

There is no auction_insight resource. Queries against it fail with BAD_RESOURCE_TYPE_IN_FROM_CLAUSE, and the metrics.auction_insight_* fields are allowlist-only. The report you can see in the Google Ads interface is interface-only.

This one matters for what you promise. A competitor-overlap section in an automated report cannot be built from the API, however much the interface suggests otherwise, and the honest handling is a section that says there is no data rather than one quietly filled with something else.

3. Quality Score returns 0, not null

Keywords Google has not scored — Display and Demand Gen keywords, and anything under the impression threshold — come back with quality_score = 0. That is the proto3 default for an integer, not a value Google has assigned.

Average them in and the damage is silent. On one account a genuinely healthy average of 4.9 across the scored keywords arrived as 0.9 once the unscored zeros were included, and the section graded an F. Nothing errored. The report simply said something false in a confident voice.

The general ruleIn any proto3 API, a missing integer and a zero are the same wire value. Anywhere zero is also a legitimate reading, you need a separate signal for absence — or, as here, a filter that keeps only values inside the valid range.

4. Impression share is a fraction, and most campaigns report nothing

Impression share metrics come back between 0 and 1, so they need multiplying by 100 before they go anywhere near a percentage sign. That part is merely fiddly.

The trap underneath it is that Display, Video, Demand Gen and Performance Max campaigns report 0 for search impression share, because the metric does not apply to them. Average across all campaigns and a healthy search account's score collapses in proportion to how much of its budget is not on Search. Filter to the campaigns that actually report a share.

5. Some fields you would bet on are not selectable

campaign.start_date is not a selectable field and the query fails outright. Pulling account-level fields by joining customer.* onto a campaign query is likewise a bad idea; query FROM customer separately and keep the two apart.

There is no shortcut for this. The field reference is the source of truth and a plausible-looking field name is not evidence that it exists.

The error you get is not the error you have

Stringifying a GoogleAdsException gives you the generic gRPC complaint that the request contains an invalid argument, which is true and useless. The detail is in the failure object: error_code, location.field_path and trigger will name the field and the reason. Unpack those into your logs before you start guessing, because every item on this list is two minutes' work once you can see which field it is and hours of bisecting a SELECT list when you cannot.

Why write this down

Three of these five produced an empty or wrong report rather than a failure, which is the worst kind of bug in reporting software: the numbers are there, the layout is fine, and no one has any reason to doubt it. If you are building against the same API, take the list — and if you would rather not, the API we expose hands back the finished audit.