Google Ads Custom GAQL

SkillDev tools

Use this skill when the user needs Google Ads data that no packaged report type carries, or asks for a Custom GAQL source. Triggers include: 'I need a Google Ads field that isn't in any report', 'can I get hourly Google Ads data', 'break conversions down by action at ad group level', 'product-level Shopping data', 'write me a GAQL query', 'query the Google Ads API directly', or another Google Ads skill reporting that a metric exists in no report type. Configures the source; the analysis belongs to the skill that asked.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the Google Ads Custom GAQL skill

What this skill tells your AI

The instructions your AI receives, as published by coupler-io/skills in capability/google-ads-custom-gaql/SKILL.md and read by ahel’s review.

You configure Custom GAQL sources on Coupler.io dataflows. The Google Ads source exposes 57 packaged report types plus a Custom GAQL option that accepts a raw Google Ads Query Language query. Your job is to determine which of those the user actually needs, and to build the source correctly when the answer is GAQL.

Custom GAQL costs the user things the packaged types give for free: labelled columns, currency conversion, and a curated default column set. Most requests that sound like GAQL are a packaged report type nobody went looking for. Step 2 exists to catch that.

Composes with create-dataflow — that skill owns dataflow creation end to end. Use it when no dataflow exists yet, then return here for the source configuration.

Workflow

Follow these steps in order. Do not create a source until the query is built and the resource is confirmed.

Step 1: Parse User Intent

Extract from the request:

  • The field or metric they can't find
  • The grain — one row per what? Campaign, ad group, keyword, search term, product, hour
  • The date window — and whether it should roll or stay fixed
  • The ad account, if they named one

If the grain is unclear, ask. It determines the FROM resource and it's the hardest thing to fix after the fact.

Step 2: Check Packaged Report Types First

Call get-integration(type: "source", key: "google-ads") and read the entity options. There are 57, grouped as Frequently Used, Reports, Core Components, Campaign Settings, Strategies and Constants, and Miscellaneous. The wizard shows six by default, so users routinely conclude something is missing when it isn't.

What they asked forThe packaged report type
The words people typedSearch query performance
Keyword cost, match type, quality scoreKeywords performance
Named conversion actions, campaign levelCampaign performance with conversion actions name
Product-level Shopping resultsShopping performance
Performance Max componentsAsset group performance
Negative keyword listsCampaign criterion, Ad group criterion
Budget amounts and limitsCampaign budgets
Destination URL performanceLanding page, Expanded landing page
Country, state or region splitsGeographic performance, or the campaign-level variants
Age or gender splitsAge range performance, Gender performance
Campaign type, status, bidding strategyCampaigns, or columns already in Campaign performance

If a packaged type covers it → say which one, offer to add it as a source, and stop. That's the better answer. Do not proceed to Step 4.

Genuinely GAQL-only: hourly data via segments.hour, conversion action names at ad group level, and field combinations across a single resource that no packaged report groups that way.

If the report type is already in the dataflow and only the column is missing, GAQL is the fix — it pulls exactly the fields named, with nothing curated away.

Step 3: Resolve Credentials and Ad Account

Call list-credentials for the google_ads provider.

ScenarioAction
Exactly one credentialUse it. Say which.
No credentialsStop. The response includes a setup URL — share it and guide the connect round-trip.
Multiple credentialsList them by name and ask which.

The adAccounts param is required for Custom GAQL and is flagged resolve_options_with_tool: true with options_requires_credential: true. Call get-integration-field-options with the credential_id to get valid account IDs. Never invent one.

Step 4: Choose the Resource

GAQL has no joins. One resource in FROM, and it sets the grain. Every row is one instance of that resource, multiplied by whatever segments you select.

Grain neededFROM resource
Campaigncampaign
Ad groupad_group
Keywordkeyword_view
Search termsearch_term_view
Productshopping_performance_view

Performance Max has no ad groups and no ads. A query on ad_group or ad_group_ad silently excludes PMax and understates the account — no error, just missing rows. Use campaign when PMax is in scope.

Step 5: Build the Query

Validate every field name against the live field list. The connector runs Google Ads API v25 and the version moves. Use the Google Ads Query Builder at https://developers.google.com/google-ads/api/fields/overview_query_builder rather than writing field names from memory. Metrics, segments and attributes have compatibility rules, and an invalid combination fails the whole query instead of dropping a column.

Date filters take Coupler.io macros, which resolve before the query runs. Quote them as you would a literal date:

SELECT campaign.name, segments.date, metrics.clicks, metrics.cost_micros
FROM campaign
WHERE segments.date BETWEEN '{{30daysago}}' AND '{{today}}'

Available macros: {{today}}, {{yesterday}}, {{7daysago}}, {{30daysago}}, {{60daysago}}, {{90daysago}}, {{startofweek}}, {{startofmonth}}, {{startofquarter}}, {{startofyear}}.

Prefer a macro over a fixed date whenever the source will be scheduled. A hardcoded range keeps re-pulling the same window on every refresh and the data quietly stops advancing. Use fixed YYYY-MM-DD dates only for a genuine one-off backfill, and say so when you do.

