get_keyword_report
Retrieve keyword-level performance metrics for an Apple Search Ads ad group, including impressions, taps, spend, installs, and install rate, grouped by country with grand totals.
Instructions
Fetch performance metrics for targeting keywords in a specific Apple Search Ads ad group: impressions, taps, TTR, spend, CPI, installs, and install rate broken down per keyword. Requires ASA authentication; read-only. Use get_search_terms_report to see the actual user queries that triggered these keywords. Results are grouped by country/region and include grand totals. Defaults to the last 30 days with WEEKLY granularity.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| startDate | No | Start of the reporting period (YYYY-MM-DD). Defaults to 30 days ago. | |
| endDate | No | End of the reporting period (YYYY-MM-DD). Defaults to today. | |
| granularity | No | Time granularity for the breakdown rows: HOURLY, DAILY, WEEKLY, or MONTHLY. Defaults to WEEKLY. | |
| campaignId | Yes | ID of the campaign containing the ad group. Obtain from list_campaigns. | |
| adGroupId | Yes | ID of the ad group to report on. Obtain from list_ad_groups. |
Implementation Reference
- src/tools/reports.ts:297-357 (handler)The get_keyword_report tool handler: calls the ASA API POST /reports/campaigns/{campaignId}/keywords with a filter on adGroupId, builds the report body using shared helper, and returns parsed JSON results.
// --- get_keyword_report --------------------------------------------------- server.tool( "get_keyword_report", "Fetch performance metrics for targeting keywords in a specific Apple Search Ads ad group: impressions, taps, TTR, spend, CPI, installs, and install rate broken down per keyword. Requires ASA authentication; read-only. Use get_search_terms_report to see the actual user queries that triggered these keywords. Results are grouped by country/region and include grand totals. Defaults to the last 30 days with WEEKLY granularity.", { startDate: z .string() .optional() .describe("Start of the reporting period (YYYY-MM-DD). Defaults to 30 days ago."), endDate: z .string() .optional() .describe("End of the reporting period (YYYY-MM-DD). Defaults to today."), granularity: GranularityEnum.optional().describe( "Time granularity for the breakdown rows: HOURLY, DAILY, WEEKLY, or MONTHLY. Defaults to WEEKLY." ), campaignId: z .number() .int() .positive() .describe("ID of the campaign containing the ad group. Obtain from list_campaigns."), adGroupId: z .number() .int() .positive() .describe("ID of the ad group to report on. Obtain from list_ad_groups."), }, async (args) => { const input = GetKeywordReportInputSchema.parse(args); const defaults = defaultDateRange(); const startDate = input.startDate ?? defaults.startDate; const endDate = input.endDate ?? defaults.endDate; const granularity = input.granularity ?? "WEEKLY"; validateDateRange(startDate, endDate); // campaignId is already in the URL path — adding it as a condition causes // INVALID_CONDITION_INPUT from ASA. Only adGroupId is needed here. const selector: ReportRequest["selector"] = { conditions: [ { field: "adGroupId", operator: "EQUALS", values: [String(input.adGroupId)], }, ], }; const body = buildReportBody(startDate, endDate, granularity, "keywordId", selector); const response = await client.post<ReportResponse>( `/reports/campaigns/${input.campaignId}/keywords`, body ); const result = parseReportResponse(response.data); return { content: [{ type: "text" as const, text: JSON.stringify(result, null, 2) }], }; } ); - src/tools/reports.ts:156-160 (schema)GetKeywordReportInputSchema: Zod schema defining input fields (startDate, endDate, granularity, campaignId, adGroupId).
export const GetKeywordReportInputSchema = z.object({ ...dateRangeFields, campaignId: z.number().int().positive().describe("Campaign ID."), adGroupId: z.number().int().positive().describe("Ad group ID."), }); - src/tools/reports.ts:170-171 (schema)GetKeywordReportInput: TypeScript type inferred from the input schema.
export type GetKeywordReportInput = z.infer<typeof GetKeywordReportInputSchema>; export type GetSearchTermsReportInput = z.infer<typeof GetSearchTermsReportInputSchema>; - src/tools/reports.ts:177-178 (registration)registerReportsTools function — registers all report tools including get_keyword_report.
/** Registers all four report tools. */ export function registerReportsTools(server: McpServer, client: AsaClient): void { - src/server.ts:10-31 (registration)registerReportsTools is imported and called in createServer() to register the tool on the MCP server.
registerReportsTools, } from "./tools/index.js"; const SERVER_INFO = { name: "aapl-ads-mcp", version: "0.1.0", }; /** Creates and configures the MCP server with all registered tools. */ export function createServer(): McpServer { const server = new McpServer(SERVER_INFO); const client = new AsaClient(getConfig()); registerHealthTool(server); registerOrgsTools(server, client); registerCampaignsTools(server, client); registerAdGroupsTools(server, client); registerKeywordsTools(server, client); registerReportsTools(server, client); return server; }