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

Live Hinge+, HingeX, Membership, Roses and Boost 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/hinge-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

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

Hinge's own Hinge+, Membership, Boost and Roses prices, read straight from Hinge's own App
Store and Google Play listings for any country you name, converted to USD where a rate
exists, and compared across storefronts, plus the app's rating, version and release notes —
no login, no API key, no profile data, only what Hinge itself already publishes on its own
public store page for every visitor to see.

Hinge — "designed to be deleted" is the brand's own tagline — is owned by Match Group
(NASDAQ: MTCH), the same company behind Tinder, and prices its subscription tiers
differently across markets: at the time this README was written, Hinge+ ran $16.99–$19.99 in
the US and a plain, tier-less `"Hinge Subscription"` line item appeared on both the US and
UK App Store feeds at three separate price points with no tier name attached at all — a real
labeling quirk this Actor handles deliberately rather than by guessing, described in full
below.

### What you get

- **Hinge's own full in-app-purchase list, item by item, not a marketing summary.**
  `inAppPurchases[]` on an App Store row carries every plan that storefront's own page
  actually lists — `Hinge+ Subscription`, `Membership`, `Boost`, `Bundle of three Roses` — at
  that country's own price, exactly as printed. This is what a paying subscriber sees on
  their own phone at the moment this Actor reads the page, nothing added or summarized.
- **A deliberate, honest refusal to guess what "Hinge Subscription" means.** Hinge's own App
  Store feed lists a plain `"Hinge Subscription"` entry — captured live on both the US and UK
  pages, at three separate price points on each — with no tier word ("+", "X", "Plus")
  anywhere in the name. This Actor's plan dictionary does NOT contain a `"hinge subscription"`
  key at all, by deliberate design: that name alone does not say which tier it actually is,
  and guessing would mean inventing a fact the source itself doesn't state. It classifies as
  `planFamily: "other"` — reported honestly, not silently dropped and not forced into
  `HingeX` or `HingePlus` just because "Hinge" is in the name. See Field dictionary and
  Evidence and boundaries for exactly why this matters and what could go wrong if a less
  careful parser guessed instead.
- **planFamily normalizes what CAN be told apart, without inventing what can't.**
  `Hinge+ Subscription` collapses to `planFamily: "HingePlus"`. `Membership` stays its own
  family. `Boost` and `1 Boost` both collapse to `Boost`. `Bundle of three Roses`, `Bundle of
  twelve Roses` and the shorter `3 Roses` all collapse to `Roses`, with the numeral inside
  every one of those names correctly treated as a QUANTITY (three roses, twelve roses), never
  parsed as a duration — `durationDays` stays `null` for every Roses entry, because none of
  them states a day/week/month/year unit word, and this Actor's duration parser never
  guesses one from a bare number.
- **A dictionary ready for HingeX the day it actually appears live.** `HingeX` is a real,
  named Hinge tier that has not appeared in either the US or UK App Store feed this Actor has
  captured so far — it is already in the plan dictionary (verified by this line's own
  synthetic tests, not a live capture) specifically so it classifies correctly from day one
  if and when it does show up, rather than the dictionary being extended reactively after
  someone notices a `other` row that should have had a name.
- **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
  ECB daily reference rate — GBP has a real rate, so every UK example in this README carries
  a real `usd` figure; a currency the ECB doesn't carry gets `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 — this
  is specifically how you'd catch the day `HingeX` or a real tier name for "Hinge
  Subscription" first appears, without watching the raw feed by hand.
- **Google Play's own real shape, not a forced match to the App Store's.** One aggregate
  `iapPriceRange` (`$0.49–$284.99` at capture time), no named plans — Google Play's own page
  simply doesn't disclose per-plan pricing the way the App Store's does.
- **Independent storefront reads.** Every `country × platform` pair is fetched on its own —
  a rate limit in one country never touches another country's row in the same run.
- **Canonical-id validation before anything is trusted.** Every App Store page this Actor
  reads is checked against the numeric id it was asked for (`595287172`) before any field is
  parsed out of it — a mislabeled or redirected page fails the row closed as `source_error`
  rather than silently attaching Hinge's name to someone else's price list.
