Skip to content

App Ratings Dataset

Access mobile app store ratings and review data from Apple App Store and Google Play Store. Track app performance metrics as alternative data signals for consumer-facing companies.

The App Ratings dataset provides:

  • iOS Ratings: App Store rating (1-5 stars) and ratings count
  • Android Ratings: Play Store rating (1-5 stars), ratings count, and install count
  • Every App, Identified: each app a company publishes arrives as its own series, keyed by store app id and carrying the app’s published title
  • Rating Changes: Track rating movements over time
  • Daily Snapshots: every app, every day, at a fixed UTC time
  • Day-over-day Movement: consecutive daily snapshots per app, so ratings growth and install growth are a one-line difference
Coverage Details
Platforms Apple App Store, Google Play Store
Companies Companies publishing mobile apps under a covered ticker
Apps per company Every app we can attribute to the issuer, not just its flagship
Update Frequency Daily, collected at 03:00 UTC
Historical Data From 3 September 2026, the day daily per-app collection began. Nothing earlier is served. An app added to the registry later starts on the day it was added and is never backfilled

Note: installCount is Play Store only — Apple publishes no install count, so it is always null on iOS.

from finbrain import FinBrainClient
fb = FinBrainClient(api_key="YOUR_API_KEY")
df = fb.app_ratings.ticker("UBER", as_dataframe=True)
print(df)

For complete code examples in Python, JavaScript, C++, Rust, and cURL, see the API Reference.

Most companies do not publish one app. A retailer ships a shopping app, a payments app and a loyalty app; a bank ships retail banking, business banking and a card app; Apple publishes over a hundred on iOS alone. Collapsing that to a single company score throws away most of the signal — and hides which product line is actually moving.

Each response therefore carries one series per app per platform, identified by its store app id and title, alongside the company-level view:

View What it reports
Blended (data, the SDK default) The company’s biggest app on each store, one row per date
Per-app (apps, per_app=True) Every app, each with its own history

There is deliberately no blended company score. Weighting a portfolio of apps into one number is a judgement — by ratings volume, by revenue relevance, by product line — and it belongs to the desk making the trade, not to the data provider. You get the parts; you decide the weights.

from finbrain import FinBrainClient
fb = FinBrainClient(api_key="YOUR_API_KEY")
# One row per app per observation
apps = fb.app_ratings.ticker("AAPL", as_dataframe=True, per_app=True)
# The company's app portfolio, biggest first
portfolio = (
apps.groupby(["platform", "app_id", "app_name"])["ratings_count"]
.max()
.sort_values(ascending=False)
)
print(portfolio.head(10))
# Track one product line on its own (Shazam on iOS)
shazam = apps[apps["app_id"] == "284993459"].sort_values("date")
print(shazam[["date", "score", "ratings_count"]].tail())

Two things to know when you work with the per-app frame:

  • It is not indexed by date — a single date carries one row per app, so a date index would not be unique.
  • Every row carries an app_id: records have been keyed per app since collection restarted on 3 September 2026, and nothing older is served.
  • The app registry is reviewed continuously. An app added later starts its series on the day it was added; earlier dates are never backfilled, so the first observation is the day FinBrain began tracking that app.

Plot app ratings with the built-in SDK chart:

from finbrain import FinBrainClient
fb = FinBrainClient(api_key="YOUR_API_KEY")
# Interactive chart: ratings count (bars) + score (line)
fb.plot.app_ratings("UBER", store="app") # iOS App Store
fb.plot.app_ratings("UBER", store="play") # Google Play Store
# Chart one specific app instead of the company's biggest on that store.
# Ids come from the per-app frame above; the app must live on that store.
fb.plot.app_ratings("AAPL", store="app", app_id="284993459")
App Ratings Chart
UBER App Store ratings over time
Rating Interpretation Signal
4.5 - 5.0 Excellent Strong user satisfaction
4.0 - 4.5 Good Healthy app performance
3.5 - 4.0 Average Room for improvement
3.0 - 3.5 Below average User concerns
< 3.0 Poor Significant issues
Trend Interpretation
Rising rating Improving product/service
Stable rating Consistent experience
Falling rating Potential issues emerging
Rating divergence (iOS vs Android) Platform-specific problems

Monitor app ratings for quality signals:

from finbrain import FinBrainClient
fb = FinBrainClient(api_key="YOUR_API_KEY")
def monitor_app_quality(symbol):
"""Monitor app quality and detect rating changes"""
df = fb.app_ratings.ticker(symbol, as_dataframe=True)
if df.empty or len(df) < 7:
return None
ios_change = df["ios_score"].iloc[0] - df["ios_score"].iloc[6]
android_change = df["android_score"].iloc[0] - df["android_score"].iloc[6]
alerts = []
if ios_change < -0.1:
alerts.append(f"App Store rating dropped {abs(ios_change):.2f}")
if android_change < -0.1:
alerts.append(f"Play Store rating dropped {abs(android_change):.2f}")
if df["ios_score"].iloc[0] < 4.0:
alerts.append(f"App Store rating below 4.0 ({df['ios_score'].iloc[0]:.1f})")
if df["android_score"].iloc[0] < 4.0:
alerts.append(f"Play Store rating below 4.0 ({df['android_score'].iloc[0]:.1f})")
return {
"symbol": symbol,
"current_ios": df["ios_score"].iloc[0],
"current_android": df["android_score"].iloc[0],
"ios_change_7d": ios_change,
"android_change_7d": android_change,
"alerts": alerts,
"status": "warning" if alerts else "healthy"
}
result = monitor_app_quality("UBER")
print(f"Status: {result['status']}")
if result["alerts"]:
print("Alerts:")
for alert in result["alerts"]:
print(f" - {alert}")

