# Bumble Subscription Prices by Country and App Store Ratings (`zinin/bumble-app-intel`) Actor

Live Bumble Premium/Premium+/Boost/Spotlight/SuperSwipe/BumbleCoins prices by country from the App Store and Google Play, converted to USD, with ratings and change tracking between runs.

- **URL**: https://apify.com/zinin/bumble-app-intel.md
- **Developed by:** [Tim Zinin](https://apify.com/zinin) (community)
- **Categories:** Social media, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.00 / 1,000 storefront snapshots

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Bumble App Intel — Subscription Prices by Country and App Store Ratings

Bumble's own Premium and Boost subscription prices, BumbleCoins pack prices, App Store and
Google Play ratings, version and release notes, read straight from Bumble's own public store
listings for any country you name, converted to USD where a rate exists, and compared across
storefronts — no login, no API key, no profile data, only what Bumble Inc. itself already
publishes on its own store page for every visitor to see.

Bumble Inc. (NASDAQ: BMBL) discloses aggregate subscriber revenue in its own quarterly
filings, but not a per-country price list — the closest a market-facing team gets to that
without a subscription-cost audit team of their own is reading the same public storefront a
paying subscriber sees. At the time this README was written, Bumble Premium ran $17.99 in
the US and £14.99–£16.99 across several UK price points captured in the same session — a
comparison nobody at Bumble Inc. publishes in one place, which this Actor builds directly
from the storefront on a schedule.

### What you get

- **Bumble's own itemized plan list, exactly as its storefront prints it.** `inAppPurchases[]`
  on an App Store row lists every real entry that storefront's own page shows —
  `Bumble Premium`, `7 Days Bumble Premium`, `Bumble Boost`, `Pack of 1 BumbleCoins` — in that
  country's own currency, with no wording changed and no plan omitted. This is the identical
  list a paying subscriber would see on their own phone at the moment this Actor reads it.
- **planFamily normalizes every real variant this brand's own pages have shown.** `Bumble
  Premium` and `7 Days Bumble Premium` both collapse to `planFamily: "Premium"`, with
  `durationDays: 7` correctly parsed off the second name's own explicit duration word and
  left `null` on the first (which states no duration at all — see Field dictionary). `Bumble
  Boost` collapses to `planFamily: "Boost"` regardless of which of its several real price
  points a given storefront lists. `Pack of 1 BumbleCoins` collapses to `planFamily: "Coins"`.
  Anything this brand's dictionary doesn't recognize comes back as `planFamily: "other"` —
  never a guess dressed up as a fact.
- **A dictionary built for names this brand has not shown live yet, without guessing when it
  hasn't.** Bumble's own IAP list also documents a `Premium+` tier, a `Spotlight` boost and a
  `SuperSwipe` item on other Bumble-owned surfaces; none of the three has appeared in this
  Actor's own live captures of the US or UK App Store feed at the time this README was
  written. All three are already in this Actor's plan dictionary — `premium+` is checked
  BEFORE the plain `premium` key specifically so a future `Premium+` listing classifies
  correctly the day it appears, rather than being wrongly folded into the base `Premium`
  family — but this README will not claim they exist in a live feed they have not yet
  appeared in. If your own market shows one of these three, this Actor will already classify
  it correctly; this README simply won't show you a fabricated example of one.
- **USD conversion, honestly null when the source currency has no published rate.** Set
  `convertToUsd` (on by default) and every priced entry gets a `usd` field from yesterday's
  European Central Bank daily reference rate. GBP has a real ECB rate, so every UK price in
  this README's own examples carries a real `usd` figure; a handful of other currencies this
  line's brands can show elsewhere do not, and get `usd: null` with an explanatory `fxNote`
  instead of a silently wrong number.
- **Track price and rating changes over time, per storefront.** Set `compareWithPreviousRun`
  and a `watchId`, and every subsequent row for that pair carries a `changes[]` array of new
  plans, removed plans, price moves, rating moves of 0.05 or more, and version bumps.
- **Google Play's own real shape, not a forced match to the App Store's.** Google Play's
  public listing exposes one aggregate `iapPriceRange` (`$0.49–$259.99` at capture time)
  across every in-app item Bumble sells there, never named individual plans; the App Store's
  own listing does name them. This Actor reports each store's real, different shape rather
  than inventing a merged view neither store actually publishes.
- **Independent storefront reads.** Every `country × platform` pair is fetched on its own —
  one country's rating drop or rate limit never touches another country's row in the same
  run. Choose from one country to sixty, across App Store and Google Play.
- **Canonical-id validation before anything is trusted.** Every App Store page this Actor
  reads is checked against the numeric id it was asked for (`930441707`) before any field is
  parsed out of it — a redirect, a mislabeled page, or a storefront serving an unexpected
  app under that id fails the row closed as `source_error` rather than silently attaching
  Bumble's brand name to someone else's price list. This matters specifically because App
  Store ids are numeric and country-redirect behavior on Apple's own side has been observed
  (this line's Tinder README documents a real 301 case); this Actor never assumes a
  same-looking page is the right one without checking.
- **A run-level free summary, not just per-row data.** Every run writes an `OUTPUT` key to
  the key-value store — `storefrontsBilled`, `storefrontsFree`, `partial`,
  `rateLimitedStorefronts`, `notAvailableStorefronts` — so you can tell at a glance whether a
  run fully succeeded without reading every row in the dataset.
- Runs on Apify: schedule it, call it from the REST API or an SDK, wire it to a webhook, or
  export straight to JSON, CSV or Excel — see Integration recipes below.

### Who uses it

- **Equity and credit analysts covering Bumble Inc. (NASDAQ: BMBL).** Bumble is the company's
  flagship, majority-revenue brand. A pricing-power read across storefronts — did Premium's
  price move after a specific quarter, how does UK pricing compare to US pricing — is exactly
  the kind of primary-source input a research note cites, built from the same public page
  Bumble Inc. itself controls and updates.
- **Competitor product and pricing teams** at Tinder, Hinge, Badoo, Twinby and every other
  dating app that competes with Bumble for the same subscriber, who want the market's
  second-largest player's current Premium and Boost pricing before setting their own.
- **ASO and mobile-growth agencies** already tracking rating trend and review velocity for a
  portfolio that includes Bumble, who want subscription pricing folded into the same report
  on the same cadence, from a public-page read they already trust for the rest of the report.
- **Currency and purchasing-power researchers** using Bumble Premium's real, deliberately
  per-country-set price ladder as a comparable input alongside this line's other four
  brands, with no shipping or import-tax noise to strip out first, since digital subscription
  pricing has none.
- **Anyone tracking their own Bumble Premium cost** across a relocation or a trip, comparing
  their current billing country's price against a prospective new one via this Actor's own
  dataset history.
- **Investor-relations and comms teams at Bumble Inc. itself**, who may want an independent,
  externally-sourced confirmation of what the company's own storefronts actually show a
  given market at a given time — the same public data an outside analyst would see, useful
  for checking that a pricing change rolled out as intended across every storefront it was
  meant to reach.
- **App-store-optimization researchers studying review velocity against price changes** —
  this Actor's `ratingCount` field, read on the same schedule as pricing, gives a rough
  proxy for whether a price move coincided with a change in review momentum, without needing
  a separate review-scraping tool that would touch either store's disallowed review feed.

This Actor does **not** serve anyone looking for Bumble user profiles, photos, messages,
match data, swipe activity, or any other personal information about Bumble's own users — see
"What this is NOT" under Evidence and boundaries.

### How to run it

1. Click **Try for free** — no card required, and the default input below runs without any
   secret or account of your own.
2. Pick your **Countries** — ISO-3166 alpha-2 codes, lowercase. The default (`us`, `gb`)
   captures both real currencies this README's own examples use.
3. Pick your **Platforms** — `appstore`, `googleplay`, or both (the default). Unlike this
   line's Twinby Actor, there is no `rustore` option here — Bumble is not sold through
   RuStore, so this input simply doesn't offer a platform this app has no listing on.
4. Leave **Convert prices to USD** on unless you specifically want each storefront's own
   local currency and nothing else.
5. Press **Start**. The default input finishes in well under a minute; a longer country list
   against Apple, at this Actor's own two-request-at-a-time pace, takes longer — see
   Operating guide.
6. Pull the results from the **Dataset** tab, or read the free run-level summary from the
   **Key-value store** tab under the `OUTPUT` key.

### Pricing

Pay-per-event, on the platform's standard **PAY_PER_EVENT** model: **$0.005 charged once at
run start, plus $0.005 per storefront that came back with real data** (`status: "ok"`,
`found: true` — the platform's own internal event name for this is `result-found`). Every
other outcome is free: a country the app is not sold in (`not_available_in_country`), a
source that did not answer after this Actor's own retries (`rate_limited`), a real
parsing/source error (`source_error`), or an invalid country code (`invalid_country`).

The default prefill — two countries, two platforms, so up to four storefronts — resolves to
**$0.005 + up to 4 × $0.005 = up to $0.025**; Bumble has resolved successfully on every one
of those four combinations in this line's own testing, so this is the figure to expect in
practice. Ten resolved storefronts (five countries across both platforms) comes to
**$0.055**. `maxStorefronts` (default 10, maximum 120) caps how many `country × platform`
combinations one run will ever attempt, enforced before any request is made, with a free
`status: "truncated"` row naming exactly what was skipped — never a silent drop, never a
charge for work that never happened.

At startup, the Actor checks that result events are configured as paid events and that ordinary dataset writes are free. It reads the event price once at startup. Before each successful storefront row, it uses that price snapshot and reads the remaining run budget from the Apify SDK. It does not compare the configured prices with the numbers printed on this page. The live Pricing tab is authoritative; set a maximum run charge to bound your spend.

This Actor's own measured cost per run, at the default prefill size, is a small fraction of a
cent — well under the $0.005 charged for even a single storefront, let alone the combined
$0.005-plus-per-storefront a full run generates. The margin exists specifically to cover the
occasional free run (a heavily rate-limited attempt, an unlucky batch of `invalid_country`
codes) without the paying storefronts needing to individually subsidize every possible
failure mode on top of their own real compute cost.

### Input contract

| Field | Required | What it does |
|---|---|---|
| `countries` | no | ISO-3166 alpha-2 codes, lowercase, e.g. `["us","gb"]`. 1 to 60 codes, deduplicated regardless of casing/whitespace. An invalid code returns a free `invalid_country` row; a code the app genuinely is not sold in returns a free `not_available_in_country` row — both free, neither has appeared in this line's own live testing of Bumble across the countries checked so far (stated plainly, not papered over). Default: `["us"]`. |
| `platforms` | no | `appstore`, `googleplay`, or both. Default: both. |
| `convertToUsd` | no | When `true` (default), priced App Store entries get a `usd` field from yesterday's ECB rate; `null` with `fxNote` for a currency the ECB doesn't carry. When `false`, no FX lookup runs at all. |
| `compareWithPreviousRun` | no | When `true`, diffs each row against the last run of that exact `country`/`platform` pair under the same `watchId`. Default: `false`. |
| `watchId` | no | Names which saved snapshot history to diff against. Pattern: `[a-zA-Z0-9_-]{1,40}`. Default: `"default"`. |
| `maxStorefronts` | no | Hard ceiling on `countries × platforms`, enforced before any request. Range: 1 to 120. Default: 10. |

```json
{
    "countries": [
        "us",
        "gb"
    ],
    "platforms": [
        "appstore",
        "googleplay"
    ],
    "convertToUsd": true
}
```

### Real happy, partial and failure output

One row per `country × platform` pair. Every example below is real and unedited, copied
as-is from an actual run of this exact code.

#### Happy path: a storefront that came back with real, priced data

**App Store, United States.** The full plan list this page carried at capture time — note
`Bumble Boost` appearing at several distinct price points and `Pack of 1 BumbleCoins`
correctly landing on its own `Coins` family:

```json
{
    "recordType": "storefront_snapshot",
    "brand": "bumble",
    "platform": "appstore",
    "country": "us",
    "appId": "930441707",
    "observedAt": "2026-09-26T15:10:59.160Z",
    "status": "ok",
    "found": true,
    "appTitle": "Bumble Dating App: Meet & Date App",
    "rating": 4.3,
    "ratingCount": 1763432,
    "ratingHistogram": {
        "1": 123990,
        "2": 57457,
        "3": 152558,
        "4": 232406,
        "5": 1197021
    },
    "version": "5.480.0",
    "lastUpdated": "2026-09-21T07:52:18.000Z",
    "inAppPurchases": [
        {
            "name": "Bumble Boost",
            "priceText": "$8.99",
            "amount": 8.99,
            "currency": "USD",
            "planFamily": "Boost",
            "durationDays": null,
            "usd": 8.99
        },
        {
            "name": "Bumble Boost",
            "priceText": "$10.99",
            "amount": 10.99,
            "currency": "USD",
            "planFamily": "Boost",
            "durationDays": null,
            "usd": 10.99
        },
        {
            "name": "Bumble Boost",
            "priceText": "$9.99",
            "amount": 9.99,
            "currency": "USD",
            "planFamily": "Boost",
            "durationDays": null,
            "usd": 9.99
        },
        {
            "name": "Bumble Premium",
            "priceText": "$17.99",
            "amount": 17.99,
            "currency": "USD",
            "planFamily": "Premium",
            "durationDays": null,
            "usd": 17.99
        },
        {
            "name": "Bumble Boost",
            "priceText": "$2.99",
            "amount": 2.99,
            "currency": "USD",
            "planFamily": "Boost",
            "durationDays": null,
            "usd": 2.99
        },
        {
            "name": "Pack of 1 BumbleCoins",
            "priceText": "$1.99",
            "amount": 1.99,
            "currency": "USD",
            "planFamily": "Coins",
            "durationDays": null,
            "usd": 1.99
        },
        {
            "name": "Bumble Premium",
            "priceText": "$19.99",
            "amount": 19.99,
            "currency": "USD",
            "planFamily": "Premium",
            "durationDays": null,
            "usd": 19.99
        },
        {
            "name": "Bumble Boost",
            "priceText": "$12.99",
            "amount": 12.99,
            "currency": "USD",
            "planFamily": "Boost",
            "durationDays": null,
            "usd": 12.99
        },
        {
            "name": "Bumble Boost",
            "priceText": "$7.99",
            "amount": 7.99,
            "currency": "USD",
            "planFamily": "Boost",
            "durationDays": null,
            "usd": 7.99
        },
        {
            "name": "Bumble Premium",
            "priceText": "$32.99",
            "amount": 32.99,
            "currency": "USD",
            "planFamily": "Premium",
            "durationDays": null,
            "usd": 32.99
        }
    ],
    "planSummary": {
        "Boost": {
            "minAmount": 2.99,
            "maxAmount": 12.99,
            "currency": "USD",
            "minUsd": 2.99,
            "maxUsd": 12.99
        },
        "Premium": {
            "minAmount": 17.99,
            "maxAmount": 32.99,
            "currency": "USD",
            "minUsd": 17.99,
            "maxUsd": 32.99
        },
        "Coins": {
            "minAmount": 1.99,
            "maxAmount": 1.99,
            "currency": "USD",
            "minUsd": 1.99,
            "maxUsd": 1.99
        }
    },
    "fx": {
        "source": "ECB",
        "date": "2026-09-25",
        "rate": 1
    },
    "error": "",
    "sourceUrl": "https://apps.apple.com/us/app/id930441707",
    "changes": []
}
```

**App Store, United Kingdom — a second currency, and the real `7 Days Bumble Premium`
duration-stating name alongside the plain, no-duration `Bumble Premium`.** Both land on
`planFamily: "Premium"`; only the one whose own name states a duration gets a non-null
`durationDays`:

```json
{
    "recordType": "storefront_snapshot",
    "brand": "bumble",
    "platform": "appstore",
    "country": "gb",
    "appId": "930441707",
    "observedAt": "2026-09-26T15:10:59.653Z",
    "status": "ok",
    "found": true,
    "appTitle": "Bumble Dating app: Chat & Date App",
    "rating": 4.2,
    "ratingCount": 275220,
    "ratingHistogram": {
        "1": 19902,
        "2": 10500,
        "3": 30956,
        "4": 45469,
        "5": 168393
    },
    "version": "5.480.0",
    "lastUpdated": "2026-09-21T07:52:18.000Z",
    "inAppPurchases": [
        {
            "name": "Bumble Boost",
            "priceText": "£7.49",
            "amount": 7.49,
            "currency": "GBP",
            "planFamily": "Boost",
            "durationDays": null,
            "usd": 9.926024
        },
        {
            "name": "Bumble Boost",
            "priceText": "£8.99",
            "amount": 8.99,
            "currency": "GBP",
            "planFamily": "Boost",
            "durationDays": null,
            "usd": 11.913879
        },
        {
            "name": "Bumble Boost",
            "priceText": "£10.99",
            "amount": 10.99,
            "currency": "GBP",
            "planFamily": "Boost",
            "durationDays": null,
            "usd": 14.564352
        },
        {
            "name": "Bumble Premium",
            "priceText": "£14.99",
            "amount": 14.99,
            "currency": "GBP",
            "planFamily": "Premium",
            "durationDays": null,
            "usd": 19.865299
        },
        {
            "name": "Pack of 1 BumbleCoins",
            "priceText": "£1.99",
            "amount": 1.99,
            "currency": "GBP",
            "planFamily": "Coins",
            "durationDays": null,
            "usd": 2.637221
        },
        {
            "name": "Bumble Premium ",
            "priceText": "£13.99",
            "amount": 13.99,
            "currency": "GBP",
            "planFamily": "Premium",
            "durationDays": null,
            "usd": 18.540063
        },
        {
            "name": "Bumble Premium",
            "priceText": "£16.99",
            "amount": 16.99,
            "currency": "GBP",
            "planFamily": "Premium",
            "durationDays": null,
            "usd": 22.515773
        },
        {
            "name": "7 Days Bumble Premium",
            "priceText": "£9.99",
            "amount": 9.99,
            "currency": "GBP",
            "planFamily": "Premium",
            "durationDays": 7,
            "usd": 13.239116
        },
        {
            "name": "Bumble Boost",
            "priceText": "£2.49",
            "amount": 2.49,
            "currency": "GBP",
            "planFamily": "Boost",
            "durationDays": null,
            "usd": 3.29984
        },
        {
            "name": "7 Days Bumble Premium",
            "priceText": "£24.99",
            "amount": 24.99,
            "currency": "GBP",
            "planFamily": "Premium",
            "durationDays": 7,
            "usd": 33.117667
        }
    ],
    "planSummary": {
        "Boost": {
            "minAmount": 2.49,
            "maxAmount": 10.99,
            "currency": "GBP",
            "minUsd": 3.29984,
            "maxUsd": 14.564352
        },
        "Premium": {
            "minAmount": 9.99,
            "maxAmount": 24.99,
            "currency": "GBP",
            "minUsd": 13.239116,
            "maxUsd": 33.117667
        },
        "Coins": {
            "minAmount": 1.99,
            "maxAmount": 1.99,
            "currency": "GBP",
            "minUsd": 2.637221,
            "maxUsd": 2.637221
        }
    },
    "fx": {
        "source": "ECB",
        "date": "2026-09-25",
        "rate": 1.32523679
    },
    "error": "",
    "sourceUrl": "https://apps.apple.com/gb/app/id930441707",
    "changes": []
}
```

**Google Play, United States — the same app, this store's genuinely different shape.** One
aggregate price range, no named plans, real rating and install-count data:

```json
{
    "recordType": "storefront_snapshot",
    "brand": "bumble",
    "platform": "googleplay",
    "country": "us",
    "appId": "com.bumble.app",
    "observedAt": "2026-09-26T15:10:59.164Z",
    "status": "ok",
    "found": true,
    "appTitle": "Bumble Dating App: Meet & Date",
    "rating": 3.775726079940796,
    "ratingCount": 1616090,
    "installs": "100M+",
    "lastUpdated": "2026-09-18T00:00:00.000Z",
    "containsAds": false,
    "iapPriceRange": {
        "min": 0.49,
        "max": 259.99,
        "currency": "USD"
    },
    "inAppPurchases": [],
    "fx": {
        "source": "ECB",
        "date": "2026-09-25",
        "rate": 1
    },
    "error": "",
    "sourceUrl": "https://play.google.com/store/apps/details?id=com.bumble.app&hl=en&gl=us",
    "changes": []
}
```

#### Failure path 1: a malformed country code, rejected before any request

```json
{
    "recordType": "storefront_snapshot",
    "brand": "bumble",
    "platform": "appstore",
    "country": "zz",
    "status": "invalid_country",
    "found": false,
    "error": "",
    "sourceUrl": null
}
```

#### Failure path 2: the source did not answer after retries

**A simulated exhausted-retries 429, run through the real code path** — captured via the same
`fetchImpl` injection point this line's own tests use (the technique is described in full in
this line's Tinder README): the real `runDatingAppIntel` entry point and the real Apify
`ChargingManager`, with only the network call itself scripted:

```json
{
    "recordType": "storefront_snapshot",
    "brand": "bumble",
    "platform": "appstore",
    "country": "us",
    "appId": "930441707",
    "observedAt": "2026-09-26T16:06:47.979Z",
    "appTitle": null,
    "rating": null,
    "ratingCount": null,
    "ratingHistogram": null,
    "installs": null,
    "version": null,
    "lastUpdated": null,
    "releaseNotes": null,
    "containsAds": null,
    "iapPriceRange": null,
    "inAppPurchases": [],
    "planSummary": {},
    "fx": null,
    "fxNote": null,
    "sourceUrl": "https://apps.apple.com/us/app/id930441707",
    "status": "rate_limited",
    "found": false,
    "error": "http 429 after retries"
}
```

### Field dictionary

| Field | Type | Meaning |
|---|---|---|
| `recordType` | string | Always `"storefront_snapshot"`. |
| `brand` | string | Always `"bumble"` here. |
| `platform` | string | `"appstore"` or `"googleplay"`. |
| `country` | string | The ISO-3166 code requested. |
| `appId` | string | `930441707` (App Store numeric id) or `com.bumble.app` (Google Play package name), always the same regardless of country. |
| `observedAt` | string (ISO 8601) | The exact moment this Actor read the page, in UTC. |
| `status` | string | `ok`, `not_available_in_country`, `invalid_country`, `rate_limited` or `source_error`. |
| `found` | boolean | `true` only for `status: "ok"`. |
| `appTitle` | string | null | The store's own title — `"Bumble Dating app: Chat & Date"` on the App Store, `"Bumble Dating App: Meet & Date"` on Google Play (the two storefronts' own titles genuinely differ in wording, not a parsing inconsistency on this Actor's part). |
| `rating` | number | null | The store's own current average rating out of 5. |
| `ratingCount` | number | null | Total ratings behind that average — over 1.6 million on the US Google Play listing at capture time. |
| `ratingHistogram` | object | null | App Store only. |
| `installs` | string | null | Google Play only — `"100M+"` at capture time. |
| `version` / `lastUpdated` / `releaseNotes` | string | null | The listed version, its last-update date, and the "What's new" text in the storefront's own language. |
| `containsAds` | boolean | null | Google Play only. |
| `iapPriceRange` | object | null | Google Play only — `{min, max, currency}` aggregate across every in-app item. |
| `inAppPurchases` | array | App Store only. |
| `inAppPurchases[].name` | string | The plan's own raw name — `"Bumble Premium"`, `"7 Days Bumble Premium"`, `"Bumble Boost"`, `"Pack of 1 BumbleCoins"`. |
| `inAppPurchases[].planFamily` | string | See the plan dictionary table below. |
| `inAppPurchases[].durationDays` | number | null | `7` for `"7 Days Bumble Premium"`, parsed from its own explicit duration word; `null` for the plain `"Bumble Premium"` and for every `Boost`/`Coins` entry, none of which state a duration. |
| `inAppPurchases[].usd` | number | null | `amount` converted via yesterday's ECB rate when `convertToUsd` is on and the ECB carries that currency — GBP always does; `null` with `fxNote` otherwise. |
| `planSummary` | object | Per-`planFamily` min/max within this row. |
| `fx` / `fxNote` | object|null / string|null | The ECB rate used, or why none was available. |
| `error` | string | Empty for a clean "no"; a specific message for `rate_limited`/`source_error`. |
| `sourceUrl` | string | null | The exact URL fetched; `null` only for `invalid_country`. |
| `changes[]` | array | Populated only with `compareWithPreviousRun: true`. |

**Longest match wins, and that ordering is deliberate, not incidental.** This Actor's own
matcher checks `premium+` against a plan's raw name before it ever checks the plain
`premium` key, specifically because any name containing `"premium+"` also, trivially,
contains `"premium"` as a substring — without checking the longer, more specific key first,
a future `"Bumble Premium+"` listing would silently be misclassified as the base `Premium`
family instead of its own distinct `PremiumPlus` family. This same longest-match-wins rule
is what lets this brand's dictionary stay a flat, unordered list rather than a carefully
hand-sorted one: the matching logic itself (shared across this entire line, in `lib/price.js`)
guarantees the more specific key wins regardless of which order the two appear in the file.

#### Plan dictionary — how `planFamily` is decided

| planFamily | Matched from (case-insensitive substring, longest match wins) | Observed live? |
|---|---|---|
| `PremiumPlus` | `"premium+"` — checked before the plain `premium` key below so it wins on any name containing both | Not yet — kept in the dictionary per SPEC.md §4 so it classifies correctly the day it appears |
| `Premium` | `"premium"` (covers `"Bumble Premium"`, `"7 Days Bumble Premium"`) | Yes, both US and GB, at capture time |
| `Boost` | `"boost"` | Yes, several price points, both storefronts |
| `Coins` | `"bumblecoins"` | Yes (`"Pack of 1 BumbleCoins"`) |
| `Spotlight` | `"spotlight"` | Not yet observed live |
| `SuperSwipe` | `"superswipe"` | Not yet observed live |
| `other` | anything not matching the above | — |

### Evidence and boundaries

**What this Actor reads, exactly.** Two public product pages:
`https://apps.apple.com/{cc}/app/id930441707` and
`https://play.google.com/store/apps/details?id=com.bumble.app&hl=en&gl={cc}`.

**robots.txt, checked, not assumed.** apps.apple.com disallows `/api/*`, `/v1/*`,
`/WebObjects/*`, `/includes/*`, `*/search?*`. play.google.com disallows `/store/getreviews`,
`/store/search`, `/store/xhr`, `/store/apps/datasafety*`. This Actor's own fetched paths are
not among either domain's disallowed set.

**Premium+, Spotlight and SuperSwipe are real Bumble Inc. product names, honestly reported as
"not yet observed live" rather than demonstrated with an invented example.** This Actor's own
plan dictionary already classifies all three correctly (verified by this line's own synthetic
tests, not a live capture) specifically so the day one appears in a live App Store feed, it is
recognized immediately rather than falling through to `other` while someone notices and files
a fix. This README will not show you a JSON example of a plan that has not actually appeared
in a real, captured page, because that would not be a real example — it is disclosed here as
a known, honest gap instead.

**429 is a measured risk, not a guess.** From this line's own test infrastructure's IP range,
hitting Apple's App Store every 8 seconds produced 5 real `200` responses and 7 real `429`s
out of 12 consecutive requests. The same IP, paced at 30 seconds between requests with a
45-second retry after any 429, produced 5 real `200`s out of 5. This Actor's own fetch layer
caps concurrent requests toward Apple at 2 per run and retries with backoff before reporting
`rate_limited`.

**What the App Store's own feed does NOT publish.** No age rating, no install count (Google
Play's own disclosure, not Apple's), and no duration for a plan whose own name doesn't state
one — `"Bumble Premium"` alone has `durationDays: null` for exactly this reason, not because
this Actor failed to find a length that the source itself never printed.