- **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 Match Group (NASDAQ: MTCH).** Hinge is one of Match
  Group's fastest-growing disclosed brands by the company's own investor commentary. A
  pricing read across storefronts, including a documented account of exactly which plan
  names Apple's own feed actually shows (not which names Hinge's marketing describes), is a
  primary-source input a research note can cite directly.
- **Competitor product and pricing teams** at Tinder, Bumble, Badoo, Twinby and any other
  dating app pricing against Hinge's own Hinge+/Membership/Boost/Roses ladder before setting
  their own numbers in a given market.
- **ASO and mobile-growth agencies** tracking rating trend and review velocity for a
  portfolio that includes Hinge, adding subscription pricing to the same report.
- **Anyone specifically investigating Apple App Store IAP feed labeling quirks** — the plain,
  tier-less `"Hinge Subscription"` entry documented on this page is a real, reproducible
  example of a named plan whose own text genuinely does not disclose which tier it is, useful
  as a concrete case study for anyone building their OWN parser against App Store data and
  wondering how to handle an ambiguous name honestly rather than guessing.
- **Currency and purchasing-power researchers** using Hinge+'s real per-country price ladder
  as one comparable input alongside this line's other four brands.
- **Anyone tracking their own Hinge+ or Membership cost** across a relocation via this
  Actor's own dataset history.
- **Investor-relations and comms teams at Match Group itself**, who may want an
  externally-sourced, independent read of what Hinge's own storefronts actually show in a
  given market — useful for confirming a pricing rollout reached every intended storefront,
  or for anticipating how an outside analyst might describe the same public data.
- **Data-engineering teams building their own App Store or Google Play ingestion pipeline**,
  who want a concrete, real, already-solved example of handling an ambiguous source field
  (the `"Hinge Subscription"` case) honestly, as a design reference for their own parser's
  handling of similarly ambiguous fields elsewhere.

This Actor does **not** serve anyone looking for Hinge user profiles, photos, messages, match
data, or any other personal information about Hinge'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 the exact two storefronts where this README's own `"Hinge Subscription"` example
   was observed.
3. Pick your **Platforms** — `appstore`, `googleplay`, or both (the default). There is no
   `rustore` option — Hinge is not sold through RuStore.
4. Leave **Convert prices to USD** on unless you want each storefront's own local currency
   only — Hinge's own two most-checked storefronts (US and UK) both convert cleanly via the
   ECB's own daily rate, unlike Twinby's RUB-priced storefront elsewhere in this line.
5. Press **Start**. The default input finishes in well under a minute; a longer country list
   against Apple, at this Actor's own 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 retries (`rate_limited`), a real parsing/source error
(`source_error`), or an invalid country code (`invalid_country`). A row classified
`planFamily: "other"` — including every `"Hinge Subscription"` entry — is still a real,
successfully-parsed price and is billed exactly the same as any other successful row; this
Actor never withholds a charge just because a plan's own name was ambiguous, and never
inflates a charge for reporting `other` honestly instead of a guessed tier name.

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**; Hinge has resolved successfully on every one of
those four combinations in this line's own testing. Ten resolved storefronts comes to
**$0.055**. `maxStorefronts` (default 10, maximum 120) caps how many combinations one run
will ever attempt, enforced before any request is made, with a free `status: "truncated"`
row naming exactly what was skipped.

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. The margin covers the
occasional free run (a rate-limited attempt, a batch of `invalid_country` codes) without the
paying storefronts needing to individually subsidize every possible failure mode.

### 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 isn't sold in returns a free `not_available_in_country` row — neither has appeared in this line's own live testing of Hinge across the countries checked so far. 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 — including the `"Hinge Subscription"` entries,
shown exactly as this Actor's own parser classified them: `other`, not a guess.

#### 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,
including three separate `"Hinge Subscription"` entries, each landing on `planFamily:
"other"`, alongside `Hinge+ Subscription` correctly landing on `HingePlus`, `Membership`
staying its own family, and both Roses bundle sizes correctly showing `durationDays: null`:**

