detect_quick_wins
Identify SEO opportunities by analyzing search data to find pages with high impressions but low click-through rates that can be improved for better rankings.
Instructions
Automatically detect SEO quick wins and optimization opportunities
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| siteUrl | Yes | The site URL as defined in Search Console. Example: sc-domain:example.com (for domain resources) or http://www.example.com/ (for site prefix resources) | |
| startDate | Yes | Start date in YYYY-MM-DD format | |
| endDate | Yes | End date in YYYY-MM-DD format | |
| minImpressions | No | Minimum impressions threshold for quick wins | |
| maxCtr | No | Maximum CTR percentage for quick wins detection | |
| positionRangeMin | No | Minimum position for quick wins (default: 4) | |
| positionRangeMax | No | Maximum position for quick wins (default: 10) | |
| estimatedClickValue | No | Estimated value per click for ROI calculation | |
| conversionRate | No | Estimated conversion rate for ROI calculation |
Implementation Reference
- src/search-console.ts:135-187 (handler)Core implementation of quick wins detection: filters low-CTR queries in good positions with sufficient impressions, calculates potential clicks at 5% CTR target, and ranks by opportunity size.private detectQuickWins( rows: any[], thresholds: { minImpressions?: number; maxCtr?: number; positionRangeMin?: number; positionRangeMax?: number; } = {} ) { const { minImpressions = 50, maxCtr = 2.0, positionRangeMin = 4, positionRangeMax = 10 } = thresholds; return rows .filter(row => { const impressions = row.impressions || 0; const ctr = (row.ctr || 0) * 100; const position = row.position || 0; return impressions >= minImpressions && ctr <= maxCtr && position >= positionRangeMin && position <= positionRangeMax; }) .map(row => { const impressions = row.impressions || 0; const currentClicks = row.clicks || 0; const currentCtr = (row.ctr || 0) * 100; const position = row.position || 0; // Calculate potential with 5% target CTR const targetCtr = 5.0; const potentialClicks = Math.round((impressions * targetCtr) / 100); const additionalClicks = Math.max(0, potentialClicks - currentClicks); return { query: row.keys?.[0] || 'N/A', page: row.keys?.[1] || 'N/A', currentPosition: Number(position.toFixed(1)), impressions: impressions, currentClicks: currentClicks, currentCtr: Number(currentCtr.toFixed(2)), potentialClicks: potentialClicks, additionalClicks: additionalClicks, opportunity: additionalClicks > 0 ? 'High' : 'Low', optimizationNote: `Move from position ${position.toFixed(1)} to improve CTR` }; }) .sort((a, b) => b.additionalClicks - a.additionalClicks); }
- src/schemas.ts:94-103 (schema)Zod schema defining input parameters for the detect_quick_wins tool, including date range and customizable thresholds.export const QuickWinsDetectionSchema = GSCBaseSchema.extend({ startDate: z.string().describe('Start date in YYYY-MM-DD format'), endDate: z.string().describe('End date in YYYY-MM-DD format'), minImpressions: z.number().default(50).describe('Minimum impressions threshold for quick wins'), maxCtr: z.number().default(2.0).describe('Maximum CTR percentage for quick wins detection'), positionRangeMin: z.number().default(4).describe('Minimum position for quick wins (default: 4)'), positionRangeMax: z.number().default(10).describe('Maximum position for quick wins (default: 10)'), estimatedClickValue: z.number().default(1.0).describe('Estimated value per click for ROI calculation'), conversionRate: z.number().default(0.03).describe('Estimated conversion rate for ROI calculation'), });
- src/index.ts:58-62 (registration)Registration of the detect_quick_wins tool in the ListTools response.{ name: 'detect_quick_wins', description: 'Automatically detect SEO quick wins and optimization opportunities', inputSchema: zodToJsonSchema(QuickWinsDetectionSchema), },
- src/index.ts:163-217 (handler)Main tool handler in the CallToolRequest switch statement: fetches search analytics data up to 25k rows and invokes enhanced analysis with quick wins detection enabled.case 'detect_quick_wins': { const args = QuickWinsDetectionSchema.parse(request.params.arguments); // First get search analytics data const requestBody: any = { startDate: args.startDate, endDate: args.endDate, dimensions: ['query', 'page'], rowLimit: 25000, // Maximum for comprehensive analysis }; const searchResponse = await searchConsole.searchAnalytics(args.siteUrl, requestBody); if (!searchResponse.data.rows) { return { content: [ { type: 'text', text: JSON.stringify({ message: 'No data available for quick wins analysis' }, null, 2), }, ], }; } // Apply quick wins detection const quickWinsOptions = { enableQuickWins: true, quickWinsThresholds: { minImpressions: args.minImpressions, maxCtr: args.maxCtr, positionRangeMin: args.positionRangeMin, positionRangeMax: args.positionRangeMax, }, }; const enhancedResult = await searchConsole.enhancedSearchAnalytics( args.siteUrl, requestBody, quickWinsOptions ); return { content: [ { type: 'text', text: JSON.stringify({ quickWins: (enhancedResult.data as any).quickWins, totalOpportunities: (enhancedResult.data as any).quickWins?.length || 0, thresholds: quickWinsOptions.quickWinsThresholds, analysis: 'Quick wins detection completed' }, null, 2), }, ], }; }
- src/search-console.ts:74-129 (helper)Supporting method that applies quick wins detection (and regex filtering) to search analytics results, invoked by the main tool handler.async enhancedSearchAnalytics( siteUrl: string, requestBody: SearchanalyticsQueryRequest, options: { regexFilter?: string; enableQuickWins?: boolean; quickWinsThresholds?: { minImpressions?: number; maxCtr?: number; positionRangeMin?: number; positionRangeMax?: number; }; } = {} ) { // Ensure requestBody is defined if (!requestBody) { throw new Error('Request body is required'); } // Apply regex filter if provided if (options.regexFilter && requestBody.dimensions?.includes('query')) { requestBody.dimensionFilterGroups = [ ...(requestBody.dimensionFilterGroups || []), { groupType: 'and', filters: [{ dimension: 'query', operator: 'includingRegex', expression: options.regexFilter }] } ]; } // Execute enhanced search analytics const result = await this.searchAnalytics(siteUrl, requestBody); // Apply quick wins detection if enabled if (options.enableQuickWins && result.data.rows) { const quickWins = this.detectQuickWins(result.data.rows, options.quickWinsThresholds); return { ...result, data: { ...result.data, quickWins: quickWins, enhancedFeatures: { regexFilterApplied: !!options.regexFilter, quickWinsEnabled: true, rowLimit: requestBody.rowLimit || 1000 } } }; } return result; }