# SaaS Pricing Page → Normalized Plans JSON (PPE) (`kyawkyaw/saas-pricing-normalizer`) Actor

Extract public SaaS pricing pages into a stable plan schema: name, price, period, currency, limits. One URL in, structured JSON out. Pay-per-event on successful parse.

- **URL**: https://apify.com/kyawkyaw/saas-pricing-normalizer.md
- **Developed by:** [kyaw kyaw](https://apify.com/kyawkyaw) (community)
- **Stats:** 2 total users, 1 monthly users, 66.7% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $20.00 / 1,000 pricing page parseds

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

## SaaS Pricing Page → Normalized Plans JSON (PPE)

**Store:** [apify.com/kyawkyaw/saas-pricing-normalizer](https://apify.com/kyawkyaw/saas-pricing-normalizer)\
**Author profile:** [apify.com/kyawkyaw](https://apify.com/kyawkyaw)\
**Status:** Published on Apify Store · PPE active\
**Charge:** `Actor.charge` for `pricing-page-parsed` since build `0.2.3`\
**Spec:** `../../specs/SPEC_01_saas_pricing_diff.md`\
**As-of:** 19 Oct 2026 (Asia/Rangoon)

***

### Short description

Extract **public** SaaS pricing pages into a stable plan schema agents can trust: plan `name`, `price`, `period`, `currency`, and `limits[]` — one URL in, structured JSON out. Pay-per-event on each successful `pricing-page-parsed`.

***

### Why this Actor

Builders and AI agents need “what does vendor X charge **now**?” without maintaining scrapers. Generic page scrapers dump HTML; this Actor **normalizes plan tiers** into a predictable schema for MCP / pipelines / competitive intel.

- Tight input (`startUrl`)
- Stable output (`plans[]` + `rawConfidence`)
- Fixture-tested parsers aimed at marketing-page churn
- Public pages only — no logins, no bypasses

***

### Input examples

#### 1) Live public URL (Store / production path)

```json
{
  "startUrl": "https://www.notion.so/pricing",
  "vendorName": "Notion",
  "currencyPreference": "USD"
}
```

#### 2) Another vendor

```json
{
  "startUrl": "https://vercel.com/pricing",
  "vendorName": "Vercel",
  "currencyPreference": "USD"
}
```

#### 3) Local fixture (dev only — do not use on Store)

```json
{
  "useFixture": true,
  "fixturePath": "fixtures/sample-pricing.html",
  "vendorName": "ExampleCo"
}
```

CLI equivalents (box / Mac, no token):

```bash
cd scaffold/SPEC_01
node src/main.js                                          # default fixture
node src/main.js fixtures/sample-pricing.html
node src/main.js --url https://example.com/pricing        # live public fetch
SPEC01_PRINT_PLANS=1 node src/main.js                     # also dump full JSON
```

***

### Output schema

```json
{
  "vendor": "ExampleCo",
  "url": "https://example.com/pricing",
  "fetchedAt": "2026-10-18T06:34:37.252Z",
  "plans": [
    {
      "name": "Starter",
      "price": 12,
      "period": "month",
      "currency": "USD",
      "limits": ["3 seats", "10 GB storage"]
    }
  ],
  "rawConfidence": 0.9,
  "mode": "fixture",
  "parseStrategy": "data-attributes"
}
```

| Field | Meaning |
|-------|---------|
| `plans[].price` | Number, or `null` for “contact sales” / unknown |
| `plans[].period` | `month` | `year` | `custom` | `one_time` | … |
| `rawConfidence` | 0–1 evidence-based confidence (priced plans + name quality) |
| `parseStrategy` | `data-attributes` | `card-heuristic` | `json-ld` | `text-proximity` | `spa-shell` |
| `billable` | `true` only if ≥1 non-null price (Free=`0` counts) and conf ≥ 0.55 — drives PPE charge |

***

### Pricing (PPE — active on Store)

| Event | When | Price |
|-------|------|-------|
| `apify-actor-start` | Run starts | platform default (~$0.00005 / GB) |
| **`pricing-page-parsed`** | Successful normalized snapshot with ≥1 billable plan | **`$0.02` / event** (primary) |
| `apify-default-dataset-item` | Each dataset item | platform default (~$0.00001) |

**Notes:** `Actor.charge({ eventName: 'pricing-page-parsed' })` runs only on the Apify platform for billable parses (not local / fixture). Spec band was `$0.01–$0.05`; live primary event is **$0.02**.

***

### Run locally (no Apify token, no spend)

```bash
cd /workspace/global-online-revenue-oct2026/week1-apify/scaffold/SPEC_01
node src/main.js
npm test   # or: node test/verify-fixture.mjs
```

Writes `storage/datasets/default/000000001.json`. Zero `npm install` required for fixture + native-fetch live mode.

Live URL path uses Node built-in `fetch` only (no residential proxies, no Apify paid infra). Auth walls / private hosts are refused.

***

### Layout

```
SPEC_01/
├── README.md
├── package.json
├── Dockerfile
├── .actor/actor.json
├── .actor/input_schema.json
├── src/main.js          ← entry (fixture | live) + Actor.charge on platform
├── src/parse.js         ← HTML → plans[]
├── src/input.js         ← CLI / INPUT.json / env
├── fixtures/            ← regression HTML
├── test/verify-fixture.mjs
└── storage/             ← local dataset mirror
```

***

### Legal / support

- Public pricing pages only. Abort behind auth.
- No trademark-implying Actor name.
- Best-effort; Issues tab only; no custom SLAs.

# Actor input Schema

## `startUrl` (type: `string`):

Public SaaS pricing page URL (https). Required on Apify.

## `vendorName` (type: `string`):

Optional display name override. If empty, inferred from page title when possible.

## `currencyPreference` (type: `string`):

Preferred ISO currency when the page shows multiple or unmarked amounts.

## `useFixture` (type: `boolean`):

Local development only. Ignored/refused on Apify (runs on the platform require startUrl).

## `fixturePath` (type: `string`):

Path to HTML fixture relative to Actor root. Ignored on Store. Example: fixtures/sample-pricing.html

## Actor input object example

```json
{
  "startUrl": "https://www.notion.com/pricing",
  "currencyPreference": "USD",
  "useFixture": false
}
```

# Actor output Schema

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

No description

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

No description

# 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 = {
    "startUrl": "https://www.notion.com/pricing"
};

// Run the Actor and wait for it to finish
const run = await client.actor("kyawkyaw/saas-pricing-normalizer").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 = { "startUrl": "https://www.notion.com/pricing" }

# Run the Actor and wait for it to finish
run = client.actor("kyawkyaw/saas-pricing-normalizer").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 '{
  "startUrl": "https://www.notion.com/pricing"
}' |
apify call kyawkyaw/saas-pricing-normalizer --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,kyawkyaw/saas-pricing-normalizer"
        }
    }
}
```

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/oVcIo4iqwZ1zNPF5w/builds/GsWdvSzS6azB9hhUE/openapi.json
