/**
* Google PageSpeed Insights Performance Analyzer
* API v5: https://pagespeedonline.googleapis.com/pagespeedonline/v5/runPagespeed
* Quota: 25,000 requests/day with free API key
*/
export interface PageSpeedOptions {
/** Strategy: mobile or desktop (default: mobile) */
strategy?: 'mobile' | 'desktop';
/** Optional API key (recommended for production, 25K/day quota) */
apiKey?: string;
/** Categories to test (default: all) */
categories?: Array<'performance' | 'accessibility' | 'best-practices' | 'seo'>;
}
export interface PageSpeedMetrics {
firstContentfulPaint?: number;
largestContentfulPaint?: number;
totalBlockingTime?: number;
cumulativeLayoutShift?: number;
speedIndex?: number;
timeToInteractive?: number;
}
export interface PageSpeedResponse {
lighthouseResult: {
categories: {
performance?: { score: number };
accessibility?: { score: number };
'best-practices'?: { score: number };
seo?: { score: number };
};
audits: Record<string, any>;
};
loadingExperience?: {
metrics: Record<string, any>;
};
}
export interface PageSpeedResult {
tool: 'pagespeed';
success: boolean;
url: string;
strategy: string;
performance_score?: number;
accessibility_score?: number;
best_practices_score?: number;
seo_score?: number;
metrics?: PageSpeedMetrics;
crux_data?: boolean;
error?: string;
raw?: PageSpeedResponse;
}
/**
* Analyze website performance using Google PageSpeed Insights
*/
export async function analyzePageSpeed(
url: string,
options: PageSpeedOptions = {}
): Promise<PageSpeedResult> {
try {
const strategy = options.strategy || 'mobile';
const categories = options.categories || ['performance', 'accessibility', 'best-practices', 'seo'];
// Build API URL
const params = new URLSearchParams({
url,
strategy,
});
// Add categories
categories.forEach(cat => params.append('category', cat));
// Add API key if provided
if (options.apiKey) {
params.set('key', options.apiKey);
}
const apiUrl = `https://pagespeedonline.googleapis.com/pagespeedonline/v5/runPagespeed?${params.toString()}`;
const response = await fetch(apiUrl);
if (!response.ok) {
throw new Error(`PageSpeed API error: ${response.status} ${response.statusText}`);
}
const data: PageSpeedResponse = await response.json();
// Extract scores (0-1 range, convert to 0-100)
const scores = data.lighthouseResult.categories;
const performance_score = scores.performance ? Math.round(scores.performance.score * 100) : undefined;
const accessibility_score = scores.accessibility ? Math.round(scores.accessibility.score * 100) : undefined;
const best_practices_score = scores['best-practices'] ? Math.round(scores['best-practices'].score * 100) : undefined;
const seo_score = scores.seo ? Math.round(scores.seo.score * 100) : undefined;
// Extract key metrics (convert to milliseconds)
const audits = data.lighthouseResult.audits;
const metrics: PageSpeedMetrics = {
firstContentfulPaint: audits['first-contentful-paint']?.numericValue,
largestContentfulPaint: audits['largest-contentful-paint']?.numericValue,
totalBlockingTime: audits['total-blocking-time']?.numericValue,
cumulativeLayoutShift: audits['cumulative-layout-shift']?.numericValue,
speedIndex: audits['speed-index']?.numericValue,
timeToInteractive: audits['interactive']?.numericValue,
};
return {
tool: 'pagespeed',
success: true,
url,
strategy,
performance_score,
accessibility_score,
best_practices_score,
seo_score,
metrics,
crux_data: !!data.loadingExperience,
raw: data,
};
} catch (error) {
return {
tool: 'pagespeed',
success: false,
url,
strategy: options.strategy || 'mobile',
error: error instanceof Error ? error.message : String(error),
};
}
}