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.
What’s Included
Section titled “What’s Included”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
Section titled “Coverage”| 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.
Quick Start
Section titled “Quick Start”from finbrain import FinBrainClient
fb = FinBrainClient(api_key="YOUR_API_KEY")
df = fb.app_ratings.ticker("UBER", as_dataframe=True)print(df)import requests
API_KEY = "YOUR_API_KEY"BASE_URL = "https://api.finbrain.tech"
headers = {"Authorization": f"Bearer {API_KEY}"}
response = requests.get(f"{BASE_URL}/v2/app-ratings/UBER", headers=headers)result = response.json()
# The envelope is {success, data, meta}; the series lives at data.datafor entry in result["data"]["data"][:5]: # Either side is None when there is no rated app on that store ios = entry["ios"] or {} android = entry["android"] or {} print(f"{entry['date']}: iOS {ios.get('score')} | Android {android.get('score')}")For complete code examples in Python, JavaScript, C++, Rust, and cURL, see the API Reference.
Every App, Not Just the Flagship
Section titled “Every App, Not Just the Flagship”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 observationapps = fb.app_ratings.ticker("AAPL", as_dataframe=True, per_app=True)
# The company's app portfolio, biggest firstportfolio = ( 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.
Visualization
Section titled “Visualization”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 Storefb.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")
Interpreting App Ratings
Section titled “Interpreting App Ratings”Rating Levels
Section titled “Rating Levels”| 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 |
Rating Trends
Section titled “Rating Trends”| Trend | Interpretation |
|---|---|
| Rising rating | Improving product/service |
| Stable rating | Consistent experience |
| Falling rating | Potential issues emerging |
| Rating divergence (iOS vs Android) | Platform-specific problems |
Use Cases
Section titled “Use Cases”App Quality Monitor
Section titled “App Quality Monitor”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}")Consumer App Comparison
Section titled “Consumer App Comparison”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 appsdelivery_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}")Rating Trend Analysis
Section titled “Rating Trend Analysis”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})")Platform Divergence Detection
Section titled “Platform Divergence Detection”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}")App Portfolio Breakdown
Section titled “App Portfolio Breakdown”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")Related Resources
Section titled “Related Resources”- App Ratings API Reference - Endpoint details, parameters, and response schema
- LinkedIn Metrics - Workforce and follower data
- News Sentiment - Market sentiment scores
