Monitor app reviews by searching Google for app store review pages, extracting review snippets and rating data from search results, and tracking changes over time. App store APIs are restrictive and often require developer accounts, but Google indexes app review pages from both the Apple App Store and Google Play Store. By searching for your app name plus review-related keywords, you can capture review snippets, rating mentions, and competitor review data through a single search API.
Prerequisites
- Python 3.8+ installed
- requests library installed
- A Scavio API key from scavio.dev
- App names to monitor
Walkthrough
Step 1: Search for app reviews
Query Google for app review pages to capture indexed review content.
import os, requests, re, json, datetime
API_KEY = os.environ['SCAVIO_API_KEY']
def search_app_reviews(app_name: str) -> list:
queries = [
f'{app_name} app reviews 2026',
f'{app_name} app store rating',
f'{app_name} user reviews',
]
all_results = []
for query in queries:
resp = requests.post('https://api.scavio.dev/api/v1/search',
headers={'x-api-key': API_KEY},
json={'platform': 'google', 'query': query}, timeout=15)
results = resp.json().get('organic_results', [])
all_results.extend(results[:3])
return all_results
reviews = search_app_reviews('Slack')
print(f'Found {len(reviews)} review-related results')
for r in reviews[:3]:
print(f" {r.get('title', '')[:60]}")Step 2: Extract rating data
Parse search results for rating numbers and review counts.
def extract_ratings(results: list) -> dict:
ratings = []
for r in results:
text = (r.get('title', '') + ' ' + r.get('snippet', '')).lower()
# Look for rating patterns like "4.5/5", "4.5 stars", "rated 4.5"
rating_matches = re.findall(r'(\d+\.?\d*)\s*(?:/5|stars?|out of 5|rating)', text)
for match in rating_matches:
try:
val = float(match)
if 1 <= val <= 5:
ratings.append(val)
except ValueError:
pass
# Look for review counts like "10K reviews", "5,000 reviews"
count_matches = re.findall(r'([\d,]+[KkMm]?)\s*reviews?', text)
if count_matches:
pass # Store for tracking
avg_rating = round(sum(ratings) / len(ratings), 1) if ratings else 0
return {
'ratings_found': len(ratings),
'average': avg_rating,
'min': min(ratings) if ratings else 0,
'max': max(ratings) if ratings else 0,
}
rating_data = extract_ratings(reviews)
print(f"Average rating: {rating_data['average']} (from {rating_data['ratings_found']} mentions)")Step 3: Detect sentiment signals
Analyze review snippets for positive and negative sentiment keywords.
POSITIVE = ['love', 'great', 'excellent', 'best', 'amazing', 'fast', 'easy', 'reliable', 'intuitive']
NEGATIVE = ['slow', 'buggy', 'crash', 'expensive', 'terrible', 'worst', 'broken', 'frustrating', 'unusable']
def analyze_sentiment(results: list) -> dict:
positive_count = 0
negative_count = 0
positive_snippets = []
negative_snippets = []
for r in results:
text = r.get('snippet', '').lower()
pos = sum(1 for w in POSITIVE if w in text)
neg = sum(1 for w in NEGATIVE if w in text)
if pos > neg:
positive_count += 1
positive_snippets.append(r.get('snippet', '')[:100])
elif neg > pos:
negative_count += 1
negative_snippets.append(r.get('snippet', '')[:100])
total = positive_count + negative_count
return {
'positive': positive_count,
'negative': negative_count,
'sentiment_ratio': round(positive_count / total, 2) if total > 0 else 0,
'top_positive': positive_snippets[:2],
'top_negative': negative_snippets[:2],
}
sentiment = analyze_sentiment(reviews)
print(f"Sentiment: {sentiment['positive']} positive, {sentiment['negative']} negative")
print(f"Ratio: {sentiment['sentiment_ratio']}")Step 4: Track changes over time
Store daily review data and detect significant changes in ratings or sentiment.
HISTORY_FILE = 'app_review_history.json'
def store_review_data(app: str, rating_data: dict, sentiment: dict):
history = []
try:
with open(HISTORY_FILE) as f:
history = json.load(f)
except FileNotFoundError:
pass
entry = {
'app': app,
'date': datetime.date.today().isoformat(),
'rating': rating_data,
'sentiment': sentiment,
}
history.append(entry)
with open(HISTORY_FILE, 'w') as f:
json.dump(history, f, indent=2)
return entry
def detect_changes(app: str) -> dict:
try:
with open(HISTORY_FILE) as f:
history = json.load(f)
except FileNotFoundError:
return {'change': 'no history'}
entries = [h for h in history if h['app'] == app]
if len(entries) < 2:
return {'change': 'insufficient data'}
prev = entries[-2]
curr = entries[-1]
rating_change = curr['rating']['average'] - prev['rating']['average']
sentiment_change = curr['sentiment']['sentiment_ratio'] - prev['sentiment']['sentiment_ratio']
return {
'rating_change': round(rating_change, 1),
'sentiment_change': round(sentiment_change, 2),
'alert': abs(rating_change) > 0.3 or abs(sentiment_change) > 0.2,
}
store_review_data('Slack', rating_data, sentiment)
changes = detect_changes('Slack')
print(f"Changes: {changes}")Python Example
import requests, os, re
H = {'x-api-key': os.environ['SCAVIO_API_KEY']}
def app_reviews(app):
data = requests.post('https://api.scavio.dev/api/v1/search', headers=H,
json={'platform': 'google', 'query': f'{app} app reviews 2026'}).json()
results = data.get('organic_results', [])[:5]
return [{'title': r.get('title', ''), 'snippet': r.get('snippet', '')[:80]} for r in results]
print(app_reviews('Slack'))JavaScript Example
const H = {'x-api-key': process.env.SCAVIO_API_KEY, 'Content-Type': 'application/json'};
async function appReviews(app) {
const r = await fetch('https://api.scavio.dev/api/v1/search', {
method: 'POST', headers: H,
body: JSON.stringify({platform: 'google', query: `${app} app reviews 2026`})
});
return ((await r.json()).organic_results || []).slice(0, 5)
.map(r => ({title: r.title, snippet: (r.snippet || '').slice(0, 80)}));
}
appReviews('Slack').then(console.log);Expected Output
An app review monitoring system that tracks ratings, sentiment, and review changes over time by querying Google for app store review content.