Google just turned its Ads API Developer Assistant into a full AI agent that can write queries, debug scripts, and scaffold campaign automations for you — but only if you know how to prompt it and where its limits are. I’ve been running it against real client accounts for the past few weeks, and the difference between “toy demo” and “production-ready code” comes down to how you frame the request and how aggressively you validate what it spits back.
This is a hands-on walkthrough. I’ll show you the prompts that produce working GAQL, the ones that produce garbage, and the automation patterns I’ve actually shipped. If you’re an analytics manager, PPC engineer, or developer wiring Google Ads data into GA4, Looker, or Slack, this is the guide I wish someone had written the week the upgrade dropped.
What Actually Changed in the Developer Assistant
The old Developer Assistant was a glorified doc search. You typed a question, it returned a paragraph from the Google Ads API reference. Useful for lookups, useless for actual code.
The upgraded agent does three things the old one couldn’t:
- Generates full GAQL queries from natural language, including nested field selection,
WHEREclauses with segment filters, and date range logic. - Writes runnable client library code in Python, Java, .NET, PHP, Ruby, and Node — not just snippets, but the full mutate operation with the correct request/response objects.
- Debugs your errors when you paste in a stack trace or an API error response. It maps error codes like
AuthenticationError.CUSTOMER_NOT_FOUNDorQueryError.INVALID_QUERYto the actual cause about 70% of the time.
The important shift is that it holds context across a conversation. You can say “now add a filter for campaigns with more than $500 spend last week” and it modifies the previous query. That’s the difference between an agent and a chatbot.
What it still can’t do: execute code, hit your account directly, or read your MCC structure. It’s a code generation and debugging assistant, not an autonomous agent that runs campaigns. Anyone selling it as the latter is lying.
Setting Up Access and Authentication
Access to the Developer Assistant lives inside the Google Ads API documentation portal. You need a Google account with an existing developer token to get the full experience — without one, the assistant will still respond, but it can’t reference your account structure or validate your customer ID against a real MCC.
Here’s the sequence I use for new client onboarding:
- Confirm the developer token status in your MCC under Tools → API Center. You need at least Basic access to run anything the assistant generates against a live account.
- Set up OAuth2 credentials in Google Cloud Console. Create a project, enable the Google Ads API, and generate an OAuth client ID of type “Desktop app” for local scripts or “Web application” for anything server-based.
- Generate a refresh token using the
generate_user_credentials.pyscript from the google-ads-python library. This is the step where most people burn an afternoon — the OAuth playground shortcut works, but only if you add the correct scope:https://www.googleapis.com/auth/adwords. - Store credentials in a
google-ads.yamlfile (never in your repo). Minimum fields:
developer_token: YOUR_DEV_TOKEN
client_id: YOUR_CLIENT_ID.apps.googleusercontent.com
client_secret: YOUR_CLIENT_SECRET
refresh_token: YOUR_REFRESH_TOKEN
login_customer_id: 1234567890 # MCC ID, no dashes
use_proto_plus: true
The assistant will happily generate code that assumes you’ve done all this. It won’t tell you if your token is still in Test access mode, which is where 90% of “the code works but returns nothing” bugs come from.
Five Prompts That Generate Working Code
The prompts below are the ones I’ve tested against v17 of the Google Ads API. I’ll show you the prompt, a note on what to check, and a sample of what the agent typically produces.
1. Automated Bid Adjustments Based on ROAS
Prompt:
Write a Python function using the google-ads library that pulls all enabled search campaigns for a given customer_id, calculates 7-day ROAS per campaign, and updates the tCPA target to 90% of current if ROAS is below 2.0, or 110% if above 4.0. Use MutateOperation and log every change to a CSV.
What the agent gets right: the GAQL query construction, the CampaignService mutate call structure, and the correct bidding_strategy_type filter.
What you need to fix: it often defaults to modifying target_cpa.target_cpa_micros on the campaign directly, which only works if the campaign uses a standard (not portfolio) bidding strategy. If the campaign uses a portfolio strategy, you need to mutate the BiddingStrategy resource instead. The assistant doesn’t catch this unless you specify it.
2. Search Terms Report with Wasted Spend Flag
Prompt:
Generate a GAQL query that returns search_term_view.search_term, campaign.name, ad_group.name, metrics.cost_micros, metrics.conversions, and metrics.clicks for the last 30 days, filtered to search terms with more than 20 clicks and zero conversions. Order by cost descending.
Result — usually correct on the first try:
SELECT
search_term_view.search_term,
campaign.name,
ad_group.name,
metrics.cost_micros,
metrics.conversions,
metrics.clicks
FROM search_term_view
WHERE segments.date DURING LAST_30_DAYS
AND metrics.clicks > 20
AND metrics.conversions = 0
ORDER BY metrics.cost_micros DESC
LIMIT 500
Divide cost_micros by 1,000,000 in your app layer, not in GAQL. GAQL doesn’t support arithmetic in the SELECT clause and the assistant occasionally forgets this and produces invalid queries.
3. Performance Max Asset Group Export
Prompt:
Write a Python script that exports all Performance Max asset groups for a customer_id, including the asset group name, status, campaign name, and each associated asset’s type (HEADLINE, DESCRIPTION, IMAGE, VIDEO) and text or URL. Output as JSON.
This is where the assistant genuinely saves time. PMax asset structure is a pain — you’re joining asset_group, asset_group_asset, and asset across three separate queries. The agent produces a working three-query pattern with an in-memory join. Verify the asset field mapping: asset.text_asset.text for headlines/descriptions, asset.image_asset.full_size.url for images, asset.youtube_video_asset.youtube_video_id for video.
4. Offline Conversion Upload from Shopify Orders
Prompt:
Write a Python function that takes a list of dicts with keys gclid, conversion_time, order_value_gbp, and order_id, and uploads them as offline conversions to a specific conversion action ID. Include retry logic for partial failures.
The agent handles the ConversionUploadService.upload_click_conversions call correctly, including the conversion action resource name format (customers/{cid}/conversionActions/{action_id}). What it gets wrong: the timestamp format. Google Ads requires ISO 8601 with a timezone offset, like 2024-11-15 14:30:00+00:00. If you pass a naive Python datetime, the upload silently succeeds but the conversion never appears. Ask the agent explicitly to format timestamps with strftime('%Y-%m-%d %H:%M:%S%z').
If you’re pulling those Shopify orders directly, our Shopify service covers the order-to-gclid attribution pipeline that makes this actually work.
5. Budget Pacing Alert
Prompt:
Write a script that checks daily spend against monthly budget for each campaign, calculates projected end-of-month spend based on the current pace, and returns any campaigns projected to overspend by more than 15% or underspend by more than 20%.
The agent produces reasonable pacing math but tends to assume campaign_budget.amount_micros is the monthly budget. It’s actually the daily budget. You have to explicitly tell it: “multiply daily budget by days in current month to get monthly budget.” Once you do, the output is production-ready.
Combining the Agent with n8n or Python for Scheduled Reporting
The agent generates code. It doesn’t run it. To ship anything useful, you need a scheduler and a destination. I use two patterns depending on the client stack.
Pattern A: n8n for non-developer teams
n8n has a Google Ads node, but it’s limited to a handful of prebuilt operations. For custom GAQL, I use the HTTP Request node with the OAuth2 credential type. The workflow looks like this:
- Cron trigger — fires daily at 8am.
- HTTP Request node — POSTs to
https://googleads.googleapis.com/v17/customers/{customer_id}/googleAds:searchStreamwith the GAQL query in the body. - Code node — parses the response, converts
cost_microsto currency, flags anomalies. - Slack node — posts a formatted summary to a channel.
- Google Sheets node — appends the full dataset for historical tracking.
The assistant is useful in step 3 — paste in the raw API response and ask “write a JavaScript function for n8n that flattens this into rows with cost in GBP.” It handles the nested response structure correctly.
Pattern B: Python + cron for engineering teams
For clients with a data engineering function, I skip n8n and run a scheduled Python job on a lightweight VM or Cloud Run. The pattern:
from google.ads.googleads.client import GoogleAdsClient
import pandas as pd
from slack_sdk import WebClient
import os
def daily_campaign_report(customer_id: str):
client = GoogleAdsClient.load_from_storage("google-ads.yaml")
ga_service = client.get_service("GoogleAdsService")
query = """
SELECT campaign.name, metrics.cost_micros,
metrics.conversions, metrics.conversions_value
FROM campaign
WHERE segments.date DURING YESTERDAY
AND campaign.status = 'ENABLED'
"""
stream = ga_service.search_stream(customer_id=customer_id, query=query)
rows = []
for batch in stream:
for row in batch.results:
rows.append({
"campaign": row.campaign.name,
"cost_gbp": row.metrics.cost_micros / 1_000_000,
"conversions": row.metrics.conversions,
"revenue_gbp": row.metrics.conversions_value,
})
df = pd.DataFrame(rows)
df["roas"] = df["revenue_gbp"] / df["cost_gbp"].replace(0, pd.NA)
slack = WebClient(token=os.environ["SLACK_TOKEN"])
summary = df.to_markdown(index=False, floatfmt=".2f")
slack.chat_postMessage(
channel="#ads-reporting",
text=f"*Yesterday's campaign performance*\n```{summary}```"
)
return df
if __name__ == "__main__":
daily_campaign_report("1234567890")
That’s roughly 90% agent-generated. I asked for the GAQL, the streaming loop, and the Slack formatter separately, then stitched them together. The one thing I always rewrite by hand is the error handling — the agent’s default try/except blocks catch too broadly and hide auth failures.
For pushing this data into GA4 as offline events or into a warehouse for BI, see our GA4 setup service — the schema mapping between Google Ads metrics and GA4 dimensions is where most integrations quietly break.
Where the Agent Hallucinates
This is the section every practitioner needs and no one else is writing. Below is a table of hallucinations I’ve caught in the past month, categorised by type.
| Hallucination | Frequency | Impact |
|---|---|---|
References deprecated expanded_text_ad when you ask for search ads | High | Code runs but creates nothing usable |
Uses v14 or v15 endpoints in URLs when the current version is v17 | Medium | 404 errors on request |
Invents field names like campaign.total_budget (doesn’t exist) | Medium | GAQL validation error |
Suggests metrics.cost instead of metrics.cost_micros | High | Query fails silently or returns wrong values |
Generates OAuth flow using deprecated oauth2client library | Low | Runs but library is unmaintained |
Claims search_term_view supports WHERE campaign.id IN (...) filtering | Medium | Query returns empty results |
Suggests conversion_action.category values that don’t exist in the enum | Low | Mutate operation rejected |
The pattern: the agent is most accurate on stable, well-documented endpoints (campaign reports, ad group management, budget queries) and least accurate on newer features (PMax asset groups, Demand Gen campaigns, offline conversion adjustments).
How to validate before deploying
Three checks, in order:
- Run the GAQL through the Query Validator in the official docs. If a field doesn’t exist, this catches it in seconds.
- Test against a sandbox or test account first. Every dev token gets a test account by default. Run mutate operations there before you touch production.
- Diff the generated code against the current client library changelog. If the agent uses a method signature that changed in the last two versions, you’ll see it immediately.
I keep a validate_agent_output.py script that runs a query through search_stream with validate_only=True — the API returns any errors without executing the query. If you’re generating dozens of queries a week, wire this into your dev loop.
Common Mistakes and Troubleshooting
Mistake 1: Trusting the agent’s cited field descriptions. It sometimes paraphrases the official docs incorrectly. When it says “metrics.conversions represents unique conversions,” check the reference — that field is total conversions, not unique. Always verify against the Google Ads API field reference.
Mistake 2: Not specifying the API version in your prompt. The agent defaults to whatever version its training data biases toward, which is not always current. Every serious prompt should include “using Google Ads API v17” (or whichever version you’re on).
Mistake 3: Asking for “the best” bidding strategy or campaign structure. The agent will give you a confident answer based on generic best practices. It doesn’t know your account, your margins, or your seasonality. Treat strategy questions as brainstorming, not recommendations.
Mistake 4: Copy-pasting code without checking the client library imports.
The Python library moved from google.ads.google_ads to google.ads.googleads a few versions back. The agent occasionally still uses the old import path. If your IDE flags an import as unresolved, that’s usually why.
Mistake 5: Assuming the agent understands MCC vs. client account context.
When generating code that uses login_customer_id, the agent doesn’t always distinguish between the MCC ID and the operating account ID. Be explicit in your prompt: “login_customer_id is the MCC, customer_id is the child account we’re querying.”
Mistake 6: Not testing offline conversion uploads in a test conversion action first. Uploads to production conversion actions can’t be reversed. If the agent’s code has a timezone bug or a value scaling issue, you’ll pollute your conversion data. Create a test conversion action, upload against it, verify in the UI, then swap the ID.
For teams building larger orchestration around this — say, a full AI-driven reporting layer that combines Ads, GA4, and Shopify data — we cover that architecture in our AI agents service. The Developer Assistant is one component of a working stack, not the whole thing.
Key Takeaways
- The upgraded Google Ads API Developer Assistant is a genuine productivity multiplier for GAQL query building, client library code generation, and API error debugging — but it’s a code assistant, not an autonomous agent.
- Prompt specificity is everything: name the API version, name the client library, name the exact fields you want, and specify the bidding strategy type when relevant.
- Validate every generated GAQL query through the official Query Validator and every mutate operation through
validate_only=Truebefore running against production accounts. - The agent hallucinates most on newer features (PMax, Demand Gen, offline adjustments) and least on stable reporting endpoints — weight your trust accordingly.
- Combine the agent with n8n for non-developer teams or scheduled Python jobs for engineering teams; the agent writes the code, your scheduler runs it, and your validation layer keeps it honest.
- Never trust the agent’s timestamp formatting,
cost_microsconversions, or bidding strategy assumptions without a manual check — these are the three highest-cost failure modes I’ve seen.
Share this article