**What this Actor does NOT do, and will not be extended to do.** No account, no login, no
profile/photo/message/match/swipe data of any real Bumble user, no review-feed reads (both
domains' review endpoints are robots.txt-disallowed and never fetched), no store search
(every app identifier is hard-coded per brand), no private API of Bumble Inc.'s, Apple's or
Google's. Permanent design boundaries, not missing features.

**Why "Bumble Dating app: Chat & Date" and "Bumble Dating App: Meet & Date" are both correct
and neither is a typo.** The App Store's own title for this app and Google Play's own title
for the same app genuinely differ in wording and even in mid-string capitalization ("app" vs
"App") — this Actor reports each store's title exactly as that store prints it, rather than
normalizing them to look consistent, because doing so would mean silently editing what the
source itself actually says.

**Google Play's rating is a floating-point number with many decimal places on purpose.**
`3.775726079940796` is not a display bug — Google Play's own page embeds the rating at that
precision in its structured data, and this Actor passes it through unrounded rather than
deciding on your behalf how many decimal places matter for your use case.

### Decision routing

**Use this Actor when** you specifically want Bumble's own subscription prices, normalized
into a stable `planFamily`, with an app-title/rating/version read attached, from the App
Store and Google Play as the two sources.

**A bare page-fetch Actor with no brand logic won't give you `planFamily` or FX-honest
`usd` fields at all** — if that's genuinely all you need (the raw HTML or a generic
App-Store/Google-Play field extraction with no dating context), a general-purpose store
scraper will be cheaper per storefront; this Actor charges more per storefront specifically
because it adds the plan-dictionary, duration-parsing and currency-conversion layers on top.

