Updated Oct 2026

Review analytics by API: average rating, reply rate, response time, trends

Every review dashboard answers the same five questions: what’s our rating, how is it moving, how many reviews do we get, how many do we answer, and how fast. Computing that across Google, Trustpilot, the App Store and the rest means normalising five rating scales and tracking replies you didn’t send through your tool. This guide does it with one endpoint, GET /v1/stats, then builds a per-location report and a feed for Looker Studio or Google Sheets. Reference: stats.

1. The stats object

cURL
curl "https://api.prismareviews.com/v1/stats?since=2026-07-01&until=2026-10-01" \
  -H "Authorization: Bearer $PRISMA_API_KEY"
JSON
{
  "since": "2026-07-01T00:00:00.000Z", "until": "2026-10-01T00:00:00.000Z", "location_id": null, "platform": null,
  "reviews": 412, "average_rating": 4.31, "ratings": {"1": 18, "2": 21, "3": 35, "4": 97, "5": 241},
  "replied": 268, "reply_rate": 0.65, "median_reply_hours": 19.4, "unreplied_negative": 11,
  "by_month": [
    {"month": "2026-07", "reviews": 131, "average_rating": 4.22, "replied": 80},
    {"month": "2026-08", "reviews": 140, "average_rating": 4.35, "replied": 96},
    {"month": "2026-09", "reviews": 141, "average_rating": 4.36, "replied": 92}
  ]
}

What each field means, and the traps:

2. A report per location

Shell
pip install requests
Python
import os, requests

API = "https://api.prismareviews.com/v1"
H = {"Authorization": f"Bearer {os.environ['PRISMA_API_KEY']}"}
PERIOD = {"since": "2026-07-01", "until": "2026-10-01"}

locations = requests.get(f"{API}/locations", headers=H, params={"limit": 100}, timeout=30).json()["data"]
rows = []
for loc in locations:
    s = requests.get(f"{API}/locations/{loc['id']}/stats", headers=H, params=PERIOD, timeout=30).json()
    rows.append((loc["name"], loc["platform"], s["reviews"], s["average_rating"], s["reply_rate"], s["median_reply_hours"], s["unreplied_negative"]))

rows.sort(key=lambda r: (r[6], -(r[3] or 0)), reverse=True)   # most unreplied negatives first
print(f"{'Location':30} {'Platform':12} {'Reviews':>7} {'Avg':>5} {'Reply%':>7} {'Median h':>9} {'Neg open':>8}")
for name, platform, n, avg, rr, med, neg in rows:
    print(f"{name[:30]:30} {platform:12} {n:7} {avg or 0:5.2f} {100*(rr or 0):6.0f}% {med or 0:9.1f} {neg:8}")

Per platform instead of per location: call GET /v1/stats?platform=google once per platform id from GET /v1/platforms.

3. Month-over-month trend

by_month is already there; the only arithmetic is the delta:

Python
s = requests.get(f"{API}/stats", headers=H, params={"since": "2025-10-01"}, timeout=30).json()
prev = None
for m in s["by_month"]:
    delta = "" if prev is None or m["average_rating"] is None or prev["average_rating"] is None else f"{m['average_rating'] - prev['average_rating']:+.2f}"
    print(m["month"], m["reviews"], m["average_rating"], f"{m['replied']}/{m['reviews']}", delta)
    prev = m

A falling average with rising volume usually means one location, not the brand: run section 2 for that month to find it.

4. Feed a dashboard: Looker Studio or Google Sheets

Looker Studio reads Google Sheets natively, so the simplest pipeline is a sheet refreshed on a schedule. In Apps Script, with the key in Script properties:

Node.js
function refreshStats() {
  const key = PropertiesService.getScriptProperties().getProperty("PRISMA_API_KEY");
  const h = { Authorization: "Bearer " + key };
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Stats");
  sheet.clear(); sheet.appendRow(["location", "platform", "reviews", "average_rating", "reply_rate", "median_reply_hours", "unreplied_negative"]);
  const locs = JSON.parse(UrlFetchApp.fetch("https://api.prismareviews.com/v1/locations?limit=100", { headers: h }).getContentText()).data;
  for (const l of locs) {
    const s = JSON.parse(UrlFetchApp.fetch(`https://api.prismareviews.com/v1/locations/${l.id}/stats?since=2026-01-01`, { headers: h }).getContentText());
    sheet.appendRow([l.name, l.platform, s.reviews, s.average_rating, s.reply_rate, s.median_reply_hours, s.unreplied_negative]);
  }
}

Add a daily time-driven trigger and point Looker Studio at the sheet. For a warehouse, the same loop writes rows through your loader; by_month gives the series without recomputing from raw reviews. Raw reviews for deeper analysis: Google reviews to Google Sheets.

Honest limits

FAQ

How do I calculate a reply rate across review platforms?

Count reviews with a published reply, whoever wrote it, divided by reviews written in the period. GET /v1/stats returns it as reply_rate per workspace, location or platform, with replied and reviews so you can recompute it.

What’s a good median reply time for reviews?

Under 24 hours for negative reviews is the target most multi-location operators use; the median_reply_hours field in the stats object gives you the actual number per location so you can set it from your own data.

Can I get review stats per month?

Yes. The stats object includes by_month: one entry per calendar month with reviews, average rating and replies, oldest first, for any period you request with since and until.

Five numbers, every platform, one call.

Get API key

2 locations free. Test keys return sample stats, so the report runs today.

Get your API key in a minute

Free Spark plan: 2 locations, no card. Test keys return sample data on all 58 platforms, so you can build before connecting a real one.