---
title: "Reviews"
description: "How a keyword collects reviews from the App Store, Google Play, Trustpilot and Google."
canonical: https://docs.rumoro.dev/platforms/reviews
markdown: https://docs.rumoro.dev/platforms/reviews.mdx
---

# Reviews

How a keyword collects reviews from the App Store, Google Play, Trustpilot and Google.

A keyword can follow the reviews of your app, company or place. Add the review page to the keyword's `reviewSources` and each new review there becomes a mention, with its star rating. The review doesn't have to mention your name, and most don't ("the app crashes on login"). That's why review sites are not searched by the keyword's term like other platforms.

| Platform | `platform` | What to paste | Countries |
| --- | --- | --- | --- |
| [App Store](/platforms/appstore) | `appstore` | The app's App Store link | Yes, `countries` |
| [Google Play](/platforms/googleplay) | `googleplay` | The app's Google Play link | Yes, `countries` and `language` |
| [Trustpilot](/platforms/trustpilot) | `trustpilot` | The company page, `trustpilot.com/review/<domain>` | No, one page |
| [Google reviews](/platforms/googlemaps) | `googlemaps` | The place's Google Maps link, a `maps.app.goo.gl` share link, or its Place ID | No, one place |

A keyword can search its term, follow review pages, or both.

| `platforms` | `reviewSources` | What the keyword collects |
| --- | --- | --- |
| `null` or a list | none | Posts with the term |
| `null` or a list | one or more pages | Posts with the term, plus every review on the pages |
| `[]` | one or more pages | Only the reviews. The term is not searched. |

A keyword with `platforms: []` and no review pages would collect nothing, so it is refused with `400 validation_error`.

Reviews go into the same feed as other mentions. You can triage, filter, export and analyse them, and alert rules see them too.

## Connect a review page

Add `reviewSources` on create or with `PATCH /v1/keywords/{id}`. Entries are links, or a `platform` and `id` pair.

```ts tab="TypeScript"
await rumoro.createKeyword({
  body: {
    term: 'Slack',
    kind: 'brand',
    reviewSources: [
      { url: 'https://apps.apple.com/us/app/slack/id618783545', countries: ['us', 'gb'] },
      { url: 'https://play.google.com/store/apps/details?id=com.Slack', language: 'en' },
      { url: 'https://www.trustpilot.com/review/slack.com' },
    ],
  },
});
```

```python tab="Python"
rumoro.keywords.create(
    term="Slack",
    kind="brand",
    reviewSources=[
        {"url": "https://apps.apple.com/us/app/slack/id618783545", "countries": ["us", "gb"]},
        {"url": "https://play.google.com/store/apps/details?id=com.Slack", "language": "en"},
        {"url": "https://www.trustpilot.com/review/slack.com"},
    ],
)
```

```bash tab="curl"
curl -X POST https://api.rumoro.dev/v1/keywords \
  -H "Authorization: Bearer $RUMORO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "term": "Slack",
    "kind": "brand",
    "reviewSources": [
      { "url": "https://apps.apple.com/us/app/slack/id618783545", "countries": ["us", "gb"] },
      { "url": "https://play.google.com/store/apps/details?id=com.Slack", "language": "en" },
      { "url": "https://www.trustpilot.com/review/slack.com" }
    ]
  }'
```

| Field | Meaning |
| --- | --- |
| `url` | Link to an App Store or Google Play app, a Trustpilot page or a Google Maps place |
| `platform`, `id` | Use these instead of `url`. `appstore` takes the app's number, `googleplay` the package name (`com.Slack`), `trustpilot` the domain (`slack.com`), `googlemaps` a Place ID (`ChIJ...`) or CID. |
| `countries` | App Store and Google Play only. Two-letter codes of the storefronts to read, up to 20. Defaults to the one in the link, or `us`. Reviews are kept per storefront, so an app mostly reviewed in Germany needs `de`. |
| `language` | Google Play only. The review language to read, such as `en`, `es` or `pt-BR`. Defaults to the link's `hl`, or `en`. |

A keyword can have up to 10 review pages. `PATCH` replaces the whole list. Sending `[]` disconnects all pages and keeps the reviews already collected. The keyword's `reviewSources` shows each page's `platform`, `id`, `url`, `countries`, `language` and `connectedAt`.

You can follow a competitor's app the same way. Create a `competitor` keyword on their app and add an alert rule with `ratings: [1, 2]`, and you get a daily list of their unhappy users. Send `"platforms": []` to collect only the reviews, without posts that mention them.

```ts tab="TypeScript"
await rumoro.createKeyword({
  body: {
    term: 'Discord',
    kind: 'competitor',
    platforms: [],
    reviewSources: [{ url: 'https://apps.apple.com/us/app/discord-talk-play-hang-out/id985746746' }],
  },
});
```

```python tab="Python"
rumoro.keywords.create(
    term="Discord",
    kind="competitor",
    platforms=[],
    reviewSources=[{"url": "https://apps.apple.com/us/app/discord-talk-play-hang-out/id985746746"}],
)
```