**Use one of this Actor's own siblings instead of Bumble** when the brand you care about is
different: the same engine also ships as separate Actors for Tinder, Twinby (adds RuStore),
Hinge and Badoo — Badoo shares Bumble Inc. as its own parent company but has an entirely
separate plan dictionary (Premium/Super Powers/Credits, no Premium+/Boost-coins naming
overlap with Bumble at all).

**Use `compareWithPreviousRun` rather than diffing two separate manual exports yourself**
whenever you need a "what changed" answer — the diff is computed from the same real, saved
snapshot this Actor already keeps per `watchId`, so it's not vulnerable to the easy mistake
of comparing two exports that were taken under different `convertToUsd` settings, or two
country lists that don't actually line up row for row.

**Do not use this Actor** for anything about real Bumble users — profiles, matches, messages,
swipe activity. No configuration will ever do that; it is a permanent boundary.

### Commercial playbooks

**Quarterly pricing-power tracking ahead of a Bumble Inc. earnings call.** An equity research
desk schedules this Actor weekly against Bumble Inc.'s disclosed core markets, with
`compareWithPreviousRun` on and a stable `watchId`, building a real time series of Premium
and Boost pricing per country ahead of each quarterly filing — a dated, sourced comparison
the company's own investor materials don't provide at this granularity.