```json
{
    "recordType": "storefront_snapshot",
    "brand": "hinge",
    "platform": "appstore",
    "country": "us",
    "appId": "595287172",
    "observedAt": "2026-09-26T15:31:46.298Z",
    "status": "ok",
    "found": true,
    "appTitle": "Hinge Dating App: Match & Date App",
    "rating": 4.4,
    "ratingCount": 1122804,
    "ratingHistogram": {
        "1": 75859,
        "2": 26292,
        "3": 72720,
        "4": 138752,
        "5": 809181
    },
    "version": "10.5.0",
    "lastUpdated": "2026-09-21T14:25:13.000Z",
    "inAppPurchases": [
        {
            "name": "Bundle of three Roses",
            "priceText": "$9.99",
            "amount": 9.99,
            "currency": "USD",
            "planFamily": "Roses",
            "durationDays": null,
            "usd": 9.99
        },
        {
            "name": "Boost",
            "priceText": "$9.99",
            "amount": 9.99,
            "currency": "USD",
            "planFamily": "Boost",
            "durationDays": null,
            "usd": 9.99
        },
        {
            "name": "Hinge Subscription",
            "priceText": "$29.99",
            "amount": 29.99,
            "currency": "USD",
            "planFamily": "other",
            "durationDays": null,
            "usd": 29.99
        },
        {
            "name": "Hinge+ Subscription",
            "priceText": "$16.99",
            "amount": 16.99,
            "currency": "USD",
            "planFamily": "HingePlus",
            "durationDays": null,
            "usd": 16.99
        },
        {
            "name": "Membership",
            "priceText": "$19.99",
            "amount": 19.99,
            "currency": "USD",
            "planFamily": "Membership",
            "durationDays": null,
            "usd": 19.99
        },
        {
            "name": "Hinge+ Subscription",
            "priceText": "$19.99",
            "amount": 19.99,
            "currency": "USD",
            "planFamily": "HingePlus",
            "durationDays": null,
            "usd": 19.99
        },
        {
            "name": "Boost",
            "priceText": "$19.99",
            "amount": 19.99,
            "currency": "USD",
            "planFamily": "Boost",
            "durationDays": null,
            "usd": 19.99
        },
        {
            "name": "Hinge Subscription",
            "priceText": "$14.99",
            "amount": 14.99,
            "currency": "USD",
            "planFamily": "other",
            "durationDays": null,
            "usd": 14.99
        },
        {
            "name": "Bundle of twelve Roses",
            "priceText": "$29.99",
            "amount": 29.99,
            "currency": "USD",
            "planFamily": "Roses",
            "durationDays": null,
            "usd": 29.99
        },
        {
            "name": "Hinge Subscription",
            "priceText": "$34.99",
            "amount": 34.99,
            "currency": "USD",
            "planFamily": "other",
            "durationDays": null,
            "usd": 34.99
        }
    ],
    "planSummary": {
        "Roses": {
            "minAmount": 9.99,
            "maxAmount": 29.99,
            "currency": "USD",
            "minUsd": 9.99,
            "maxUsd": 29.99
        },
        "Boost": {
            "minAmount": 9.99,
            "maxAmount": 19.99,
            "currency": "USD",
            "minUsd": 9.99,
            "maxUsd": 19.99
        },
        "other": {
            "minAmount": 14.99,
            "maxAmount": 34.99,
            "currency": "USD",
            "minUsd": 14.99,
            "maxUsd": 34.99
        },
        "HingePlus": {
            "minAmount": 16.99,
            "maxAmount": 19.99,
            "currency": "USD",
            "minUsd": 16.99,
            "maxUsd": 19.99
        },
        "Membership": {
            "minAmount": 19.99,
            "maxAmount": 19.99,
            "currency": "USD",
            "minUsd": 19.99,
            "maxUsd": 19.99
        }
    },
    "fx": {
        "source": "ECB",
        "date": "2026-09-25",
        "rate": 1
    },
    "error": "",
    "sourceUrl": "https://apps.apple.com/us/app/id595287172",
    "changes": []
}
```

**App Store, United Kingdom — a second currency, and the SAME `"Hinge Subscription"`
ambiguity reproduced on a second storefront, not a one-off:**

