// Script to query Apple iTunes Search API for ASO & Competition Analysis import https from 'https'; const KEYWORDS_DE = [ 'receipt scanner', 'beleg scanner', 'belege digitalisieren', 'rechnung scanner', 'kassenbon scanner', 'quittung scanner', 'spesen app', 'buchhaltung scanner', 'datev scanner', 'receipt to excel', 'expense tracker', 'ausgaben tracker', 'belegmanager', 'rechnungsprogramm', 'fahrtenbuch und belege', 'steuer belege', 'ocr scanner excel' ]; const KEYWORDS_US = [ 'receipt scanner', 'receipt to excel', 'receipt scanner to excel', 'expense tracker', 'receipt keeper', 'invoice scanner', 'receipts and expenses', 'ocr receipt scanner', 'bookkeeping scanner', 'mileage and receipts', 'tax receipt organizer', 'smart receipt' ]; function fetchAppleSearch(term, country = 'de', limit = 25) { return new Promise((resolve, reject) => { const url = `https://itunes.apple.com/search?term=${encodeURIComponent(term)}&country=${country}&entity=software&limit=${limit}`; https.get(url, { headers: { 'User-Agent': 'Mozilla/5.0' } }, (res) => { let data = ''; res.on('data', chunk => data += chunk); res.on('end', () => { try { const json = JSON.parse(data); resolve(json); } catch (e) { reject(e); } }); }).on('error', reject); }); } async function analyzeKeywords(keywords, country) { console.log(`\n======================================================`); console.log(`🔍 ANALYSING APPLE APP STORE SEARCH DATA [Country: ${country.toUpperCase()}]`); console.log(`======================================================\n`); const results = []; for (const kw of keywords) { try { const data = await fetchAppleSearch(kw, country, 25); const totalResults = data.resultCount; const apps = data.results || []; // Metrics calculation const top5 = apps.slice(0, 5); const top5Names = top5.map(a => a.trackName); const avgRating = top5.reduce((acc, a) => acc + (a.averageUserRating || 0), 0) / (top5.length || 1); const totalTop5Ratings = top5.reduce((acc, a) => acc + (a.userRatingCount || 0), 0); const avgTop5RatingCount = Math.round(totalTop5Ratings / (top5.length || 1)); // Check title keyword match density in top 10 const titleMatches = apps.slice(0, 10).filter(a => (a.trackName || '').toLowerCase().includes(kw.toLowerCase()) || (a.description || '').toLowerCase().includes(kw.toLowerCase()) ).length; // Price distribution const freeCount = top5.filter(a => a.price === 0).length; results.push({ keyword: kw, resultCount: totalResults, top1: apps[0] ? `${apps[0].trackName} (${apps[0].userRatingCount || 0} reviews, ★${apps[0].averageUserRating?.toFixed(1) || '0'})` : 'None', top5AvgRating: avgRating.toFixed(2), avgTop5Reviews: avgTop5RatingCount, titleMatchInTop10: titleMatches, topCompetitors: top5.map(a => ({ name: a.trackName, seller: a.sellerName, reviews: a.userRatingCount || 0, rating: a.averageUserRating || 0, price: a.price, genres: a.genres })) }); // Avoid hitting rate limits await new Promise(r => setTimeout(r, 200)); } catch (err) { console.error(`Error fetching "${kw}":`, err.message); } } return results; } async function run() { const deResults = await analyzeKeywords(KEYWORDS_DE, 'de'); const usResults = await analyzeKeywords(KEYWORDS_US, 'us'); console.log(JSON.stringify({ de: deResults, us: usResults }, null, 2)); } run();