**Competitive benchmarking before a Boost or Premium price change at a rival app.** A product
team at Tinder, Hinge, Badoo or Twinby reads Bumble's current price points for the exact
countries they're about to reprice in, as one direct input into their own pricing decision,
same-day rather than after a multi-hour manual App Store visit across country settings.

**ASO agency monthly client report.** An agency already tracking a client's own rating trend
adds a Bumble-specific competitive-pricing line item, refreshed on the same monthly cadence,
with zero extra manual research time per client.

**Currency and purchasing-power research.** A researcher uses Bumble Premium's real
per-country price ladder, alongside this line's other four brands, as a clean comparable
input into a broader digital-subscription pricing study.

**Pre-earnings sanity check for an IR or comms team.** Before a quarterly filing references
subscriber pricing or a pricing-related initiative, a Bumble Inc. IR or comms team runs this
Actor across the company's own disclosed key markets to confirm what is actually live on
every storefront that a filing or an analyst call might reference, rather than relying on
whatever the internal pricing team's last change ticket said should have shipped.

**Tracking Premium+/Spotlight/SuperSwipe rollout, the moment it happens.** A team that wants
to know the exact day Bumble Inc. starts showing one of these three not-yet-observed plan
names in a given country's App Store feed can schedule this Actor against that country and
watch for the first `changes[]` entry naming a new `planFamily` — since the dictionary
already recognizes all three, the first live appearance is caught on day one, not discovered
weeks later by chance.

