Google Ads Adapter
The Google Ads adapter provides access to advertising data using OAuth2 authentication and Google Ads Query Language (GAQL).
Installation
npm install @openetl/google-ads
Features
- OAuth2 Authentication - Secure authentication with automatic token refresh
- GAQL Support - Query data using Google Ads Query Language
- Multiple Resources - Access campaigns, ad groups, ads, keywords, and more
- Flexible Queries - Custom GAQL for advanced data retrieval
Authentication
The Google Ads adapter uses oauth2 authentication with additional credentials:
const vault = {
'google-ads-auth': {
id: 'google-ads-auth',
type: 'oauth2',
credentials: {
client_id: process.env.GOOGLE_CLIENT_ID,
client_secret: process.env.GOOGLE_CLIENT_SECRET,
access_token: process.env.GOOGLE_ACCESS_TOKEN,
refresh_token: process.env.GOOGLE_REFRESH_TOKEN,
developer_token: process.env.GOOGLE_ADS_DEVELOPER_TOKEN,
},
},
};
Note: You need a Google Ads developer token. Apply at Google Ads API Center.
Quick Start
import { Orchestrator } from 'openetl';
import { googleAds } from '@openetl/google-ads';
const vault = {
'google-ads-auth': {
id: 'google-ads-auth',
type: 'oauth2',
credentials: {
client_id: process.env.GOOGLE_CLIENT_ID,
client_secret: process.env.GOOGLE_CLIENT_SECRET,
access_token: process.env.GOOGLE_ACCESS_TOKEN,
refresh_token: process.env.GOOGLE_REFRESH_TOKEN,
developer_token: process.env.GOOGLE_ADS_DEVELOPER_TOKEN,
},
},
};
const etl = Orchestrator(vault, { googleAds });
const pipeline = {
id: 'export-campaigns',
source: {
id: 'campaigns-source',
adapter_id: 'google-ads',
endpoint_id: 'campaigns',
credential_id: 'google-ads-auth',
config: {
customer_id: '1234567890', // Your Google Ads customer ID (no dashes)
},
fields: ['campaign.id', 'campaign.name', 'campaign.status', 'metrics.impressions', 'metrics.clicks'],
pagination: { type: 'offset', itemsPerPage: 1000 },
},
};
const result = await etl.runPipeline(pipeline);
console.log(`Exported ${result.data.length} campaigns`);
Endpoints
campaigns
Retrieve campaign data.
| Config Property | Required | Description |
|---|---|---|
customer_id |
Yes | Google Ads customer ID (without dashes) |
const connector = {
adapter_id: 'google-ads',
endpoint_id: 'campaigns',
credential_id: 'google-ads-auth',
config: {
customer_id: '1234567890',
},
fields: [
'campaign.id',
'campaign.name',
'campaign.status',
'campaign.advertising_channel_type',
'metrics.impressions',
'metrics.clicks',
'metrics.cost_micros',
],
};
ad_groups
Retrieve ad group data.
const connector = {
adapter_id: 'google-ads',
endpoint_id: 'ad_groups',
credential_id: 'google-ads-auth',
config: {
customer_id: '1234567890',
},
fields: [
'ad_group.id',
'ad_group.name',
'ad_group.status',
'campaign.id',
'metrics.impressions',
'metrics.clicks',
],
};
ads
Retrieve ad data.
const connector = {
adapter_id: 'google-ads',
endpoint_id: 'ads',
credential_id: 'google-ads-auth',
config: {
customer_id: '1234567890',
},
fields: [
'ad_group_ad.ad.id',
'ad_group_ad.ad.type',
'ad_group_ad.status',
'ad_group.id',
'campaign.id',
'metrics.impressions',
],
};
keywords
Retrieve keyword data.
const connector = {
adapter_id: 'google-ads',
endpoint_id: 'keywords',
credential_id: 'google-ads-auth',
config: {
customer_id: '1234567890',
},
fields: [
'ad_group_criterion.keyword.text',
'ad_group_criterion.keyword.match_type',
'ad_group_criterion.status',
'metrics.impressions',
'metrics.clicks',
],
};
custom_query
Execute a custom GAQL query.
const connector = {
adapter_id: 'google-ads',
endpoint_id: 'custom_query',
credential_id: 'google-ads-auth',
config: {
customer_id: '1234567890',
custom_query: `
SELECT
campaign.id,
campaign.name,
segments.date,
metrics.impressions,
metrics.clicks,
metrics.cost_micros
FROM campaign
WHERE segments.date DURING LAST_30_DAYS
ORDER BY metrics.impressions DESC
LIMIT 100
`,
},
fields: [],
};
Common Fields
Metrics
| Field | Description |
|---|---|
metrics.impressions |
Number of impressions |
metrics.clicks |
Number of clicks |
metrics.cost_micros |
Cost in micros (divide by 1,000,000 for actual cost) |
metrics.ctr |
Click-through rate |
metrics.average_cpc |
Average cost per click |
metrics.conversions |
Number of conversions |
Segments
| Field | Description |
|---|---|
segments.date |
Date segment |
segments.device |
Device type |
segments.ad_network_type |
Network type |
Filtering
Use GAQL WHERE clause in custom queries:
config: {
customer_id: '1234567890',
custom_query: `
SELECT campaign.id, campaign.name, metrics.clicks
FROM campaign
WHERE campaign.status = 'ENABLED'
AND metrics.clicks > 100
`,
}
Complete Example
import { Orchestrator, Pipeline } from 'openetl';
import { googleAds } from '@openetl/google-ads';
import { postgresql } from '@openetl/postgresql';
const vault = {
'google-ads-auth': {
id: 'google-ads-auth',
type: 'oauth2',
credentials: {
client_id: process.env.GOOGLE_CLIENT_ID,
client_secret: process.env.GOOGLE_CLIENT_SECRET,
access_token: process.env.GOOGLE_ACCESS_TOKEN,
refresh_token: process.env.GOOGLE_REFRESH_TOKEN,
developer_token: process.env.GOOGLE_ADS_DEVELOPER_TOKEN,
},
},
'warehouse': {
id: 'warehouse',
type: 'basic',
credentials: {
host: 'localhost',
database: 'analytics',
username: 'etl',
password: process.env.DB_PASSWORD,
},
},
};
const etl = Orchestrator(vault, { googleAds, postgresql });
const pipeline: Pipeline = {
id: 'sync-campaign-metrics',
source: {
id: 'campaigns-source',
adapter_id: 'google-ads',
endpoint_id: 'custom_query',
credential_id: 'google-ads-auth',
config: {
customer_id: process.env.GOOGLE_ADS_CUSTOMER_ID,
custom_query: `
SELECT
campaign.id,
campaign.name,
segments.date,
metrics.impressions,
metrics.clicks,
metrics.cost_micros
FROM campaign
WHERE segments.date DURING LAST_7_DAYS
`,
},
fields: [],
},
target: {
id: 'metrics-target',
adapter_id: 'postgresql',
endpoint_id: 'table_insert',
credential_id: 'warehouse',
config: { schema: 'marketing', table: 'google_ads_campaigns' },
fields: ['campaign_id', 'campaign_name', 'date', 'impressions', 'clicks', 'cost'],
},
error_handling: {
max_retries: 3,
retry_interval: 2000,
fail_on_error: true,
},
};
const result = await etl.runPipeline(pipeline);
console.log(`Synced ${result.data.length} campaign records`);
Transformations
Convert cost_micros to actual cost:
// cost_micros is in millionths of the account currency
// To convert: cost_micros / 1,000,000 = actual cost
source: {
// ...
transform: [
{
type: 'toNumber',
options: { field: 'cost_micros' },
},
],
},
// Then divide by 1000000 in post-processing or target