Compare app performance across competitors:

from finbrain import FinBrainClient
fb = FinBrainClient(api_key="YOUR_API_KEY")
def compare_app_ratings(tickers):
"""Compare app ratings across competitors"""
results = []
for symbol in tickers:
try:
df = fb.app_ratings.ticker(symbol, as_dataframe=True)
if df.empty:
continue
ios = df["ios_score"].iloc[0]
android = df["android_score"].iloc[0]
combined = (ios + android) / 2
total_ratings = df["ios_ratingsCount"].iloc[0] + df["android_ratingsCount"].iloc[0]
results.append({
"symbol": symbol,
"ios": ios,
"android": android,
"combined": combined,
"total_ratings": total_ratings
})
except Exception:
continue
return sorted(results, key=lambda x: x["combined"], reverse=True)
# Compare food delivery apps
delivery_apps = ["UBER", "DASH", "GRUB"]
comparison = compare_app_ratings(delivery_apps)
print("Food Delivery App Comparison:")
print("-" * 50)
for app in comparison:
print(f"{app['symbol']}: Combined {app['combined']:.2f} | iOS {app['ios']:.1f} | Android {app['android']:.1f}")

Analyze rating trends over time:

from finbrain import FinBrainClient
fb = FinBrainClient(api_key="YOUR_API_KEY")
def analyze_rating_trend(symbol, days=30):
"""Analyze rating trend over time"""
df = fb.app_ratings.ticker(symbol, as_dataframe=True)
if df.empty or len(df) < days:
return None
# Calculate combined rating for each row
df["combined"] = (df["ios_score"] + df["android_score"]) / 2
recent = df["combined"].head(days)
# Calculate trend: compare recent half vs older half
second_half_avg = recent.head(days // 2).mean()
first_half_avg = recent.tail(days // 2).mean()
change = second_half_avg - first_half_avg
if change > 0.05:
trend = "improving"
elif change < -0.05:
trend = "declining"
else:
trend = "stable"
return {
"symbol": symbol,
"current_rating": df["combined"].iloc[0],
"30d_change": change,
"trend": trend
}
result = analyze_rating_trend("NFLX", 30)
print(f"{result['symbol']}: {result['trend']} (30d change: {result['30d_change']:+.2f})")

Detect when iOS and Android ratings diverge:

from finbrain import FinBrainClient
fb = FinBrainClient(api_key="YOUR_API_KEY")
def detect_platform_divergence(symbol, threshold=0.3):
"""Detect significant App Store vs Play Store rating divergence"""
df = fb.app_ratings.ticker(symbol, as_dataframe=True)
if df.empty:
return None
ios_score = df["ios_score"].iloc[0]
android_score = df["android_score"].iloc[0]
divergence = abs(ios_score - android_score)
alert = None
if divergence > threshold:
better_platform = "App Store" if ios_score > android_score else "Play Store"
worse_platform = "Play Store" if better_platform == "App Store" else "App Store"
alert = f"{worse_platform} rating significantly lower than {better_platform}"
return {
"symbol": symbol,
"ios_rating": ios_score,
"android_rating": android_score,
"divergence": divergence,
"alert": alert
}
result = detect_platform_divergence("META")
if result["alert"]:
print(f"Alert: {result['alert']}")
print(f" App Store: {result['ios_rating']:.1f} | Play Store: {result['android_rating']:.1f}")

Find which product line is moving, rather than watching one blended number:

from finbrain import FinBrainClient
fb = FinBrainClient(api_key="YOUR_API_KEY")
def portfolio_breakdown(symbol, min_observations=4):
"""Score change per app, so a decline can be attributed to a product"""
apps = fb.app_ratings.ticker(symbol, as_dataframe=True, per_app=True)
if apps.empty:
return []
rows = []
for (platform, app_id, app_name), grp in apps.groupby(
["platform", "app_id", "app_name"], dropna=False
):
grp = grp.sort_values("date")
if len(grp) < min_observations:
continue
first, last = grp.iloc[0], grp.iloc[-1]
rows.append({
"platform": platform,
"app": app_name or f"app {app_id}",
"score": last["score"],
"score_change": last["score"] - first["score"],
"ratings": last["ratings_count"],
})
# Biggest apps first: a 0.3 drop on the flagship is not the same
# event as a 0.3 drop on an app with 200 ratings.
return sorted(rows, key=lambda r: r["ratings"] or 0, reverse=True)
for app in portfolio_breakdown("AAPL")[:10]:
print(f"{app['platform']:<8} {app['app']:<28} "
f"{app['score']:.2f} ({app['score_change']:+.2f}) "
f"{app['ratings']:,} ratings")