# Mobile App Paywall & In-App Pricing Extractor (`enigmalabs/mobile-paywall-extractor`) Actor

Launches real Android 13 mobile sessions across geo-targeted regions to capture onboarding flows, in-app subscription paywalls, trial terms, and XML UI pricing text.

- **URL**: https://apify.com/enigmalabs/mobile-paywall-extractor.md
- **Developed by:** [Enigma Labs](https://apify.com/enigmalabs) (community)
- **Categories:** AI, Marketing, E-commerce
- **Features:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $80.00 / 1,000 app paywall & pricing extractions

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

Capture **in-app subscription paywalls**, **free-trial terms**, **observed IAP prices**, and **step-by-step onboarding screenshots** from real **Android 13** sessions. Built by **Enigma Labs** for mobile growth teams, pricing analysts, competitive-intelligence researchers, and **AI Agents (`mcp.apify.com`)**.

### What does it do?

Many subscription apps only show their plan prices, weekly vs. annual discounts and trial lengths inside native onboarding screens, which store web pages do not expose. This Actor sends a Google Play `package_name` to a live Android 13 capture worker and returns:

| Capability | Details |
| :--- | :--- |
| **Native Android 13 session** | Fresh Android 13 session per app. `country` is used for reporting/timezone labelling; prices shown are those Google Play Billing renders for the session (see limitations). |
| **Onboarding navigation** | Walks welcome prompts (`Get Started`, `Continue`, `Next`, `Skip`) towards the paywall, without logging in. |
| **Price & trial detection** | Parses the Android accessibility tree for currency amounts, billing periods (`/week`, `/month`, `/year`) and trial terms. |
| **PNG screenshots** | Step-by-step screenshots (`screen_00.png`, ...) saved in the run's Key-Value Store, with the system status bar removed. |

#### Important limitations (read before buying)

- **Store country vs. input country**: prices rendered by Google Play Billing follow the store country of the device account. The output reports the prices actually observed; `country` controls locale/timezone.
- **Login walls**: apps that require sign-in before showing a paywall are reported as `paywall_detected: false`. The Actor never signs in, never starts trials and never purchases.
- **Live worker availability**: live capture runs on a dedicated worker with a curated set of installed apps. When the worker is unavailable, or the requested app is not on the capture device (`worker_status: APP_NOT_AVAILABLE_ON_LIVE_WORKER`), you get either a Google Play listing fallback or a status record (see `require_live`) and you are **not charged**.

### How much does it cost? (Pay-Per-Event)

| Event | Price | When it is charged |
| :--- | :--- | :--- |
| `app_session_start` | $0.01 | Once per run, only when the live Android worker actually handled the app |
| `onboarding_screen_captured` | $0.015 | Per screenshot actually stored |
| `paywall_pricing_extracted` | $0.08 | Once, only when a paywall was detected |

Example: 3 screens + paywall found = $0.01 + 3 × $0.015 + $0.08 = **$0.135**.

**$0 is charged** for invalid input, live-worker unavailability, and the Google Play listing fallback.

### Input

- **`package_name`** *(string, required)*: Google Play package ID (`?id=<package_name>` in the Play URL).
- **`country`** *(select, default `US`)*: locale/timezone of the device session.
- **`max_screens`** *(integer 1–12, default 4)*: onboarding screens to capture.
- **`require_live`** *(boolean, default false)*: if true, skip the Google Play listing fallback when the live worker is unavailable.

```json
{ "package_name": "com.duolingo", "country": "US", "max_screens": 5, "require_live": true }
```

### Output

```json
{
  "package_name": "com.duolingo",
  "country": "US",
  "device_timezone": "America/New_York",
  "capture_mode": "live_android_13",
  "worker_status": "LIVE_ANDROID_13_WORKER",
  "paywall_detected": true,
  "all_detected_prices": ["14-day free trial", "$12.99 / month", "$83.99 / year"],
  "price_granularity": "in_app_paywall_ui",
  "screens": [
    { "step": 0, "screenshot_kvs_key": "screen_00.png", "is_paywall": false, "detected_prices": [], "cta_buttons": ["GET STARTED"] },
    { "step": 1, "screenshot_kvs_key": "screen_01.png", "is_paywall": true, "detected_prices": ["$12.99 / month"], "cta_buttons": ["START FREE TRIAL"] }
  ],
  "billing": { "requests": 1, "charged_events": 3, "charge_failures": 0, "stopped_early": false }
}
```

`capture_mode` values:

- `live_android_13`: real in-app capture (charged as above).
- `google_play_listing_fallback`: Play store-listing **price range per item** only (`price_granularity: store_listing_range_per_item`), not per-plan prices. $0.
- `live_required_unavailable`: `require_live=true` and live worker unavailable. $0.
- `invalid_input`: malformed package name. $0.

*(Example values above are illustrative.)*

### Python / MCP

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_APIFY_API_TOKEN")
run = client.actor("enigmalabs/mobile-paywall-extractor").call(
    run_input={"package_name": "com.duolingo", "country": "US", "max_screens": 5, "require_live": True}
)
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["capture_mode"], item["paywall_detected"], item["all_detected_prices"])
```

Screenshots: `client.key_value_store(run["defaultKeyValueStoreId"]).get_record("screen_00.png")`.

### FAQ

- **Does it bypass logins, CAPTCHAs or paid gates?** No. It only observes screens shown to a new, signed-out user.
- **Why did I get no prices?** The app may require login before its paywall, use a web checkout, or show no paywall during onboarding.

*Independent UI/UX and pricing research tool by Enigma Labs. App names and package identifiers belong to their respective owners.*

# Actor input Schema

## `package_name` (type: `string`):

Google Play package ID (e.g. com.duolingo, com.calm.android).

## `country` (type: `string`):

ISO-2 country code used for device locale/timezone. Note: in-app subscription prices shown by Google Play Billing follow the store country of the device account, which may differ from this value; the output reports what was actually observed.

## `max_screens` (type: `integer`):

Maximum onboarding screens to capture while looking for the subscription paywall.

## `require_live` (type: `boolean`):

If true and the live Android capture worker is unavailable, the run returns a status record only (no Google Play listing fallback). Nothing is charged in either case when live capture does not happen.

## Actor input object example

```json
{
  "package_name": "com.duolingo",
  "country": "US",
  "max_screens": 2,
  "require_live": false
}
```

# Actor output Schema

## `dataset` (type: `string`):

Geo-targeted Android 13 paywall detection, detected subscription pricing tiers, and UI screenshot metadata.

# 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 = {
    "package_name": "com.android.settings",
    "country": "US",
    "max_screens": 2
};

// Run the Actor and wait for it to finish
const run = await client.actor("enigmalabs/mobile-paywall-extractor").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 = {
    "package_name": "com.android.settings",
    "country": "US",
    "max_screens": 2,
}

# Run the Actor and wait for it to finish
run = client.actor("enigmalabs/mobile-paywall-extractor").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 '{
  "package_name": "com.android.settings",
  "country": "US",
  "max_screens": 2
}' |
apify call enigmalabs/mobile-paywall-extractor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,enigmalabs/mobile-paywall-extractor"
        }
    }
}
```

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/CB57ZozCW8XqRWf6I/builds/KTKp8dK7f8TjcyEdB/openapi.json