### Integration recipes

**Schedule it (Apify Console).** Open the Schedules tab, create a cron expression (`0 8 * * 1`
for weekly Monday runs is a common choice), attach the input you want repeated, and every
scheduled run lands in this Actor's own dataset history, automatically diffed against the
run before it when `compareWithPreviousRun` is on.

**Call it from the REST API.**

```bash
curl -X POST "https://api.apify.com/v2/acts/zinin~bumble-app-intel/runs?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"countries": ["us", "gb"], "platforms": ["appstore", "googleplay"], "convertToUsd": true}'
```

**Call it from Node.js or Python via Apify's own SDK.**

```js
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('zinin/bumble-app-intel').call({
    countries: ['us', 'gb'],
    platforms: ['appstore', 'googleplay'],
    convertToUsd: true,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

The Python client (`apify-client` on PyPI) mirrors the same call shape:
`client.actor('zinin/bumble-app-intel').call(run_input={...})`.

**Wire it to a webhook.** Attach a webhook fired on `ACTOR.RUN.SUCCEEDED` (via this Actor's
page or `POST /v2/webhooks`) to your own endpoint. Check `OUTPUT.partial` in the payload,
since a run can finish `SUCCEEDED` while one storefront came back `rate_limited`.

**Call it as a tool from an LLM agent, via Apify's MCP server** (`https://mcp.apify.com`) —
once published, callable by name with the same input shape as any other path here.

