Google Ads
Overview
The Google Ads Core API exposes Google Ads API reads and the three campaign mutations — accounts, campaign insights, Search impression share, hourly metrics, and Performance Max asset groups.
| Base path | /api/core/google-ads |
| Auth | the calling organization's own stored Google OAuth credentials, resolved from its google_ads integration, plus Analify's server-side developer token. You never pass either yourself |
| Every request | requires ?organizationId=<uuid> |
This surface is shaped differently from Meta and Shopify. Google Ads has no REST resource per object — everything is a GAQL query against googleAds:searchStream. Each read route below therefore mirrors one specific, named GAQL query, not an arbitrary endpoint. There is deliberately no "run any GAQL" route: an open query surface would let a caller select fields the response contract never promised.
Choosing an account. Calls default to the customer id stored on the integration. An org whose OAuth grant reaches several customers can target any of them with ?customerId= (no dashes). Discover the reachable set with GET /api/core/google-ads/accounts.
Money is in micros, not piasters. Google Ads reports cost as micro-units of the account currency — divide by 1,000,000. campaigns/budget likewise takes amountMicros.
API Endpoints
| Endpoint | Google Real Endpoint / GAQL | Description |
|---|---|---|
GET /accounts | GET /customers:listAccessibleCustomers | Customer ids the OAuth grant can reach |
GET /accounts/info | GAQL customer | Account name, currency, timezone |
GET /campaigns/insights | GAQL campaign + metrics | Daily campaign metrics |
GET /campaigns/impression-share | GAQL Search impression share | Impression share (SEARCH campaigns only) |
GET /insights/hourly | GAQL segments.hour | Hour-of-day metric rows |
GET /asset-groups/assets | GAQL asset thumbnails | PMax asset thumbnails (account-wide, no date filter) |
GET /asset-groups/strength | GAQL asset-group ad strength | PMax asset-group ad strength |
GET /asset-groups/asset-insights | GAQL per-asset metrics | Per-asset impressions |
POST /campaigns/pause | googleAds:mutate (status → PAUSED) | Pause a campaign |
POST /campaigns/resume | googleAds:mutate (status → ENABLED) | Resume a campaign |
POST /campaigns/budget | googleAds:mutate (amount_micros) | Change a campaign budget |
Accounts
GET /api/core/google-ads/accounts
GET /api/core/google-ads/accountsThe raw listAccessibleCustomers response — every customer id the organization's OAuth grant can reach. Use one of these as ?customerId= on the routes below.
curl "https://analify.scalyax.ai/api/core/google-ads/accounts?organizationId={organizationId}" \
-H "Authorization: Bearer {anlfy_token}"Reads
All date-scoped reads take date_from and date_to (YYYY-MM-DD).
curl -G "https://analify.scalyax.ai/api/core/google-ads/campaigns/insights" \
-H "Authorization: Bearer {anlfy_token}" \
--data-urlencode "organizationId={organizationId}" \
--data-urlencode "date_from=2026-08-01" \
--data-urlencode "date_to=2026-08-16"Targeting a specific customer:
curl -G "https://analify.scalyax.ai/api/core/google-ads/campaigns/insights" \
-H "Authorization: Bearer {anlfy_token}" \
--data-urlencode "organizationId={organizationId}" \
--data-urlencode "customerId=1234567890" \
--data-urlencode "date_from=2026-08-01" \
--data-urlencode "date_to=2026-08-16"Each read returns the raw GAQL result rows, exactly as searchStream produced them — nested under Google's own field names (campaign.id, metrics.costMicros, segments.date, …).
Two reads with their own rules
impression-sharecovers SEARCH campaigns only. Google does not expose impression-share metrics for Performance Max or Display, so those campaigns are absent from the result rather than returned as zero.asset-groups/assetsis account-wide and takes no date filter — it returns asset thumbnails as they currently exist, not a windowed report. Passingdate_from/date_tohas no effect.
GET /api/core/google-ads/insights/hourly
GET /api/core/google-ads/insights/hourlyMirrors the segments.hour GAQL — one row per hour of day, for day-parting analysis.
Mutations
All three take a resource name, not a bare id — Google's own identifier form, as returned by the reads above.
# Pause a campaign
curl -X POST "https://analify.scalyax.ai/api/core/google-ads/campaigns/pause?organizationId={organizationId}" \
-H "Authorization: Bearer {anlfy_token}" -H "Content-Type: application/json" \
-d '{ "campaignResourceName": "customers/1234567890/campaigns/9876543210" }'
# Change a budget — amountMicros, NOT piasters and NOT major units
curl -X POST "https://analify.scalyax.ai/api/core/google-ads/campaigns/budget?organizationId={organizationId}" \
-H "Authorization: Bearer {anlfy_token}" -H "Content-Type: application/json" \
-d '{
"campaignBudgetResourceName": "customers/1234567890/campaignBudgets/5555555555",
"amountMicros": 10000000
}'amountMicros: 10000000 is 10 units of the account currency (10,000,000 ÷ 1,000,000). Getting this wrong by a factor of a million is the single most expensive mistake available on this page. Budget, pause and resume all change a live account immediately and require an API token with the write scope.
A budget is a separate object from the campaign, and budgets can be shared across campaigns. Changing one may affect more than the campaign you started from — read campaign.campaignBudget first.
Error Shape
| Code | Status | Meaning |
|---|---|---|
GOOGLE_ADS_NOT_CONNECTED | 400 | The organization has no connected Google Ads integration |
GOOGLE_ADS_RECONNECT_REQUIRED | 401 | The stored OAuth grant is dead — the user must reconnect |
GOOGLE_ADS_CREDENTIALS_ERROR | 400 | Credential resolution failed for another reason |
GOOGLE_ADS_API_ERROR | 502 | Any failure reported by the Google Ads API, carrying Google's own message |
GOOGLE_ADS_UNKNOWN_ERROR | 500 | A non-platform failure inside the route |
{
"success": false,
"error": {
"message": "The customer account can't be accessed because it is not yet enabled or has been deactivated.",
"code": "GOOGLE_ADS_API_ERROR"
}
}Updated about 2 months ago