```bash tab="curl"
curl -X POST https://api.rumoro.dev/v1/keywords \
  -H "Authorization: Bearer $RUMORO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "term": "Discord", "kind": "competitor", "platforms": [],
        "reviewSources": [{ "url": "https://apps.apple.com/us/app/discord-talk-play-hang-out/id985746746" }] }'
```

A keyword costs $5 a month either way, and each review is billed like any other mention.

## What a review looks like

A review is a normal mention with an extra `review` object.

```json
"review": {
  "rating": 2,
  "ratingMax": 5,
  "title": "Notifications stopped working",
  "version": "25.09.10",
  "country": "gb",
  "verified": null,
  "response": null,
  "responseAt": null,
  "app": { "platform": "appstore", "id": "618783545", "url": "https://apps.apple.com/gb/app/id618783545" }
}
```

`review` is `null` for posts that aren't reviews.

- `response` is the reply from the owner or developer. The App Store feed never includes one.
- `title` exists on the App Store and Trustpilot, `version` on both app stores, `verified` on Trustpilot.
- On the app stores `country` is the storefront. On Trustpilot it is where the reviewer lives.
- `app` is the review page, on every platform.

Reviews are scored differently from posts.

| | How it works |
| --- | --- |
| Relevance | Always relevant, because you chose the page. 95 for a `brand` keyword, 75 for `competitor`, 60 for `topic`. |
| Sentiment | From the stars, not the text. 4 and 5 stars are positive, 3 neutral, 1 and 2 negative. |
| Intents | Read from the text, so a review can be tagged `bug_report`, `churn_intent`, `pricing`, `praise` and so on. It also gets a language. |
| Date | `post.publishedAt` is the writing time, not the time Rumoro found the review. |

## Filter reviews

`ratings` keeps only reviews with the given star ratings, and `notRatings` removes them. Both work on `GET /v1/mentions`, exports, views and alert rules. Posts that aren't reviews never match `ratings`.

```ts tab="TypeScript"
const { data: page } = await rumoro.searchMentions({ query: { platforms: ['appstore', 'googleplay'], ratings: [1, 2] } });
```

```python tab="Python"
page = rumoro.mentions.search(platforms=["appstore", "googleplay"], ratings=[1, 2])
```

```bash tab="curl"
curl "https://api.rumoro.dev/v1/mentions?platforms=appstore,googleplay&ratings=1,2" \
  -H "Authorization: Bearer $RUMORO_API_KEY"
```

The CSV export adds two columns, `rating` and `app_id` (the page's id).

## Review report

`GET /v1/analytics/reviews` summarises your reviews over a period. It takes the same options as the other analytics reports (`range`, `from` and `to`, `timezone`, `keywordIds`, `platforms`, `compare`), plus `bucket` set to `day` or `week`.

```ts tab="TypeScript"
const { data: report } = await rumoro.getReviewsReport({ query: { range: '30d', compare: true } });
```

```python tab="Python"
report = rumoro.analytics.reviews(range_="30d", compare=True)
```

```bash tab="curl"
curl "https://api.rumoro.dev/v1/analytics/reviews?range=30d&compare=true" \
  -H "Authorization: Bearer $RUMORO_API_KEY"
```

| Part | Holds |
| --- | --- |
| `totals` | Review count, average rating, split by stars, replied reviews and open 1 and 2 star ones. Two keywords matching one review count it once. |
| `tags` | Tags on 1 and 2 star reviews, most common first. It shows what unhappy reviewers complain about. |
| `pages` | The same numbers per review page, with the average rating per day or week |
| `previous` | With `compare=true`, the numbers for the equal period before |

The same report is `rumoro analytics:reviews` in the CLI, `get_reviews_report` in MCP, and the **Reviews** page in the dashboard.

## How reviews are collected

- **Once a day** for each page (and each storefront on the app stores). Workspaces that follow the same page share the read. Each read also covers the two days before, because Apple lists some reviews late.
- **The first 30 days are free.** Connecting a page, or adding a storefront or language, brings in its 100 newest reviews from the last 30 days. They are not billed and don't trigger instant alerts, but they show in the feed and digests. This happens once per page per workspace. Disconnecting and reconnecting the page, or deleting the keyword and creating it again, brings no new free reviews. Two keywords on the same page share one free batch.
- **After that, each new review is billed** like any mention. A review seen in two storefronts counts once, and an edited review isn't counted again.
- **Each storefront is separate.** A keyword gets reviews from the storefronts it lists. A country added later starts that day, with its own free 30 days.
- **A muted keyword** stops collecting. When you unmute it, the two-day overlap can bring up to about three days of reviews written in the meantime, billed as new.
- **The monthly mention cap** applies to new reviews too, which limits the bill for a very busy app. The free reviews don't count toward it. Each page is read up to its 1,000 newest reviews a day.