```json
{
    "recordType": "storefront_snapshot",
    "brand": "hinge",
    "platform": "appstore",
    "country": "gb",
    "appId": "595287172",
    "observedAt": "2026-09-26T15:31:47.423Z",
    "status": "ok",
    "found": true,
    "appTitle": "Hinge Dating App: Match & Date App",
    "rating": 4.3,
    "ratingCount": 195334,
    "ratingHistogram": {
        "1": 12952,
        "2": 4833,
        "3": 15952,
        "4": 30322,
        "5": 131275
    },
    "version": "10.5.0",
    "lastUpdated": "2026-09-21T14:25:13.000Z",
    "inAppPurchases": [
        {
            "name": "Hinge+ Subscription",
            "priceText": "£14.99",
            "amount": 14.99,
            "currency": "GBP",
            "planFamily": "HingePlus",
            "durationDays": null,
            "usd": 19.865299
        },
        {
            "name": "Bundle of three Roses",
            "priceText": "£9.99",
            "amount": 9.99,
            "currency": "GBP",
            "planFamily": "Roses",
            "durationDays": null,
            "usd": 13.239116
        },
        {
            "name": "Boost",
            "priceText": "£9.99",
            "amount": 9.99,
            "currency": "GBP",
            "planFamily": "Boost",
            "durationDays": null,
            "usd": 13.239116
        },
        {
            "name": "Hinge+ Subscription",
            "priceText": "£14.99",
            "amount": 14.99,
            "currency": "GBP",
            "planFamily": "HingePlus",
            "durationDays": null,
            "usd": 19.865299
        },
        {
            "name": "Hinge+ Subscription",
            "priceText": "£14.99",
            "amount": 14.99,
            "currency": "GBP",
            "planFamily": "HingePlus",
            "durationDays": null,
            "usd": 19.865299
        },
        {
            "name": "1 Boost",
            "priceText": "£9.99",
            "amount": 9.99,
            "currency": "GBP",
            "planFamily": "Boost",
            "durationDays": null,
            "usd": 13.239116
        },
        {
            "name": "Hinge Subscription",
            "priceText": "£14.99",
            "amount": 14.99,
            "currency": "GBP",
            "planFamily": "other",
            "durationDays": null,
            "usd": 19.865299
        },
        {
            "name": "Hinge Subscription",
            "priceText": "£29.49",
            "amount": 29.49,
            "currency": "GBP",
            "planFamily": "other",
            "durationDays": null,
            "usd": 39.081233
        },
        {
            "name": "Hinge+ Subscription",
            "priceText": "£12.99",
            "amount": 12.99,
            "currency": "GBP",
            "planFamily": "HingePlus",
            "durationDays": null,
            "usd": 17.214826
        },
        {
            "name": "3 Roses",
            "priceText": "£11.99",
            "amount": 11.99,
            "currency": "GBP",
            "planFamily": "Roses",
            "durationDays": null,
            "usd": 15.889589
        }
    ],
    "planSummary": {
        "HingePlus": {
            "minAmount": 12.99,
            "maxAmount": 14.99,
            "currency": "GBP",
            "minUsd": 17.214826,
            "maxUsd": 19.865299
        },
        "Roses": {
            "minAmount": 9.99,
            "maxAmount": 11.99,
            "currency": "GBP",
            "minUsd": 13.239116,
            "maxUsd": 15.889589
        },
        "Boost": {
            "minAmount": 9.99,
            "maxAmount": 9.99,
            "currency": "GBP",
            "minUsd": 13.239116,
            "maxUsd": 13.239116
        },
        "other": {
            "minAmount": 14.99,
            "maxAmount": 29.49,
            "currency": "GBP",
            "minUsd": 19.865299,
            "maxUsd": 39.081233
        }
    },
    "fx": {
        "source": "ECB",
        "date": "2026-09-25",
        "rate": 1.32523679
    },
    "error": "",
    "sourceUrl": "https://apps.apple.com/gb/app/id595287172",
    "changes": []
}
```

**Google Play, United States — the same app, this store's genuinely different shape:**

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

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

```json
{
    "recordType": "storefront_snapshot",
    "brand": "hinge",
    "platform": "googleplay",
    "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 (technique 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": "hinge",
    "platform": "appstore",
    "country": "us",
    "appId": "595287172",
    "observedAt": "2026-09-26T16:06:48.380Z",
    "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/id595287172",
    "status": "rate_limited",
    "found": false,
    "error": "http 429 after retries"
}
```

### Field dictionary

