ads-system.store

AdsApp.search vs report vs Selectors: Which to Use

Selectors are the only layer that can write to an account but cover limited campaign types and cap at 50,000 results. AdsApp.search() is the default read layer: GAQL, all channel types, no entity limits. AdsApp.report() is worth keeping only for exportToSheet().

There are three ways to get data out of an account inside a script, and they are not interchangeable. Choosing wrong is the difference between a report that is complete and a report that quietly omits a third of the account.

The comparison

Selectors AdsApp.search() AdsApp.report()
Language Method chaining GAQL AWQL (legacy)
Can write Yes No No
Entity limits 50,000 per iterator Not subject to them Not subject to them
Performance Max Only via the dedicated selector Yes, via FROM campaign Yes
Speed on large accounts Slow Fast Fast
Export to Sheets Manual Manual exportToSheet() built in

Selectors: the write layer

Selectors return live objects. That is their entire advantage: campaign.pause(), keyword.setMaxCpc(), ad.applyLabel(). Nothing else in the scripting environment can change the account.

Their cost is coverage and speed. AdsApp.campaigns() returns Search and Display campaigns only. Shopping, Video and Performance Max each have their own selector, and Demand Gen has none at all — those campaigns are reachable through reporting only. A loop that iterates AdsApp.campaigns() and calls itself an account-wide check is wrong in most modern accounts.

AdsApp.search(): the default read

This is where new scripts should read from. It speaks GAQL, it matches the field names in the Google Ads API documentation, it covers every campaign type through FROM campaign, and it is exempt from entity limits.

The friction is that you get nested objects in camelCase, everything numeric wants a Number() wrapper, and there is no built-in spreadsheet export.

AdsApp.report(): the one thing it still does better

report() speaks AWQL, the older query language, and it has exportToSheet(). If the entire job is “run a query, put it in a tab of this spreadsheet”, that one method saves twenty lines of cell writing and handles the sizing for you.

For anything else, prefer search(). AWQL field names diverge from the API documentation, which means every question you have will be answered in the wrong vocabulary.

A rule you can apply without thinking

  • Need to change something? Selector, fetched narrowly, by ID or exact name.
  • Need numbers across the account? AdsApp.search() with FROM campaign.
  • Need those numbers in a Sheet and nothing more? AdsApp.report() with exportToSheet().

Most non-trivial scripts use two of the three. Read wide with a query, decide in JavaScript, then fetch and modify only the handful of entities that need changing. That keeps the write list short enough to log, and a short log is what makes a change reversible.

The failure this pattern prevents is covered in the Performance Max guide.

Questions

Is AdsApp.report() deprecated?
It still works and still has exportToSheet(), which AdsApp.search() lacks. For new scripts, search() is the better default because it uses GAQL and matches the API documentation.
Which layer can pause a campaign?
Only selectors. AdsApp.search() and AdsApp.report() are read-only.
Do selectors cover every campaign type?
No. AdsApp.campaigns() covers Search and Display. Shopping, Video and Performance Max have dedicated selectors, and some newer types such as Demand Gen are reachable through reporting only.