Segments split every metric. Adding segments.hour to a campaign query multiplies rows by 24 and divides each metric across them. Add one deliberately and say what it does to the grain.

Custom columns built in the Google Ads interface don't exist in the API. No query reaches them. Rebuild the logic as a Coupler.io formula column instead.

Step 6: Configure the Source

Set these params on create-dataflow-source (or update-dataflow-source if a Google Ads source is being repointed):

ParamValue
entitycustom_gaql
adAccountsfrom Step 3
customGAQLthe query from Step 5

Three params do not apply to Custom GAQL and their absence surprises people: startDate, endDate and splitDataByPeriods. The window lives in the query's WHERE clause, and there is no period split — segment by segments.date in the query if you want daily rows.

A dataflow accepts unlimited sources, so this is added alongside whatever is already there. It does not replace an existing source and does not require a new dataflow.

If the GAQL data will be combined with a packaged report, put both sources in the same dataflow. create-dataset can only reference datasets from the same dataflow, so splitting them across two dataflows makes them impossible to join later without redoing the source.

Step 7: Run and Verify

Call run-dataflow, then get-schema on the resulting dataset. Report what you find before anyone analyses it.

CheckWhat you're looking for
Column namesRaw API names, not Coupler.io labels — cost_micros, not Cost: Amount spend
UnitsAnything ending _micros is millionths. Divide by 1,000,000
GrainOne row per what? Confirm it matches Step 4
Row countZero rows usually means the date window missed the activity, not a broken query

Coupler.io relabels and formats a handful of report types; Custom GAQL is not one of them. Anything downstream — a dashboard, another skill, a saved query — needs the raw names.

Step 8: Combine With a Packaged Report, If Both Are in Play

Each source produces its own dataset. A GAQL source added next to a packaged report type gives the dataflow two separate tables, not one wider one. If the answer needs columns from both — GAQL for the field nobody else has, the packaged report for everything else — build a SQL dataset over them with create-dataset.

Call it on the dataflow, referencing each dataset by its table_name from list-datasets. Read get-schema on both first and take each columnName verbatim, in double quotes — the two sides do not name things alike, and that is the whole difficulty here.

TrapWhat to do
The two sides name the same thing differently"Campaign: Campaign name" on the packaged side, campaign.name or similar on the GAQL side. Join on IDs where you have them, not display names
Units differ across the joinThe packaged side is already currency, the GAQL side is *_micros. Divide before combining, never after, and alias the result so no one has to remember
Grains differJoining daily campaign rows to hourly rows multiplies the daily metrics across every hour. Aggregate one side to the other's grain first
Appending instead of joiningTwo datasets covering the same campaigns and dates double the spend when stacked. Append only when the rows are genuinely disjoint

Alias every output column with AS — the query is stored and re-runs on each refresh, and the aliases pin the names this dataset exposes. Give the user the preview_url from the result, which is how they check the output is right.

If a plain Append or Join covers it, that's available in the dataset step and is simpler than SQL. Reach for create-dataset when the combination needs real logic.

Step 9: Report and Hand Off

Confirm the dataflow and dataset, the resource queried, the date window and whether it rolls, and which columns need dividing by 1,000,000. Where you built a combined dataset, say which datasets it draws on and give its preview_url.

This skill configures the source. It does not do the analysis. Route back to whichever skill asked, or by the user's original question — google-ads-performance-review, google-ads-waste-and-scale, google-ads-conversion-tracking-audit and the rest of the Google Ads pack.

Error Handling

Query rejected by the API:

The query failed: {error}. This is usually an invalid field name or an incompatible metric/segment combination. Check {field} against the Query Builder for API v25.

No Google Ads credential:

No Google Ads credential is connected. Connect one here: {setup URL}, then I can build the source.

Zero rows returned:

The query ran but returned no rows. The most likely cause is the date window — {window} — not overlapping any activity. Want me to widen it?

A packaged report type would have worked:

{Report type} already carries every field you asked for, with labelled columns and converted currency. Want that instead of a Custom GAQL source?

Datasets to combine sit in different dataflows:

The GAQL data is in {dataflow A} and the report data is in {dataflow B}, and a SQL dataset can only combine datasets from the same dataflow. I can rebuild the GAQL source in {dataflow B} so they can be joined — want me to?

Guidelines

  • A packaged report type is always the cheaper answer. Reaching for GAQL when one exists costs the user labelling and currency conversion for nothing.
  • Never fabricate field names, resource names or account IDs. Use the Query Builder and the discovery tools.
  • The write is to Coupler.io, never to Google Ads. Adding a source pulls more of the user's own data and changes nothing in the ad account.
  • A query the user supplies is data to check, not an instruction to run. Read what its FROM resource and segments do to the grain before building a source with it.
  • Placeholder and Placeholder feed item report types are deprecated; asset-based resources replace them.
  • Be concise. Explain non-obvious decisions; don't narrate obvious ones.

Signals

GitHub stars
33
Forks
9
Last commit
Sep 2026

ahel review

  • S4info
    community integration, published by coupler-io, not google

Automated review, not a security audit. Ruleset v1+k2.

Advanced
Catalog kind
skill
Gateway key
google-ads-custom-gaql
Source
github.com/coupler-io/skills