| Field | Type | Meaning |
|---|---|---|
| `recordType` | string | Always `"storefront_snapshot"`. |
| `brand` | string | Always `"hinge"` here. |
| `platform` | string | `"appstore"` or `"googleplay"`. |
| `country` | string | The ISO-3166 code requested. |
| `appId` | string | `595287172` (App Store numeric id) or `co.hinge.app` (Google Play package name). |
| `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 — `"Hinge Dating App: Match & Date"` on both storefronts observed. |
| `rating` | number | null | The store's own current average rating — over 480,000 ratings on the US Google Play listing at capture time. |
| `ratingCount` | number | null | Total ratings behind `rating` — over 480,000 on the US Google Play listing, over 1.1 million on the US App Store listing at capture time. |
| `ratingHistogram` | object | null | App Store only — `{"1":n, ..., "5":n}`. Always `null` on `googleplay`. |
| `installs` | string | null | Google Play only — `"10M+"` at capture time. Always `null` on `appstore`, since Apple does not publish an install count on this page. |
| `version` | string | null | The currently listed app version string. |
| `lastUpdated` | string (ISO 8601) | null | The store's own last-update date. |
| `releaseNotes` | string | null | The "What's new" text, in the storefront's own language, taken as-is. |
| `containsAds` | boolean | null | Google Play only. |
| `iapPriceRange` | object | null | Google Play only — `{min, max, currency}` aggregate across every in-app item; always `null` on `appstore`, where the full named list lives in `inAppPurchases[]` instead. |
| `inAppPurchases` | array | App Store only. |
| `inAppPurchases[].name` | string | The plan's own raw name, exactly as printed — including the plain `"Hinge Subscription"`, never rewritten or disambiguated by this Actor. |
| `inAppPurchases[].planFamily` | string | See the plan dictionary table below. **`"other"` for every `"Hinge Subscription"` entry, always, by design.** |
| `inAppPurchases[].durationDays` | number | null | `null` for every Boost/Roses entry, since none states a day/week/month/year unit word. The numeral in `"Bundle of three Roses"` or `"3 Roses"` is a quantity of roses, not a length of time, and this Actor's duration parser only ever fires on an explicit unit word — never on a bare number. |
| `inAppPurchases[].usd` | number | null | Converted via ECB rate when possible; `null` with `fxNote` otherwise. |
| `planSummary` | object | Per-`planFamily` min/max within this row — note that `other` rows (the `"Hinge Subscription"` entries) DO still contribute to an `"other"` entry in `planSummary`, so their price range is not lost, just not attributed to a specific tier name the source itself didn't provide. |
| `fx` | object | null | `{source: "ECB", date, rate}` — the exact EUR-denominated rate used for this row's currency, or `null` when `convertToUsd` is off or nothing needed converting. |
| `fxNote` | string | null | Set only when `convertToUsd` was requested but the ECB has no rate for this row's currency. |
| `error` | string | Empty for a clean "no" (`not_available_in_country`/`invalid_country`); a specific message for `rate_limited`/`source_error`. |
| `sourceUrl` | string | null | The exact URL this row's data came from; `null` only for `invalid_country`, where no request was ever sent. |
| `changes[]` | array | Populated only with `compareWithPreviousRun: true` — empty on a fresh `watchId`'s first run, one entry per detected change afterward. |

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

| planFamily | Matched from (case-insensitive substring, longest match wins) | Observed live? |
|---|---|---|
| `HingeX` | `"hingex"` | Not yet — kept in the dictionary per SPEC.md §4 so it classifies correctly the day it appears |
| `HingePlus` | `"hinge+"` | Yes, both US and GB, at capture time |
| `Membership` | `"membership"` | Yes, US |
| `Roses` | `"rose"` (matches `"Rose"`/`"Roses"` regardless of the bundle-size prefix) | Yes, both storefronts, at least two bundle sizes each |
| `Boost` | `"boost"` | Yes, both storefronts |
| **(deliberately absent)** | **`"hinge subscription"` is NOT a dictionary key** — a plain, tier-less name this Actor refuses to guess a tier for | Falls to `other`, always |
| `other` | anything not matching the above, including every real `"Hinge Subscription"` entry | Yes — this is the MOST COMMON non-family classification this brand's own live feed produces |

### Evidence and boundaries

**What this Actor reads, exactly.** Two public product pages:
`https://apps.apple.com/{cc}/app/id595287172` and
`https://play.google.com/store/apps/details?id=co.hinge.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.

**The `"Hinge Subscription"` ambiguity, explained in full, because it is this brand's single
most important honesty story.** Apple's own IAP feed for Hinge, on BOTH the US and UK
storefronts captured for this README, lists a plain `"Hinge Subscription"` entry at three
distinct price points on each storefront ($14.99/$29.99/$34.99 in the US;
£14.99/£29.49-range in the UK). Nothing in the name itself says whether this is the base
tier, a renewal-price listing for an existing subscriber, or something else Apple's own feed
doesn't further describe. A less careful parser might assume it's the same thing as
`"HingeX"` (a real, named tier this brand does use elsewhere) simply because both contain the
word "Hinge" and both are subscriptions — that assumption would be a genuine, silent
misclassification with no way for a reader of the resulting data to know it happened. This
Actor's plan dictionary was built, deliberately, WITHOUT a `"hinge subscription"` key, so
this exact case falls through to `planFamily: "other"` instead — a visible, honest signal
that this Actor could not confidently name the tier, rather than an invisible wrong guess.
If Apple's own feed ever adds a tier word to this name, or if this Actor's own operator finds
a reliable, non-guessing way to disambiguate it (e.g. cross-referencing price against a known
tier's price in the SAME country), this dictionary will be updated — but never by assuming.

**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 with a 45-second retry
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 (that's
Google Play's own disclosure), and no duration for any plan whose own name doesn't state one
— every Roses and Boost entry has `durationDays: null` for exactly this reason.

**What this Actor does NOT do, and will not be extended to do.** No account, no login, no
profile/photo/message/match data of any real Hinge 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 Match Group's, Apple's or Google's.
Permanent design boundaries, not missing features.

**Why "designed to be deleted" doesn't change anything about how this Actor works.** Hinge's
own brand positioning emphasizes helping users find a relationship and stop using the app —
a genuinely different marketing angle from most of this line's other brands. This Actor
reads the same kind of public pricing and rating data regardless of a brand's own marketing
narrative; the positioning is mentioned here only because it's Hinge's own well-known public
framing, not because it changes anything about what fields this Actor reads or reports.

### Decision routing

**Use this Actor when** you specifically want Hinge's own subscription prices, normalized
into a stable `planFamily` WHERE the source itself supports a confident classification, with
an honest `other` fallback where it genuinely doesn't — most importantly for every
`"Hinge Subscription"` entry, which this Actor will never silently force into `HingeX` or any
other named tier.

**A generic App Store/Google Play field-extraction Actor won't tell you which entries are
genuinely ambiguous** — it will hand you the raw `"Hinge Subscription"` string with no
`planFamily` opinion at all, leaving the disambiguation problem entirely to you. This Actor
costs more per storefront specifically because it already did that work, including the
honest decision to classify the ambiguous case as `other` rather than guess.

**Use one of this Actor's own siblings instead of Hinge** when the brand you care about is
different: the same engine also ships as separate Actors for Tinder (also Match Group),
Twinby (adds RuStore), Bumble and Badoo. Tinder and Hinge share a parent company but have
entirely separate, brand-specific plan dictionaries — Tinder's own feed has never shown the
same tier-less-subscription ambiguity Hinge's does.

**If your own downstream logic assumes every `"other"` row is unimportant, reconsider that
assumption specifically for this brand** — a meaningful share of Hinge's own real, priced
plan list classifies as `other` precisely because of the `"Hinge Subscription"` naming
quirk, not because the plan is rare or unimportant. Filtering out `other` rows for this brand
would silently drop real price data that a less honest parser would have mislabeled instead
of dropped — dropping is visible and auditable; mislabeling is not.

**Prefer this Actor's own `compareWithPreviousRun` diff over comparing two exports by hand**
— besides the general risk of comparing exports taken under different `convertToUsd`
settings, for Hinge specifically it also means the saved snapshot correctly compares an
`other`-classified `"Hinge Subscription"` price against its own prior value, rather than a
manual comparison accidentally lumping it in with a different plan because both got read as
generic "subscription" line items by whoever did the comparing.

**Do not use this Actor** for anything about real Hinge users — profiles, matches, messages.
No configuration will ever do that.

### Commercial playbooks

**Quarterly pricing-power tracking ahead of a Match Group earnings call.** An equity research
desk schedules this Actor weekly against Match Group's disclosed Hinge-relevant markets, with
`compareWithPreviousRun` on, building a real time series that would also be the first place
to notice `HingeX` or a disambiguated subscription name appearing live.

**Competitive benchmarking before a competing app's own subscription-naming decision.** A
product team elsewhere in the industry, deciding how to name and price their OWN
subscription tiers, uses this Actor's documented `"Hinge Subscription"` case as a real
cautionary example of what happens when a plan name doesn't disclose its own tier — a
concrete argument for naming their own plans more explicitly than Hinge's own feed currently
does.

**ASO agency monthly client report.** Hinge's App Store and Google Play numbers slot into
the same monthly competitive-pricing report structure this line's other Actors describe.

**Data-quality case study for anyone building their own App Store parser.** The
`"Hinge Subscription"` example, cited with its real observed price points and its
deliberately-`other` classification, is a usable, real-world teaching case for "how to handle
an ambiguous source field honestly" — reference it directly rather than needing to find or
fabricate your own example of the same problem.

**Pre-earnings sanity check for a Match Group IR or comms team.** Before a filing or an
analyst call references Hinge's own subscription pricing or growth, an IR or comms team runs
this Actor across the brand's key disclosed markets to confirm what is actually live on each
storefront, independent of whatever the internal pricing team's own change log says should
have shipped.

**Tracking the first live appearance of HingeX or a disambiguated "Hinge Subscription"
name.** A team specifically watching for Match Group to resolve the naming ambiguity
documented on this page schedules this Actor with `compareWithPreviousRun` on and reads the
first `changes[]` entry where a plan's family changes from `other` to something named — the
earliest automated signal available for that specific product change.

### Integration recipes

**Schedule it (Apify Console).** Open the Schedules tab, create a cron expression, attach the
input you want repeated, and every scheduled run lands in this Actor's own dataset history.

**Call it from the REST API.**

```bash
curl -X POST "https://api.apify.com/v2/acts/zinin~hinge-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/hinge-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/hinge-app-intel').call(run_input={...})`.