**Export straight to Google Sheets.** Use the Dataset tab's "Export data" action and choose
Google Sheets, or a Sheets-sync automation watching for `ACTOR.RUN.SUCCEEDED`, to keep a live
spreadsheet updated on the same schedule as your scheduled runs.

**Plain CSV/Excel for a one-off analysis.** The Dataset tab's **Export** button produces a
ready-to-open CSV or Excel file of exactly the rows in that run, one per `country × platform`
pair.

### Operating guide

**Reading a partial run.** Check `OUTPUT.partial` first, not the run's own top-level status —
a run can finish successfully while some storefronts came back `rate_limited` or
`source_error`. `partial: true` plus a non-empty `rateLimitedStorefronts` array names exactly
which `country:platform` pairs to re-run, at no extra charge for the ones that already
succeeded.

**When to retry a rate-limited storefront.** A single `rate_limited` row on an otherwise
successful run is usually transient at this Actor's own request pacing (see Evidence and
boundaries). Waiting a few minutes and re-running just that storefront is normally enough; a
storefront that stays `rate_limited` across several attempts over an hour or more is worth
treating as a real, ongoing block rather than transient noise.

**Choosing a `watchId` strategy.** One `watchId` per logical tracking list — a "BMBL investor
coverage" watch and a "competitive Boost pricing" watch should use different `watchId`s so
their `changes[]` histories never mix, even though reusing one won't corrupt the underlying
data (the diff is keyed per `country`/`platform`/`watchId`, not per whole run).

