# outbid.lol Scraper - Bids, Clicks & Change Monitoring (`memo23/outbid-scraper`) Actor

Know what every outbid.lol listing pays and what it gets. The full pay-to-rank board in under 3 seconds, with clicks-per-dollar and cost-per-click on every row, plus change monitoring that reports who moved, who was outranked and who joined since your last run. No proxy cost.

- **URL**: https://apify.com/memo23/outbid-scraper.md
- **Developed by:** [Muhamed Didovic](https://apify.com/memo23) (community)
- **Categories:** Lead generation, SEO tools, Automation
- **Stats:** 15 total users, 15 monthly users, 90.5% runs succeeded, 0 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

from $4.00 / 1,000 listings

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?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## outbid.lol Scraper — Leaderboard, Bids, Clicks & Change Monitoring

Scrape the entire [outbid.lol](https://outbid.lol/) pay-to-rank leaderboard — every listing's rank, bid, claim price, click count, cost-per-click, description and listing date — then track what moves between runs.

![How the outbid.lol Scraper works](https://raw.githubusercontent.com/muhamed-didovic/muhamed-didovic.github.io/main/assets/how-it-works-outbid.png)

### Why use this scraper

- **The whole board, not the first page.** Roughly 900 listings in under 3 seconds. No cap at rank 1,000.
- **Change monitoring built in.** The board re-ranks continuously. Turn on monitoring and each run reports what is new, gone, re-ranked, raised or gaining clicks, with the previous value and the delta on every row. Set `onlyChanges` and a scheduled run bills for the handful of rows that moved instead of the whole board every time.
- **Filters that match how people actually query a leaderboard.** Rank *ranges* (not just a ceiling), bid and click floors *and* ceilings, cost-per-click bounds, include *and* exclude keywords, per-field keyword targeting, domain and TLD filters, listing type, and three ways to filter by date.
- **Board-level context, free.** One unbilled `boardStats` row per run carries live visitors, 30-day visitors, visitors since launch, total board revenue, listing count and the sum of every live bid.
- **No proxy cost.** The actor clears the site's Vercel security checkpoint itself and runs direct by default. You are not paying for residential bandwidth you do not need.
- **Opt-in contact emails.** Every listing carries its own website, so email enrichment scrapes the real site rather than guessing at a company match. Billed only when an email is actually found.

### Overview

outbid.lol is a public leaderboard where rank is bought: you pay a dollar amount and that amount *is* your position. It launched on 19 August 2026 and has carried over a million visitors, roughly 900 paid listings and about $132,000 in bids.

That makes the board a live, priced signal of what indie founders and small SaaS companies are willing to spend to get traffic — and, because every listing publishes its click count, of what that traffic is actually worth. This actor turns the board into structured rows you can sort, filter, diff and export.

The site renders each board page as a React Server Component payload, so the actor decodes that payload directly rather than parsing markup. There are no detail pages to fetch and no browser to run: every field arrives with the listing, 50 per request.

### Supported inputs

| Input | What it does |
|---|---|
| *(nothing)* | Run with an empty input and get the entire board. |
| `https://outbid.lol/` | The board. Equivalent to an empty input. |
| `https://outbid.lol/?page=3` | Accepted; the actor still walks the board from rank 1. |

Only outbid.lol URLs are accepted. Anything else fails immediately with a clear message rather than silently scraping the board instead.

### Use cases

- **Competitive and ad-spend research.** See exactly what competitors pay for placement and what each of them gets for it in clicks.
- **Price the board before you bid.** `claimPriceUsd` tells you what it costs to take any given rank right now — $5 over the top bid for #1, $1 over the listing for anything else.
- **Find undervalued placement.** Sort by `clicksPerDollar`, or filter with `maxCostPerClickUsd`, to see which bids are buying traffic cheaply.
- **Watch your own listing.** Monitor mode tells you the moment you are outranked, with the rank delta and who passed you.
- **Track a launch cohort.** Filter by `listedWithinHours: 24` to get every product that joined the board today.
- **Build a lead list.** The board is 1,300+ founders who just demonstrated they will pay for traffic, each with a live URL and a self-written positioning line. Turn on `enrichEmails` to attach contact details.

### How it works

1. **Clear the checkpoint.** outbid.lol fronts the board with Vercel's security checkpoint. The actor clears it once at the start in a headless browser, then reuses that session for every request — no proxy needed, and no browser at all if the checkpoint happens to be off.
2. **Read the board API.** Listings come straight from the site's own `/api/ranking` JSON endpoint — 50 per page, each with every field the board knows — so there are no DOM selectors to break on a redesign.
3. **Paginate in parallel.** The API reports the board's exact size, so the actor fetches all pages concurrently and knows precisely when it is done. When `maxRank` is the only filter set, it stops as soon as that rank is covered instead of pulling the whole board and discarding most of it.
4. **Derive the economics.** `clicksPerDollar`, `costPerClickUsd` and `claimPriceUsd` are computed per listing from the live board state.
5. **Diff against the last run.** With monitoring on, a snapshot in a named key-value store is compared to the current board and change rows are emitted.

### Input configuration

Every field is optional.

| Field | Type | Default | Description |
|---|---|---|---|
| `startUrls` | array | `[]` | outbid.lol URLs. Optional — the board is one paginated resource. |
| `minRank` / `maxRank` | integer | `0` | Rank range. `0` = no bound. |
| `minBidUsd` / `maxBidUsd` | integer | `0` | Bid range in USD. |
| `minClicks` / `maxClicks` | integer | `0` | Click-count range. |
| `minCostPerClickUsd` / `maxCostPerClickUsd` | number | `0` | Cost-per-click range. Listings with zero clicks are excluded when either bound is set. |
| `keywords` | array | `[]` | Keep listings matching any of these. |
| `excludeKeywords` | array | `[]` | Drop listings matching any of these. Wins over `keywords`. |
| `keywordFields` | array | `[]` | Limit matching to `url`, `domain`, `handle`, `name` or `description`. |
| `domains` | array | `[]` | Domain filter. Subdomains included; a bare TLD like `.ai` works. |
| `identityTypes` | array | `[]` | `website`, `x`, or both. |
| `listedWithinHours` | integer | `0` | Only listings first listed in the last N hours. |
| `listedAfter` / `listedBefore` | string | `""` | ISO date bounds, e.g. `2026-08-21`. |
| `sortBy` | string | `rank` | `rank`, `bidDesc`, `bidAsc`, `clicksDesc`, `clicksPerDollarDesc`, `newest`, `oldest`. |
| `monitorChanges` | boolean | `false` | Diff against the previous run and emit change rows. |
| `onlyChanges` | boolean | `false` | Emit only the changes. Requires `monitorChanges`. |
| `monitorStoreName` | string | `outbid-monitor` | Named key-value store holding the snapshot. |
| `includeBoardStats` | boolean | `true` | Append the unbilled board-statistics row. |
| `includeTrending` | boolean | `true` | Add `clicksLastHour` to listings currently trending. |
| `enrichEmails` | boolean | `false` | Scrape each listing's site for contact details. |
| `maxItems` | integer | `100000` | Row cap. Free Apify accounts are capped at 100. |
| `maxConcurrency` | integer | `8` | Pages fetched in parallel. |
| `proxy` | object | direct | Off by default; the actor clears the site's checkpoint without one. |

#### Monitoring: give each schedule its own store

The snapshot lives in a **named** key-value store (`outbid-monitor` by default) because a run's default store is created fresh each run and could never carry state forward. If you run several monitors with different filters, give each one its own `monitorStoreName` — otherwise they overwrite each other's snapshot and every run reports the whole board as "new".

### Output overview

Three row types, distinguished by `rowType`:

| `rowType` | What it is | Billed |
|---|---|---|
| `listing` | One board entry at its live rank. | Yes |
| `change` | One listing that moved since the last run, with previous values and deltas. | Yes |
| `boardStats` | One per run: visitors, revenue, listing count, summed bids. | **No** |

### Output samples

A `listing` row:

```json
{
  "rowType": "listing",
  "rank": 1,
  "bidUsd": 14013,
  "claimPriceUsd": 14018,
  "identityType": "website",
  "identityKey": "website:joni.ai",
  "sourceUrl": "https://joni.ai/",
  "domain": "joni.ai",
  "handle": null,
  "displayName": "JONI | Your Personal AI Computer",
  "description": "JONI is your personal AI computer. Chat once and a team of AI agents and skills gets to work, with the right model picked for every job.",
  "clickCount": 8411,
  "clicksLastHour": 552,
  "clicksPerDollar": 0.6,
  "costPerClickUsd": 1.666,
  "isTakeover": false,
  "listedAt": "2026-08-21T19:52:11.016Z",
  "listedAgoHours": 15.2,
  "entryId": "e52daf73-79da-4093-a811-f27c0ba908ac",
  "boardUrl": "https://outbid.lol/",
  "scrapedAt": "2026-08-22T11:06:04.587Z"
}
```

A `change` row:

```json
{
  "rowType": "change",
  "changeType": "rankUp",
  "rank": 6,
  "previousRank": 9,
  "rankDelta": 3,
  "bidUsd": 3500,
  "previousBidUsd": 3000,
  "bidDeltaUsd": 500,
  "clickCount": 921,
  "previousClickCount": 874,
  "clickDelta": 47,
  "domain": "fuellog.ai",
  "previousSnapshotAt": "2026-08-22T10:05:21.311Z"
}
```

The `boardStats` row:

```json
{
  "rowType": "boardStats",
  "liveVisitorCount": 685,
  "visitorsLast30Days": 127537,
  "visitorsSinceLaunch": 1154999,
  "boardRevenueUsd": 132785,
  "totalListings": 900,
  "sumOfLiveBidsUsd": 132791
}
```

### Key output fields

| Field | Meaning |
|---|---|
| `rank` | Live board position. Always the true rank, never renumbered by filtering. |
| `bidUsd` | What this listing currently pays. |
| `claimPriceUsd` | What it would cost you to take this exact rank right now. |
| `clickCount` | Lifetime clicks the board has recorded for this listing. |
| `clicksLastHour` | Clicks in the last hour, for listings currently trending. |
| `clicksPerDollar` | `clickCount / bidUsd` — the board's value metric. Higher is better placement value. |
| `costPerClickUsd` | `bidUsd / clickCount`. `null` when a listing has no clicks yet. |
| `isTakeover` | Whether this listing bought a full-board takeover (2× the top bid). |
| `listedAt` | When the listing first appeared. Raises do not change it. |
| `changeType` | `new`, `gone`, `rankUp`, `rankDown`, `bidRaised` or `clicksJumped`. |

**Not included, on purpose:** outbid.lol's public payload also carries `polarOrderId` and `polarCheckoutId` — its payment processor's internal order references for each buyer's transaction. They are of no analytical use, and reselling someone else's payment identifiers is not something this actor does. They are dropped.

### FAQ

**How many listings are there?**
About 1,360 as of late August 2026, and still growing daily. There is no cap in this actor — you always get the full board, and its exact size is reported in the `boardStats` row.

**How fast is it?**
The full board harvests in under 3 seconds once the run is going. Clearing the site's security checkpoint adds a few seconds of headless-browser startup at the beginning of each run.

**Does it need a proxy?**
No. outbid.lol fronts the board with Vercel's security checkpoint, but the actor clears it itself and then reads the board's JSON API directly. The proxy input exists if you want the traffic from a specific network, but leaving it off costs you nothing in quality and saves you the proxy bill.

**Why did my first monitoring run report zero changes?**
There was nothing to compare against. The first run stores a baseline; the second run onward reports the diff.

**Why did a monitoring run report the whole board as `new`?**
The snapshot is per store, and it records the listings your filters returned. If you change filters between runs, everything outside the old filter looks new. Give each filter configuration its own `monitorStoreName`.

**Can I get contact emails?**
Turn on `enrichEmails`. Because each listing already carries its own website, enrichment reads that site directly instead of guessing which company a name refers to. It is best-effort — expect a hit rate, not a guarantee — and you are billed only for listings where an email is found.

**Are `costPerClickUsd` figures reliable?**
They are exactly `bidUsd / clickCount` as the board reports both. Note that outbid.lol's click counter is an open, unauthenticated endpoint, so a handful of listings show click counts far outside the distribution of everything else. Treat extreme outliers as unverified.

**Do I pay for the boardStats row?**
No. It is one row of context per run and it is not billed.

### Support

Found a bug or need a field that is not here? Open an issue on the actor's Issues tab and it will be looked at.

### Additional services

Need a scraper for a site that is not on the Store yet, or a custom data pipeline? Get in touch through the Apify profile.

### Explore more scrapers

- [Whop Leaderboards Scraper](https://apify.com/memo23/whop-leaderboards-scraper)
- [Uneed.best Scraper](https://apify.com/memo23/uneed-scraper)
- [YouTube Comments Scraper](https://apify.com/memo23/youtube-comments-scraper)

### 🤖 For AI agents & LLM apps

This actor is a good fit for agent pipelines: it needs no input to produce useful output, it returns strictly typed rows, and it is cheap and fast enough to call on a schedule.

```json
{
  "maxRank": 50,
  "sortBy": "clicksPerDollarDesc",
  "includeBoardStats": true
}
```

Call it via the Apify API, or through the Apify MCP server as `memo23/outbid-scraper`. For a change feed, set `monitorChanges: true` and `onlyChanges: true` and poll on a schedule — a quiet interval returns a handful of rows.

### ⚠️ Disclaimer

This actor collects only data that outbid.lol publishes publicly and serves to any visitor without authentication. It does not bypass access controls, solve challenges, log in, or collect private personal information. Payment-processor identifiers present in the site's public payload are deliberately excluded from the output.

You are responsible for how you use the data, including compliance with outbid.lol's terms, applicable data-protection law such as GDPR and CCPA, and anti-spam rules such as CAN-SPAM if you use the optional email-enrichment output. Scrape responsibly and at a reasonable rate.

### SEO keywords

outbid.lol scraper, outbid leaderboard scraper, outbid.lol api, pay to rank leaderboard data, leaderboard scraper, bid tracking scraper, startup directory scraper, indie hacker directory data, click analytics scraper, cost per click data, competitive intelligence scraper, advertising research data, product launch tracker, rank monitoring, leaderboard change detection, startup lead generation, founder contact emails, apify outbid actor

# Actor input Schema

## `startUrls` (type: `array`):

outbid.lol board URLs. The board is one paginated resource, so this is optional — leave it empty and the whole board is crawled from rank 1. Only outbid.lol URLs are accepted; anything else fails fast rather than silently scraping the board instead.

## `minRank` (type: `integer`):

Start from this rank. Pair with 'Maximum rank' to pull a slice such as ranks 50-100 without paying for the rows above it. Empty or 0 = start at rank 1.

## `maxRank` (type: `integer`):

Stop after this rank. Empty or 0 = the entire board (roughly 900 listings and growing). When this is the only filter set, pagination stops early instead of pulling the whole board and discarding it.

## `minBidUsd` (type: `integer`):

Only listings bidding at least this much. The board floor is $5 for new listings, though older entries sit as low as $1.

## `maxBidUsd` (type: `integer`):

Only listings bidding at most this much. Empty or 0 = no ceiling.

## `minClicks` (type: `integer`):

Only listings with at least this many recorded clicks.

## `maxClicks` (type: `integer`):

Only listings with at most this many clicks. Use with a low 'Maximum bid' to surface cheap listings that have not been discovered yet. Empty or 0 = no ceiling.

## `minCostPerClickUsd` (type: `number`):

Only listings whose bid divided by clicks is at least this. Listings with zero clicks have no cost per click and are excluded whenever either CPC bound is set.

## `maxCostPerClickUsd` (type: `number`):

Only listings whose bid divided by clicks is at most this — the 'who is buying traffic cheaply' query. Empty or 0 = no ceiling.

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

Keep only listings matching any of these (case-insensitive substring). Empty = keep everything.

## `excludeKeywords` (type: `array`):

Drop listings matching any of these. Applied after the include list, so an excluded term always wins.

## `keywordFields` (type: `array`):

Restrict keyword matching to specific fields. Empty = match across the URL, domain, handle, name and description at once.

## `domains` (type: `array`):

Only listings pointing at these domains. Subdomains are included automatically, and a bare TLD such as `.ai` matches every listing on it.

## `identityTypes` (type: `array`):

The board carries product websites and X profiles. Empty = both.

## `listedWithinHours` (type: `integer`):

Only listings first listed within this many hours. 24 = the last day. Empty or 0 = all dates.

## `listedAfter` (type: `string`):

ISO date or datetime, e.g. `2026-08-21` or `2026-08-21T12:00:00Z`.

## `listedBefore` (type: `string`):

ISO date or datetime, e.g. `2026-08-22`.

## `monitorChanges` (type: `boolean`):

Emit `rowType: "change"` rows for listings that are new, gone, re-ranked, raised, or gaining clicks. Each carries the previous value and the delta. The first run has nothing to compare against, so it stores a baseline and reports zero changes.

## `onlyChanges` (type: `boolean`):

Skip the full board and return the change rows alone. This is the cheap scheduled-run setting: a quiet hour costs a handful of rows instead of the entire board. Requires 'Track changes since the last run'.

## `monitorStoreName` (type: `string`):

Named key-value store holding the snapshot. Defaults to `outbid-monitor`. It must be a named store — a run's default store is created fresh each run, so a snapshot there would never survive to the next run. Give each scheduled monitor its own name so several can track different filters independently.

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

Ordering of the listing rows. Ranks are unaffected — a row always reports its real board position.

## `includeBoardStats` (type: `boolean`):

Append one `rowType: "boardStats"` row with live visitors, 30-day visitors, visitors since launch, total board revenue, listing count and the sum of all live bids. This row is never billed.

## `includeTrending` (type: `boolean`):

Fold the board's trending feed in, so listings currently gaining traffic carry a `clicksLastHour` value alongside their lifetime click count. Costs one extra request per run.

## `enrichEmails` (type: `boolean`):

Best-effort: visit each listing's own website and harvest contact emails, phone numbers and social profiles into `contactEmail` and `emailEnrichment`. Every listing on the board already carries its website, so no company-matching guesswork is involved. Billed only for listings where an email is actually found. Adds roughly a second per listing.

## `maxItems` (type: `integer`):

Hard cap on rows pushed to the dataset, counting listing and change rows together. Free-tier Apify accounts are capped at 100.

## `maxConcurrency` (type: `integer`):

Board pages fetched in parallel. The default is comfortable; the site answers 12 concurrent requests in under a second, but there is no reason to push it.

## `proxy` (type: `object`):

Runs direct by default, so you pay nothing for proxies. outbid.lol fronts the board with Vercel's security checkpoint; the actor clears it in-run on its own, no proxy required. Enable one only if you want the traffic to originate from a specific network — it will be used for both the checkpoint and the scrape.

## Actor input object example

```json
{
  "startUrls": [
    "https://outbid.lol/"
  ],
  "minRank": 0,
  "maxRank": 100,
  "minBidUsd": 0,
  "maxBidUsd": 0,
  "minClicks": 0,
  "maxClicks": 0,
  "minCostPerClickUsd": 0,
  "maxCostPerClickUsd": 0,
  "keywords": [],
  "excludeKeywords": [],
  "keywordFields": [],
  "domains": [],
  "identityTypes": [],
  "listedWithinHours": 0,
  "listedAfter": "",
  "listedBefore": "",
  "monitorChanges": false,
  "onlyChanges": false,
  "monitorStoreName": "",
  "sortBy": "rank",
  "includeBoardStats": true,
  "includeTrending": true,
  "enrichEmails": false,
  "maxItems": 1000,
  "maxConcurrency": 8,
  "proxy": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `results` (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 = {
    "startUrls": [
        "https://outbid.lol/"
    ],
    "maxRank": 100,
    "maxItems": 1000
};

// Run the Actor and wait for it to finish
const run = await client.actor("memo23/outbid-scraper").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 = {
    "startUrls": ["https://outbid.lol/"],
    "maxRank": 100,
    "maxItems": 1000,
}

# Run the Actor and wait for it to finish
run = client.actor("memo23/outbid-scraper").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 '{
  "startUrls": [
    "https://outbid.lol/"
  ],
  "maxRank": 100,
  "maxItems": 1000
}' |
apify call memo23/outbid-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,memo23/outbid-scraper"
        }
    }
}

```

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/aB3n7IYpWhnpksfRS/builds/WCuNq8dMLKOhjc6fB/openapi.json
