# ClickBank Scraper | Offer Monitor, Gravity & EPC (`cauldo/clickbank-offer-monitor`) Actor

Monitor public ClickBank offers: current Gravity, earnings per conversion, true EPC and return-rate context. Bulk keywords/categories, new or changed offers, before/after values, CSV and category summaries. No login. $2 per 1,000 saved offers; no start fee.

- **URL**: https://apify.com/cauldo/clickbank-offer-monitor.md
- **Developed by:** [Cauldo](https://apify.com/cauldo) (community)
- **Categories:** Automation
- **Stats:** 1 total users, 0 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$2.00 / 1,000 offer saveds

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

## ClickBank Scraper & Offer Monitor

Track public ClickBank marketplace offers with **current Gravity, earnings per conversion, actual earnings per click, and clearly labeled return-rate data**. Search multiple keywords and categories, export clean CSV, and reuse a monitor to get only first-seen or changed offers on later runs.

**$2 per 1,000 saved offers. No start fee. No ClickBank login or API key.** One saved offer costs $0.002, including comparisons and exports. The first 10 offers cost $0.02; 100 cost $0.20. Platform usage is included in this Actor's event price. Apify's available account credits can be used to try it.

### Why use this Actor?

- **Metrics with the right meaning.** ClickBank's current interface uses different fields from its older data. This Actor distinguishes earnings per conversion from earnings per click, retains the original metrics, and converts unavailable negative values to `null`.
- **Offer changes, ready for automation.** Keep a named baseline and receive first-seen/changed offers, exact before-and-after values, Gravity and earnings deltas, and observation timestamps.
- **Return-rate context.** A category estimate is labeled `category_estimate`, never presented as an offer's own refund history. The optional return-rate filter requires historical offer data.
- **Bulk research in one run.** Combine keyword and category searches with a single result cap, deduplicate by vendor, and filter by Gravity, earnings, EPC, language or recurring billing.
- **Useful exports.** Offers and changes have dedicated dataset views. Download an Excel-friendly CSV and category summaries based on the returned sample.
- **Pay for delivered offers.** Failed searches, duplicates, filtered offers and unchanged offers suppressed by monitoring incur no result event. No special five-row restriction for free-plan users.

### Quick start

```json
{
  "keywords": ["guitar", "piano"],
  "maxResults": 50
}
```

Click **Start**, then open **Offers & current metrics**. Download **Offers CSV** for a flattened spreadsheet, or use the dataset API for complete JSON including changes and original source metrics.

To browse a category without keywords:

```json
{
  "keywords": [],
  "categories": ["Arts & Entertainment", "Education"],
  "maxResults": 100,
  "maxOffersPerSearch": 500
}
```

Category names must match ClickBank, including punctuation. Both arrays empty means the entire public marketplace. Keywords and categories form combinations, up to 50 searches per run. Results are processed in input order; a global cap can be consumed by earlier searches.

### Monitor new and changed offers

```json
{
  "keywords": ["guitar", "piano"],
  "mode": "changed",
  "monitorName": "music-offers",
  "maxResults": 100,
  "maxOffersPerSearch": 500
}
```

The first run saves a baseline and returns matching offers. Reuse **the same monitor name** on later runs:

| Mode | Saved results |
| --- | --- |
| `all` | All matching offers; comparison fields included when a monitor is supplied |
| `new` | Only vendors not previously observed in this monitor |
| `changed` | First-seen offers plus offers whose tracked fields changed |

Tracked fields include title, description, destination URL, category, current and legacy Gravity, rank, earnings, EPC, conversion/return metrics and their basis, languages, recurring/physical flags and affiliate-tools URL. Image changes alone do not produce a changed event.

Use Apify schedules and run-success webhooks for recurring workflows in n8n, Make, Zapier or your own application. A webhook is a run notification; inspect the dataset count to decide whether to notify your team. This Actor does not itself send messages.

History is stored in your account in `clickbank-monitor-<monitorName>`, retains up to 20,000 offers observed in the past 90 days, and compares matching offers across searches using that name. Use different names for independent baselines. Configure **non-overlapping runs** for a monitor; the overlap guard is best effort. First seen means first observed by this monitor, not newly launched. No disappearance or expiry is inferred from partial, filtered or capped searches.

### Input reference

| Field | Default | Purpose |
| --- | --- | --- |
| `keywords` | `["guitar"]` when no categories supplied | Up to 20 keywords; `[]` to browse without keywords |
| `categories` | `[]` | Up to 10 exact category names |
| `maxResults` | `100` | Global saved-row limit, 1–10,000 |
| `maxOffersPerSearch` | `500` | Offers inspected before filters, 1–5,000 per search |
| `sortBy` | `gravity` | `gravity`, `averageEarningsPerConversion`, `earningsPerClick`, `rank`, `relevance` |
| `sortDescending` | `true` | Highest numeric values first; set false for rank 1 first |
| `mode` | `all` | `all`, `new` or `changed` |
| `monitorName` | unset | Saved comparison history; required for `new`/`changed` |
| `minGravity` | unset | Minimum current Gravity |
| `minAverageEarningsUsd` | unset | Minimum average earnings per conversion |
| `minEpcUsd` | unset | Minimum actual earnings per click |
| `maxReturnRatePercent` | unset | Maximum historical offer return percentage; excludes category estimates and unknowns |
| `recurringOnly` | `false` | Require the marketplace's recurring flag |
| `languages` | `[]` | Match any of `en`, `de`, `es`, `fr`, `it`, `pt` |
| `proxyConfiguration` | direct connection | Optional Apify or custom proxy |

Filters run before billing. Missing values do not pass numeric filters. Raising an inspection cap can improve coverage when filters are restrictive. There is a 150-request safety limit per run, including retries. Large combinations may require multiple runs.

### Metrics and output

One row represents one vendor offer. It includes vendor, title, description, category/subcategory, public offer and tools URLs, language and product flags, activation date, observation time and search provenance. The first matching search supplies the provenance when searches overlap.

The following mappings were verified against ClickBank's public marketplace client. The similarly named source fields are easy to confuse:

| Output field | Public source field | Meaning |
| --- | --- | --- |
| `gravity` | `biGravity` | Current displayed Gravity |
| `legacyGravity` | `gravity` | Older source Gravity metric, retained separately |
| `averageEarningsPerConversionUsd` | `averageEPC` | Current average earnings per conversion, **not per click** |
| `initialEarningsPerConversionUsd` | `initialEPC` | Current initial earnings per conversion |
| `futureEarningsPerRebillUsd` | `futureEPC` | Current future earnings per rebill |
| `earningsPerClickUsd` | `netEPC` | Actual current earnings per click |
| `conversionRatePercent` | `conversionRate × 100` | Percentage; `2.5` means 2.5% |
| `expectedReturnRatePercent` | `expectedReturnRate × 100` | Return metric; always read its source and basis |

`returnRateSource` preserves the source's label. `returnRateBasis` is `offer_history`, `category_estimate`, `other` or `unknown`. A change in the source label makes the return-rate delta `null` so unlike estimates are not subtracted.

Negative unavailable sentinels become `null` in normalized metrics; actual zero remains zero. `missingMetrics` identifies these fields and `sourceMetrics` preserves the original object, including any negative sentinels, for auditing. USD figures are affiliate earnings metrics, not product retail prices or a promise of profit. See [ClickBank's metric definitions](https://support.clickbank.com/en/articles/10535274-how-do-i-use-the-clickbank-marketplace-stats).

Monitoring adds `observationType`, `firstSeenAt`, `previousObservedAt`, `changedFields`, `changes`, `gravityDelta`, `averageEarningsDeltaUsd`, `earningsPerClickDeltaUsd`, `returnRateDeltaPercentagePoints` and `comparisonHours`. First observations have no prior values, so deltas are `null`. Comparisons use the previous matching observation, including runs that suppressed unchanged rows.

### Coverage and reliability

The **Run report & search coverage** output contains each search's reported total, inspected/filtered/duplicate counts, pages, completion state, errors and stop reason. Empty searches are valid. All failed searches fail the run; mixed success produces an explicit `partial` report. Capped output is marked `limited`.

The source returns a live, changing catalog; pagination is not a transactionally frozen snapshot. The Actor deduplicates vendor IDs, detects repeated pages and reports incomplete coverage. Saved rows use a durable pending-batch journal and stable event keys to recover within the same run without repeating a charge. If a partial dataset write cannot be reconciled, it stops and preserves the pending batch for investigation. A new run has its own billing and is not a resume of an earlier run.

Category CSV/JSON summaries describe **only returned rows**. In changed mode they summarize changed/first-seen offers, not all marketplace offers. Median values ignore unavailable metrics.

### JavaScript API example

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

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('cauldo/clickbank-offer-monitor').call({
  keywords: ['guitar', 'piano'],
  mode: 'changed',
  monitorName: 'music-offers',
  maxResults: 100,
}, { maxTotalChargeUsd: 0.20 });
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

### FAQ

**Do I need a ClickBank account?** No. This Actor reads only marketplace data available through the public search endpoint. It does not log in, change listings, generate affiliate tracking links, or access private account reports.

**Why are there fewer offers than requested?** The source may have fewer matches, filters may exclude offers, monitoring may suppress unchanged records, or a result/inspection/budget cap may have been reached. Check the run report.

**Why is EPC zero or unavailable?** Zero is preserved when the source supplies zero. Negative unavailable sentinels are normalized to `null`. This Actor does not estimate missing performance numbers.

**Can an unchanged run cost zero in result events?** Yes. In `new`/`changed` mode no rows means no `offer-result` events. There is no Actor start fee.

**How do I reset history?** Use a new monitor name, or delete the named history store through your Apify account. Avoid changing history while a run is active.

**Is this official?** This is an independent tool, not affiliated with ClickBank. Public source access and schemas can change. For a problem, open the Actor's Issues tab and include the run ID and input without any credentials.

# Actor input Schema

## `keywords` (type: `array`):

Up to 20 keyword searches. Clear this list to browse whole categories or the entire marketplace. Keywords combine with each category.

## `categories` (type: `array`):

Exact ClickBank category names, e.g. Arts & Entertainment, Education, or Home & Garden. Leave empty for all categories. At most 50 keyword × category combinations.

## `maxResults` (type: `integer`):

Global cap after filtering and deduplication. One saved offer costs $0.002. Also respects your run charge limit.

## `maxOffersPerSearch` (type: `integer`):

Controls work before filters. Increase if filters or monitoring return too few rows. The report shows incomplete/capped searches.

## `sortBy` (type: `string`):

Sort each search before applying the result cap. For rank 1 first, turn Descending off.

## `sortDescending` (type: `boolean`):

Highest numeric values first. Turn off for rank 1 first. Relevance uses the source default.

## `mode` (type: `string`):

New/changed modes need a monitor name. The first run creates a baseline and returns matching offers. All mode also adds comparisons when a monitor is supplied.

## `monitorName` (type: `string`):

Optional saved history name, 1–50 lowercase letters/digits/hyphens, e.g. guitar-market. Reuse for future runs. History is shared across searches under this name. Use non-overlapping schedules.

## `minGravity` (type: `number`):

Current Gravity as displayed by ClickBank, not the legacy gravity field. Missing values fail this filter.

## `minAverageEarningsUsd` (type: `number`):

Filters the current earnings-per-conversion metric. This is not the retail product price.

## `minEpcUsd` (type: `number`):

Uses the current public net EPC metric. Missing values fail the filter; genuine zero remains zero.

## `maxReturnRatePercent` (type: `number`):

Example: 10 means 10%. Requires an offer-specific historical source label. Category estimates and unavailable values are excluded.

## `recurringOnly` (type: `boolean`):

Keep offers marked as recurring by the marketplace. This does not guarantee future rebill earnings.

## `languages` (type: `array`):

Optional language codes: en, de, es, fr, it, pt. An offer must match at least one.

## `proxyConfiguration` (type: `object`):

Direct HTTP is the default and was tested on Apify. Optional Apify/custom proxy if needed. No ClickBank credentials are requested.

## Actor input object example

```json
{
  "keywords": [
    "guitar"
  ],
  "categories": [],
  "maxResults": 100,
  "maxOffersPerSearch": 500,
  "sortBy": "gravity",
  "sortDescending": true,
  "mode": "all",
  "recurringOnly": false,
  "languages": [],
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `offers` (type: `string`):

No description

## `changes` (type: `string`):

No description

## `csv` (type: `string`):

No description

## `categories` (type: `string`):

No description

## `categoryJson` (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 = {
    "keywords": [
        "guitar"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("cauldo/clickbank-offer-monitor").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 = { "keywords": ["guitar"] }

# Run the Actor and wait for it to finish
run = client.actor("cauldo/clickbank-offer-monitor").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 '{
  "keywords": [
    "guitar"
  ]
}' |
apify call cauldo/clickbank-offer-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,cauldo/clickbank-offer-monitor"
        }
    }
}
```

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/HXPodLV4RTbT56whi/builds/CgpzUxmGE8fcQ4YMI/openapi.json
