gads_keyword_performance
Retrieve keyword-level performance data including match type, quality score, clicks, cost, and conversions. Filter by campaign or ad group with default last 28 days and enabled keywords.
Instructions
Keyword-level performance with match type, quality score, clicks, cost, conversions. Filter by campaign_id or ad_group_id. Default last 28 days, enabled keywords.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| start_date | No | 28daysAgo | |
| end_date | No | yesterday | |
| customer_id | No | ||
| limit | No | ||
| campaign_id | No | Filter to a specific campaign ID | |
| ad_group_id | No | Filter to a specific ad group ID | |
| status | No | ENABLED |
Implementation Reference
- src/index.ts:103-108 (registration)Registration of the 'gads_keyword_performance' tool with the MCP server. Maps the tool name to keywordPerformanceSchema and keywordPerformance handler.
server.tool( "gads_keyword_performance", "Keyword-level performance with match type, quality score, clicks, cost, conversions. Filter by campaign_id or ad_group_id. Default last 28 days, enabled keywords.", keywordPerformanceSchema, async (args) => { try { return ok(await keywordPerformance(args)); } catch (e) { return err(e); } } ); - src/tools/keywords.ts:11-16 (schema)Zod schema for keyword performance inputs: date range, campaign_id, ad_group_id, and status filter (default ENABLED).
export const keywordPerformanceSchema = { ...dateRange, campaign_id: z.string().optional().describe("Filter to a specific campaign ID"), ad_group_id: z.string().optional().describe("Filter to a specific ad group ID"), status: z.enum(["ENABLED", "PAUSED", "REMOVED", "ALL"]).default("ENABLED"), }; - src/tools/keywords.ts:18-50 (handler)Main handler function that executes the GAQL query against the Google Ads API. Builds dynamic filters, queries keyword_view for keyword-level metrics (quality score, match type, clicks, cost, conversions), and returns rows.
export async function keywordPerformance(args: z.infer<z.ZodObject<typeof keywordPerformanceSchema>>) { const customer = getCustomer(args.customer_id); const start = resolveDate(args.start_date); const end = resolveDate(args.end_date); const filters = [ args.campaign_id ? `AND campaign.id = ${args.campaign_id}` : "", args.ad_group_id ? `AND ad_group.id = ${args.ad_group_id}` : "", args.status === "ALL" ? "" : `AND ad_group_criterion.status = '${args.status}'`, ].filter(Boolean).join(" "); const rows = await customer.query(` SELECT campaign.name, ad_group.name, ad_group_criterion.criterion_id, ad_group_criterion.keyword.text, ad_group_criterion.keyword.match_type, ad_group_criterion.status, ad_group_criterion.quality_info.quality_score, metrics.impressions, metrics.clicks, metrics.ctr, metrics.average_cpc, metrics.cost_micros, metrics.conversions, metrics.conversions_value FROM keyword_view WHERE segments.date BETWEEN '${start}' AND '${end}' ${filters} ORDER BY metrics.cost_micros DESC LIMIT ${args.limit} `); return { rowCount: rows.length, rows }; } - src/client.ts:24-31 (helper)getCustomer helper used by keywordPerformance to obtain an authenticated Google Ads API Customer object.
export function getCustomer(override?: string): Customer { const refresh_token = process.env.GOOGLE_ADS_REFRESH_TOKEN; if (!refresh_token) throw new GoogleAdsError("GOOGLE_ADS_REFRESH_TOKEN is not set"); const customer_id = (override ?? process.env.GOOGLE_ADS_CUSTOMER_ID ?? "").replace(/-/g, ""); if (!customer_id) throw new GoogleAdsError("GOOGLE_ADS_CUSTOMER_ID is not set and no customer_id was passed"); const login_customer_id = process.env.GOOGLE_ADS_LOGIN_CUSTOMER_ID?.replace(/-/g, "") || undefined; return getApi().Customer({ customer_id, login_customer_id, refresh_token }); } - src/client.ts:42-49 (helper)resolveDate helper that converts date strings (e.g., '28daysAgo', 'yesterday') into YYYY-MM-DD format for the GAQL query.
export function resolveDate(d: string): string { if (/^\d{4}-\d{2}-\d{2}$/.test(d)) return d; if (d === "today") return toISO(new Date()); if (d === "yesterday") return toISO(offsetDays(new Date(), -1)); const m = d.match(/^(\d+)daysAgo$/); if (m) return toISO(offsetDays(new Date(), -parseInt(m[1], 10))); throw new GoogleAdsError(`Unrecognized date: ${d}. Use YYYY-MM-DD, today, yesterday, or NdaysAgo.`); }