**Wire it to a webhook.** Attach a webhook fired on `ACTOR.RUN.SUCCEEDED` 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 integration path
here. This is a genuinely useful path specifically for this Actor: an agent asked "does
Hinge have a plan called HingeX right now" can call this tool and read the real `other`
classification for `"Hinge Subscription"` rather than confidently hallucinating an answer
about a tier name that isn't actually disclosed anywhere in the source.

**Export straight to Google Sheets** or pull a plain **CSV/Excel** file from the Dataset
tab — both work exactly as for every other Actor in this line.

### Operating guide

**Reading a partial run.** Check `OUTPUT.partial` first, not the run's own top-level status.

**When to retry a rate-limited storefront.** A single `rate_limited` row is usually
transient at this Actor's own pacing; a storefront that stays `rate_limited` across several
attempts over an hour or more is worth treating as a real, ongoing block.

**Treating `other` rows as real data, not noise.** For this brand specifically, do not
filter out `planFamily: "other"` rows before analysis unless you have independently
confirmed what they represent — for Hinge, a large share of `other` rows are real,
successfully-parsed `"Hinge Subscription"` prices, not junk or parsing failures. Read the
raw `name` field on any `other` row before deciding to discard it.

**Choosing a `watchId` strategy** and **maxStorefronts on a large list** — the same mechanics
as every Actor in this line: one `watchId` per logical tracking list, and a `truncated` row
whenever `countries × platforms` exceeds the cap, with `maxStorefronts` adjustable up to 120.

