Google Ads API
Unofficial Google Ads API client library for Node.js
npm install google-ads-api
import { GoogleAdsApi } from "google-ads-api";
const client = new GoogleAdsApi({
client_id: "<CLIENT-ID>",
client_secret: "<CLIENT-SECRET>",
developer_token: "<DEVELOPER-TOKEN>",
});
const customer = client.Customer({
customer_id: "1234567890",
refresh_token: "<REFRESH-TOKEN>",
});
// Also supports login & linked customer ids
const customer = client.Customer({
customer_id: "1234567890",
login_customer_id: "<LOGIN-CUSTOMER-ID>",
linked_customer_id: "<LINKED-CUSTOMER-ID>",
refresh_token: "<REFRESH-TOKEN>",
});
This is a special client method for listing the accessible customers for a given refresh token, and is equivalent to CustomerService.listAccessibleCustomers. It returns the resource names of available customer accounts.
const client = new GoogleAdsApi({
client_id: "<CLIENT-ID>",
client_secret: "<CLIENT-SECRET>",
developer_token: "<DEVELOPER-TOKEN>",
});
const refreshToken = "<REFRESH-TOKEN>";
const customers = await client.listAccessibleCustomers(refreshToken);
import { enums } from "google-ads-api";
const campaigns = await customer.report({
entity: "campaign",
attributes: [
"campaign.id",
"campaign.name",
"campaign.bidding_strategy_type",
"campaign_budget.amount_micros",
],
metrics: [
"metrics.cost_micros",
"metrics.clicks",
"metrics.impressions",
"metrics.all_conversions",
],
constraints: {
"campaign.status": enums.CampaignStatus.ENABLED,
},
limit: 20,
});
If you prefer to use the Google Ads Query Language (GAQL) the query method is available. Internally report uses this function. More GAQL examples can be found here.
const campaigns = await customer.query(`
SELECT
campaign.id,
campaign.name,
campaign.bidding_strategy_type,
campaign_budget.amount_micros,
metrics.cost_micros,
metrics.clicks,
metrics.impressions,
metrics.all_conversions
FROM
campaign
WHERE
campaign.status = "ENABLED"
LIMIT 20
`);
import { enums } from "google-ads-api";
const campaigns = await customer.report({
entity: "ad_group",
metrics: [
"metrics.cost_micros",
"metrics.clicks",
"metrics.impressions",
"metrics.all_conversions",
],
segments: ["segments.date"],
from_date: "2021-01-01",
to_date: "2021-02-01",
});
Calls searchStream internally but returns the rows one by one in an async iterator.
import { enums } from "google-ads-api";
const stream = customer.reportStream({
entity: "ad_group_criterion",
attributes: [
"ad_group_criterion.keyword.text",
"ad_group_criterion.status",
],
constraints: {
"ad_group_criterion.type": enums.CriterionType.KEYWORD,
},
});
// Rows are streamed in one by one
for await (const row of stream) {
// Break the loop to stop streaming
if (someLogic) {
break
}
}
Or use a GAQL query.
const stream = customer.queryStream(`
SELECT
ad_group_criterion.keyword.text,
ad_group_criterion.status
FROM
ad_group_criterion
WHERE
ad_group_criterion.type = "KEYWORD"
`);
// Rows are streamed in one by one
for await (const row of stream) {
// Break the loop to stop streaming
if (someLogic) {
break
}
}
Returns the raw stream so that events can be handled manually. For more information on Google's stream methods please consult their docs.
import { enums, parse } from "google-ads-api";
const reportOptions = {
entity: "ad_group_criterion",
attributes: [
"ad_group_criterion.keyword.text",
"ad_group_criterion.status",
],
constraints: {
"ad_group_criterion.type": enums.CriterionType.KEYWORD,
},
};
const stream = customer.reportStreamRaw(reportOptions);
// Rows are streamed in 10,000 row chunks
stream.on("data", (chunk) => {
const parsedResults = parse({
results: chunk.results,
reportOptions,
});
});
stream.on("error", (error) => {
throw new Error(error);
});
stream.on("end", () => {
console.log("stream has finished");
});
import { resources, enums, ResourceNames } from "google-ads-api";
const ad = new resources.Ad({
expanded_text_ad: {
headline_part1: "Cruise to Mars",
headline_part2: "Best Space Cruise Line",
description: "Buy your tickets now!",
path1: "cruises",
path2: "mars",
},
final_urls: ["https://example.com"],
type: enums.AdType.EXPANDED_TEXT_AD,
});
const adGroup = ResourceNames.adGroup(cus.credentials.customerId, "123");
const adGroupAd = new resources.AdGroupAd({
status: enums.AdGroupAdStatus.PAUSED,
ad_group,
ad,
});
// Returns an array of newly created resource names if successful
const { results } = await cus.adGroupAds.create([adGroupAd]);
import {
resources,
enums,
toMicros,
ResourceNames,
MutateOperation,
} from "google-ads-api";
// Create a resource name with a temporary resource id (-1)
const budgetResourceName = ResourceNames.campaignBudget(
customer.credentials.customer_id,
"-1"
);
const operations: MutateOperation<
resources.ICampaignBudget | resources.ICampaign
>[] = [
{
entity: "campaign_budget",
operation: "create",
resource: {
// Create a budget with the temporary resource id
resource_name: budgetResourceName,
name: "Planet Express Budget",
delivery_method: enums.BudgetDeliveryMethod.STANDARD,
amount_micros: toMicros(500),
},
},
{
entity: "campaign",
operation: "create",
resource: {
name: "Planet Express",
advertising_channel_type: enums.AdvertisingChannelType.SEARCH,
status: enums.CampaignStatus.PAUSED,
manual_cpc: {
enhanced_cpc_enabled: false,
},
// Use the temporary resource id which will be created in the previous operation
campaign_budget: budgetResourceName,
network_settings: {
target_google_search: true,
target_search_network: true,
},
},
},
];
const result = await customer.mutateResources(operations);
import {
resources,
enums,
toMicros,
ResourceNames,
MutateOperation,
} from "google-ads-api";
// Ad Group to which you want to add the keyword
const adGroupResourceName = "customers/123/adGroups/456";
const keyword = "24 hour locksmith harlem";
const operations: MutateOperation<
resources.IAdGroupCriterion & {
exempt_policy_violation_keys?: google.ads.googleads.v23.common.IPolicyViolationKey[];
}
>[] = [
{
entity: "ad_group_criterion",
operation: "create",
resource: {
// Keyword with policy violation exemptions
ad_group: adGroupResourceName,
keyword: {
text: "24 hour locksmith harlem",
match_type: enums.KeywordMatchType.PHRASE,
},
status: enums.AdGroupStatus.ENABLED,
},
exempt_policy_violation_keys: [
{
policy_name: "LOCAL_SERVICES",
violating_text: keyword,
},
],
},
];
const result = await customer.mutateResources(operations);
const clickConversion = {
gclid: "<GOOGLE-CLICK-ID>",
conversion_action: "customers/1234567890/conversionActions/111222333",
conversion_date_time: "2022-01-11 00:00:00",
conversion_value: 123,
currency_code: "GBP",
};
const request = new services.UploadClickConversionsRequest({
customer_id: customerId,
conversions: [clickConversion],
});
await customer.conversionUploads.uploadClickConversions(request);
If a summary row is requested in the report method, it will be included as the first row of the results.
const [summaryRow, ...response] = await customer.report({
entity: "campaign",
metrics: ["metrics.clicks", "metrics.all_conversions"],
search_settings: {
return_summary_row: true,
},
});
If a summery row is requested in the reportStream method, it will be included as the final iterated row of the results.
const stream = customer.reportStream({
entity: "campaign",
metrics: ["metrics.clicks", "metrics.all_conversions"],
search_settings: {
return_summary_row: true,
},
});
const accumulator = [];
for await (const row of stream) {
accumulator.push(row);
}
const summaryRow = accumulator.slice(-1)[0];
The reportCount method acts like report but returns the total number of rows that the query would have returned (ignoring the limit). This replaces the return_total_results_count report option. Hooks are not called in this function to avoid cacheing conflicts.
const totalRows = await customer.reportCount({
entity: "search_term_view",
attributes: ["search_term_view.resource_name"],
});
There are 2 methods of sorting the results of report. The prefered method is to use the order key, which should be an array of objects with a field key and an optional sort_order key. The order of the items in the array will map to the order of the sorting keys in the GAQL query, and hence the priorities of the sorts.
const response = await customer.report({
entity: "campaign",
attributes: ["campaign.id"],
metrics: ["metrics.clicks"],
segments: ["segments.date"],
order: [
{ field: "metrics.clicks", sort_order: "DESC" },
{ field: "segments.date", sort_order: "ASC" },
{ field: "campaign.id" }, // default sort_order is descending
],
});
The other method is to use the order_by and sort_order keys, however this will be deprecated in a future version of the API.
const response = await customer.report({
entity: "campaign",
attributes: ["campaign.id"],
metrics: ["metrics.clicks"],
segments: ["segments.date"],
order_by: "metrics.clicks",
sort_order: "DESC",
});
The library provides a set of helper methods under the ResourceNames export. These are used for compiling resource names from ids. Arguments can be of the type string, number, or a mix of both. If you have a client.Customer instance available, you can get the customer id with customer.credentials.customerId.
import { ResourceNames } from "google-ads-api";
const customerId = "1234567890";
const campaignId = "3218318373";
ResourceNames.campaign(customerId, campaignId);
// "customers/1234567890/campaigns/3218318373"
ResourceNames.adGroup(123, 123);
// "customers/123/adGroups/123"
ResourceNames.adGroupAd("1", "2", "3");
// "customers/1/adGroupAds/2~3"
const amsterdamLocationId = 1010543;
ResourceNames.geoTargetConstant(amsterdamLocationId);
// "geoTargetConstants/1010543"
ResourceNames.accountBudget(customer.credentials.customer_id, 123);
// "customers/1234567890/accountBudgets/123"
The library provides hooks that can be executed before, after or on error of a query, stream or a mutation.
onQueryStartonQueryErroronQueryEndonStreamStartonStreamErrorThese hooks have access to the customerCredentials argument, containing the `custo
$ claude mcp add google-ads-api \
-- python -m otcore.mcp_server <graph>