**maxStorefronts on a large country list.** Combinations beyond the cap are dropped before
any request is made and reported in one free `status: "truncated"` row naming exactly what
was skipped. Raise `maxStorefronts` (up to 120) for a genuinely large single run, or split a
long country list across a few scheduled runs to keep `OUTPUT.deadlineHit` `false` (the soft
280-second budget on the prefill-sized path).

**Interpreting a mixed `not_available_in_country` and `ok` result across a country list.**
If a future run shows Bumble unavailable in a specific country after previously being
available there, that is real information worth investigating on its own — a genuine market
exit is a different story than a temporary technical hiccup, and this Actor's `status` field
already distinguishes the two (a real "not sold here" answer vs. a `rate_limited`/
`source_error` technical failure) so you don't have to guess which one you're looking at.

**Comparing US and UK pricing side by side.** Both currencies convert cleanly to USD via the
ECB rate when `convertToUsd` is on, so a direct `usd` field comparison across the two rows is
safe without any extra work on your end — this is specifically NOT true for every brand in
this line (Twinby's RUB rows never get a `usd` figure at all), so double-check which
currencies are actually involved before assuming a cross-country USD comparison is valid.

### FAQ

**Why haven't Premium+, Spotlight or SuperSwipe shown up in any example on this page?**
Because they haven't appeared in this Actor's own live captures of the US or UK App Store
feed yet, and this README only ever shows real, captured examples — never an invented one.
All three are already correctly classified by this Actor's plan dictionary (verified by
synthetic tests) and will show up here the moment a live capture actually contains one.

**I need Bumble's Premium price from Google Play, but `inAppPurchases[]` is empty on that
row — where is it?** It isn't there, because Google Play's own public listing for Bumble
never names individual plans — only one aggregate `iapPriceRange` (`min`/`max`/`currency`)
across every in-app item the app sells. If you need a named Premium price specifically, read
the App Store row instead; this Actor reports each store's real, genuinely different shape
rather than forcing Google Play's aggregate into a named-plan shape it doesn't actually have.