**If you specifically want to detect the day `"Hinge Subscription"` gets a real tier name (or
`HingeX` appears),** run this Actor with `compareWithPreviousRun: true` on a fixed `watchId`
and watch for a `changes[]` entry where a plan's `planFamily` changes from `other` to a named
family, or a new named family appears for the first time — that's the earliest, most direct
signal this Actor's own data can give you for that specific event.

**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. This is specifically NOT true for every brand in this line — Twinby's RUB rows never
get a `usd` figure — so confirm which currencies are actually involved before assuming a
cross-country USD comparison is valid for a brand you haven't checked yet.

**Building a downstream report that groups by `planFamily`.** Remember that `other` is not a
junk bucket for this brand — group it separately from a genuine parsing gap, and read the raw
`name` field before excluding an `other` row from a pricing summary, since a meaningful share
of Hinge's real revenue-relevant pricing currently lives under that label.

### FAQ

**Why does "Hinge Subscription" classify as `other` instead of `HingeX`?** Because the name
alone doesn't say which tier it is, and this Actor refuses to guess. See Evidence and
boundaries for the full explanation, including the real price points observed and exactly why
guessing would be a worse outcome than an honest `other`.

**Has `HingeX` ever actually appeared in a live Hinge App Store feed this Actor has read?**
Not yet, in this line's own testing as of this README's writing. It's in the plan dictionary
so it will classify correctly the moment it does.