**My run shows `invalid_country` for one code — does that mean Bumble isn't sold there?** No
— it means the code itself was never a real ISO-3166 territory in the first place, so no
request was ever sent to either store at all. That's a different, distinct status from
`not_available_in_country` (a real country the store genuinely doesn't sell into), which
Bumble has not actually triggered in this line's own testing so far — worth knowing before
you assume a specific country is excluded from Bumble's own market coverage.

**Does this Actor read anything about real Bumble users?** No, and it never will. It reads
one public product page per country per platform, with no login, no account, and no code
path that could reach a profile, a photo, a message or any match/swipe data belonging to a
real person.

**If my US run gets rate-limited, does that affect the GB row in the same run?** No — each
`country × platform` combination is fetched independently, so a `rate_limited` result for
`appstore:us` leaves `appstore:gb` completely unaffected in the same run; you'd see one free
row and one normal, billed row side by side in the same dataset.

**Bumble Boost showed a different price the second time I ran this — is that a bug?** Not
necessarily — Apple and Google both run real promotional pricing on Boost from time to time,
and this Actor reports whatever price is live at the moment of the read. Use
`compareWithPreviousRun` to confirm a genuine change rather than assuming either run was
wrong; a documented price MOVE is exactly what that feature is built to catch.

**Why is `durationDays` null for "Bumble Premium" but not for "7 Days Bumble Premium"?**
Because the second name states its own duration in words and the first doesn't — this
Actor's duration parser only ever fires on an explicit day/week/month/year unit word inside
the plan's own name, and never infers a duration from context, price, or which family a plan
belongs to. A plain "Bumble Premium" with no duration word gets `null`, honestly, rather than
an assumed "probably monthly" guess.

**Can I run this for a Bumble Inc.-owned brand other than Bumble?** Yes, but not with this
Actor — Badoo is also owned by Bumble Inc. and has its own separate Actor in this line
(`badoo-app-intel`), with its own distinct plan dictionary (Premium/Super Powers/Credits).
The two Actors do not share a brand configuration, since the two apps' own IAP naming does
not overlap.

**Is the `iapPriceRange` on the Google Play row the same thing as the App Store's
`inAppPurchases[]` list?** No — `iapPriceRange` is one aggregate `{min, max, currency}` across
every in-app item Google Play's page discloses for this app, with no plan names attached.
`inAppPurchases[]` is the App Store's own named, itemized list. The two fields exist because
the two stores genuinely disclose different things, not because this Actor picked one shape
and forced the other store into it.

**Why does this README show fewer JSON examples than some sibling Actors in this line?**
It doesn't withhold anything real — every status this brand has actually produced live is
shown; the ones that aren't shown (`not_available_in_country` specifically) simply haven't
happened yet in this line's own testing of Bumble, and this README says so directly under
Evidence and boundaries rather than manufacturing a placeholder example to fill the gap.

**Does a higher `ratingCount` mean Bumble is more popular than a sibling brand in this
line?** Not necessarily on its own — `ratingCount` reflects how many people left a rating on
that specific storefront, which correlates with but is not identical to total install base
or active-user count (Google Play's separate `installs` field is a bucketed estimate of the
latter, and the two numbers answer genuinely different questions). Treat both as one
data point each, not as a single combined popularity score this Actor computes for you.

### Related app intelligence tools

- [App Store App Intel](https://apify.com/zinin/appstore-app-intel) — Inspect another iOS app by app ID or App Store URL.
- [Google Play App Intel](https://apify.com/zinin/googleplay-app-intel) — Get a public Google Play app card by package ID or URL.
- [App Store Customer Reviews](https://apify.com/zinin/appstore-customer-reviews-scraper) — Collect public App Store review text for separate review analysis.

### Sources and rights

Every fact in this Actor's output comes from one of two public sources: Apple's own App
Store product page at `apps.apple.com/{cc}/app/id930441707`, and Google's own Google Play
product page at `play.google.com/store/apps/details?id=com.bumble.app`. Both are pages any
visitor can open with no account; this Actor respects both domains' published `robots.txt`
and calls no private, undocumented or authenticated API belonging to Apple, Google or Bumble
Inc.

Currency conversion, when requested, uses the European Central Bank's own published daily
reference rate feed — public data, no key or account required.

This Actor collects no personal data of any kind and has no code path that reads, infers or
stores anything about a real Bumble user. "Bumble" and its plan names (Premium, Boost,
BumbleCoins) are Bumble Inc.'s own trademarks and terminology, reported here only because
they are the storefront's own public labels for its own public prices — this Actor is
independent and not affiliated with, endorsed by, or operated on behalf of Bumble Inc., Apple
or Google.

Found a wrong result, or need a case this README doesn't cover? Open an issue on this Actor's
own page.

This Actor's own source code respects the same public-data-only boundary this page describes
throughout: every field it can produce traces back to one of the two URLs above, every
currency conversion traces back to the ECB's own published feed, and no field is ever
populated from an assumption, a cached guess, or a value carried over from a previous run
when the current run's own source didn't actually provide it. That is the standard this
README holds itself to, and the standard every other Actor in this same line is held to as
well.

***

Built by [zinin](https://apify.com/zinin). Questions? Telegram [@timzinin](https://t.me/timzinin).

# Actor input Schema

## `countries` (type: `array`):

Storefront countries to check, ISO-3166 alpha-2 codes (e.g. `us`, `gb`, `de`). 1-60 codes; duplicates are removed; a code that isn't a real ISO country produces a free `invalid_country` row.

## `platforms` (type: `array`):

Which storefronts to check for each country.

## `convertToUsd` (type: `boolean`):

Add a `usd` field to every in-app purchase using the ECB daily reference rate. When the ECB has no rate for a storefront's currency, `usd` is left null rather than guessed — the local-currency price is still delivered.

## `compareWithPreviousRun` (type: `boolean`):

Diff each storefront's prices/rating/version against the last run of this Actor with the same `watchId` (stored in your account's key-value store, isolated per buyer). First run for a `watchId` reports a clean baseline, not a false "change".

## `watchId` (type: `string`):

Label for the comparison baseline (letters, digits, `-`, `_`, up to 40 chars). Use different watch IDs to track separate country/platform sets independently.

## `maxStorefronts` (type: `integer`):

Safety cap on countries x platforms for this run. Extra combinations are dropped (not charged for) before any work starts.

## Actor input object example

```json
{
  "countries": [
    "us",
    "gb"
  ],
  "platforms": [
    "appstore",
    "googleplay"
  ],
  "convertToUsd": true,
  "compareWithPreviousRun": false,
  "watchId": "default",
  "maxStorefronts": 10
}
```

# Actor output Schema

## `results` (type: `string`):

API URL for the default dataset items produced by this run.

## `summary` (type: `string`):

Free per-run summary (counts, rate-limited/skipped storefronts, FX status).

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "countries": [
        "us",
        "gb"
    ],
    "platforms": [
        "appstore",
        "googleplay"
    ],
    "convertToUsd": true,
    "maxStorefronts": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("zinin/bumble-app-intel").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "countries": [
        "us",
        "gb",
    ],
    "platforms": [
        "appstore",
        "googleplay",
    ],
    "convertToUsd": True,
    "maxStorefronts": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("zinin/bumble-app-intel").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "countries": [
    "us",
    "gb"
  ],
  "platforms": [
    "appstore",
    "googleplay"
  ],
  "convertToUsd": true,
  "maxStorefronts": 10
}' |
apify call zinin/bumble-app-intel --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,zinin/bumble-app-intel"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/kuPvDiVbg7sBwaODh/builds/OMpmu5gsfmgcOcZV5/openapi.json