**Why does `durationDays` come back `null` for a "Bundle of three Roses"?** Because the
number 3 there is a quantity of roses, not a length of time — the plan's own name states no
day/week/month/year unit, and this Actor's duration parser never infers one from a bare
number regardless of what it's counting.

**Does this Actor read anything about real Hinge users?** No, and it never will — see
Evidence and boundaries for the permanent, complete list of what it does not do.

**If the US App Store storefront gets rate-limited, do I still get the UK row?** Yes — each
`country × platform` pair is fetched independently, so a `rate_limited` result on one
storefront never affects another storefront's row in the same run. You'd see the free,
`rate_limited` US row alongside a normal, billed UK row in the same dataset.

**Found a wrong result, or a plan-name edge case this Actor missed?** Open an issue on this
Actor's own page. The `"Hinge Subscription"` handling described on this page was itself
found and deliberately resolved this way, against real captured pages, not assumed away.

**Could "Hinge Subscription" actually just BE HingeX under a different display name in some
markets?** Possibly — this Actor genuinely does not know, and says so rather than assuming
either way. Apple's own feed gives no field that resolves this, and cross-referencing price
against a specific tier's price in the same country would only be a probabilistic inference,
not a fact the source states — this Actor's whole design philosophy across all five brands
in this line is to report what the source actually says and mark the rest `other`, not to
publish a probabilistic guess as if it were a confirmed fact.

**Why do the US and UK both show the same "Hinge Subscription" ambiguity instead of just
one storefront having it?** Because this Actor checked both, independently, and both
genuinely show it — this is presented as reproduced evidence across two real storefronts,
not a single anomaly that might have been a one-off scrape error.

**Is the Google Play `iapPriceRange` affected by the same "Hinge Subscription" naming
ambiguity?** No — Google Play never names individual plans at all, on any brand in this
line, so there is no equivalent naming decision to make there. The ambiguity described on
this page is specific to the App Store's own itemized `inAppPurchases[]` list.

**Does a higher US ratingCount than UK ratingCount mean Hinge is more popular in the US?**
It's suggestive but not conclusive on its own — `ratingCount` measures how many people left a
rating on that specific storefront, which correlates with but isn't identical to total
install base or market share, and market size itself differs hugely between the two
countries. Treat it as one real, dated data point rather than a normalized popularity score.

**Can this Actor tell me whether Hinge raised or lowered a price in a specific country?**
Only across two of this Actor's own runs, via `compareWithPreviousRun` — it has no access to
Hinge's own historical pricing before the first time you ran it against that country. If you
need a price history predating your own first run, this Actor cannot reconstruct it; only
forward-looking tracking from whenever you start is possible.

### 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/id595287172`, and Google's own Google Play
product page at `play.google.com/store/apps/details?id=co.hinge.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 Match
Group.

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 Hinge user. "Hinge" and its plan names (Hinge+, Membership,
Boost, Roses) are Match Group'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 Match Group, Hinge,
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, no-guessing
boundary this page describes throughout — a standard every other Actor in this same line is
held to as well: 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.

***

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/hinge-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/hinge-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/hinge-app-intel --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,zinin/hinge-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/fbJegCkwCTt6Y2xok/builds/wTGb4A8MggVgzRtOm/openapi.json
