# Indie Hackers Products Scraper (`devilscrapes/indie-hackers-products-scraper`) Actor

Scrape the Indie Hackers product directory — name, tagline, logo, and self-reported monthly revenue for every bootstrapped SaaS product. Filter by category and sort by revenue. Export to JSON or CSV. We handle the blocks so your dataset stays clean.

- **URL**: https://apify.com/devilscrapes/indie-hackers-products-scraper.md
- **Developed by:** [DevilScrapes](https://apify.com/devilscrapes) (community)
- **Categories:** Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

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

<div align="center">
  <img src=".actor/icon.svg" width="160" alt="Devil Scrapes mark" />
</div>

## Indie Hackers Products Scraper

**💰 $5.00 / 1 000 results**  ·  pay only for results  ·  no credit card to try

*The devil's in the data.* 😈

Indie Hackers is where bootstrapped founders publish their product and,
often, their real self-reported monthly revenue. This Actor turns that
directory into clean rows — name, tagline, logo, MRR, category — so you
can screen the bootstrapped SaaS market without opening a single tab.

### 🎯 What this scrapes

The Indie Hackers `/products` directory, filtered by zero or more
categories and sorted the way you choose (recently updated, recently
added, highest revenue, or lowest revenue). Each row is one product card:
its slug, name, tagline, logo, and self-reported monthly revenue (when the
founder chose to publish one).

### 🔥 Features

- 🛡️ **We rotate browser fingerprints** (curl-cffi impersonation across
  Chrome, Firefox, and Safari profiles) so every request looks like a
  real browser, not a script.
- 🔁 **We retry with exponential backoff** on `408 / 429 / 503` and honour
  `Retry-After` — up to 5 attempts per category page.
- 🧱 **We isolate faults, not runs.** A malformed card is skipped and
  logged; a category that fails to load is skipped too. Your run only
  fails outright when every requested category is unreachable.
- 🧊 **Dedup you can trust.** A product that shows up under two categories
  you requested is emitted once, attributed to whichever category you
  listed first.
- 💰 **You pay only for results that land.** No data → no charge beyond
  the small warm-up fee.

### 💡 Use cases

- **SaaS founders** scouting competitor pricing and positioning inside a
  specific category before they launch.
- **Acquirers and brokers** screening bootstrapped products by
  self-reported MRR for acquisition leads.
- **Investors and researchers** tracking how the indie/bootstrapped SaaS
  market is trending category by category.
- **Growth marketers** building a target list of tools in a niche to pitch
  affiliate or integration partnerships.

### ⚙️ How to use it

1. Pick one or more `categories` (leave empty for the unfiltered
   directory) and a `sorting` order — `highest-revenue` surfaces the
   biggest self-reported earners first.
2. Set `maxProducts` to cap how many rows you want across all requested
   categories, after dedup.
3. Run it, then export to JSON, CSV, or Excel — or pull the dataset
   straight from the Apify API.
4. Re-run on a schedule to track how self-reported revenue for a category
   moves over time.

### 📥 Input

| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
| `categories` | `array<string>` | no | `["ai", "productivity"]` | Zero or more of the 57 valid Indie Hackers category slugs. Empty array = the unfiltered directory. |
| `sorting` | `string` | no | `"highest-revenue"` | One of `recently-updated`, `recently-added`, `highest-revenue`, `lowest-revenue`. |
| `maxProducts` | `integer` | no | `40` | Hard cap on total rows across all requested categories, after dedup (1-500). |
| `proxyConfiguration` | `object` | no | `{"useApifyProxy": false}` | Apify Proxy configuration. No anti-bot surface confirmed on this target, so proxy is optional. |

#### Example input

```json
{
  "categories": ["ai", "productivity"],
  "sorting": "highest-revenue",
  "maxProducts": 10
}
```

### 📤 Output

One row per product matching your filters.

| Field | Type | Notes |
|---|---|---|
| `product_slug` | `string` | Slug parsed from the product's `/product/<slug>` URL. |
| `product_url` | `string` | Full absolute URL to the product's Indie Hackers page. |
| `name` | `string` | Product display name. |
| `tagline` | `string \| null` | Product tagline, if present. |
| `logo_url` | `string \| null` | Product logo/avatar image URL. |
| `monthly_revenue_usd` | `integer \| null` | Self-reported monthly revenue in USD. `null` when the product did not self-report — never faked as `0`. |
| `revenue_period` | `string \| null` | Revenue period label (e.g. `"month"`). `null` when revenue is absent. |
| `matched_category` | `string \| null` | First category slug this product was discovered under this run. |
| `matched_sorting` | `string` | The sorting value used for the request that found this row. |
| `scraped_at` | `string` | ISO-8601 UTC timestamp this row was scraped. |

#### Example output

```json
{
  "product_slug": "jotlee",
  "product_url": "https://www.indiehackers.com/product/jotlee",
  "name": "Jotlee",
  "tagline": "Everything job seekers need in one AI-powered platform",
  "logo_url": "https://storage.googleapis.com/indie-hackers.appspot.com/product-avatars/jotlee/128x128_jotlee.webp",
  "monthly_revenue_usd": 0,
  "revenue_period": "month",
  "matched_category": "ai",
  "matched_sorting": "highest-revenue",
  "scraped_at": "2026-09-14T10:00:00+00:00"
}
```

### 💰 Pricing

Pay-Per-Event — you pay only when these events fire:

| Event | USD | What it is |
|---|---:|---|
| `actor-start` | $0.20 | One-off warm-up charge per run |
| `result` | $0.0048 | Per product row written to the dataset |

Example: 1 000 products = **$5.00 per run** ($0.20 start + 1000 x $0.0048).
No subscription, no minimum, no card required to try it.

### 🚧 Limitations

- **Revenue is self-reported and unvalidated.** Indie Hackers never audits
  the numbers founders publish — treat `monthly_revenue_usd` as a signal
  of what a founder is willing to claim, not audited financials.
- **~17-18 cards per category page, no deep pagination in v1.** The site's
  own "Load More" pagination runs through an undocumented client-side
  index that could change without notice; iterating multiple categories
  is how you get breadth today. Deeper pagination is a candidate for a
  future version.
- **`maxProducts` counts post-dedup rows.** If two requested categories
  share a product, it only counts once against your cap.
- **No product detail-page enrichment.** This Actor scrapes the directory
  card only (name, tagline, logo, revenue) — founder story and milestone
  data from the individual product page are out of scope for v1.

### ❓ FAQ

**Is the revenue data accurate?**
It's exactly what the founder chose to publish on their own product page —
self-reported, not verified by Indie Hackers or by us. Use it as a
directional signal, not a financial statement.

**Can I filter by more than one category in a single run?**
Yes. Pass an array to `categories`; each one is fetched as its own
request, and products that appear under more than one requested category
are only counted once.

**What happens if a category has no self-reported revenue for some
products?**
Those rows come back with `monthly_revenue_usd` and `revenue_period` both
`null` — never a fabricated `0`.

**Do I need to configure a proxy?**
No. We didn't find any anti-bot surface on this target during testing, so
`proxyConfiguration` defaults to a direct connection. You can still turn
on Apify Proxy if you want the extra layer.

### 🙋 Your feedback

Found a category slug that changed, a card layout we mis-parse, or a
feature you wish this Actor had? Open an issue on the Actor's Apify Store
page or message **DevilScrapes** directly — we ship fixes fast.

# Actor input Schema

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

Zero or more Indie Hackers category slugs. Leave empty for the unfiltered directory (site default sort).

## `sorting` (type: `string`):

How Indie Hackers should sort each category page.

## `maxProducts` (type: `integer`):

Hard cap on TOTAL rows across all requested categories, after dedup. It is a global budget, not per-category, so keep it comfortably above one page (~17 products) per category you request — otherwise the first category consumes the whole budget and later ones return nothing.

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

No anti-bot surface confirmed on this target — proxy optional, defaults to a direct connection.

## Actor input object example

```json
{
  "categories": [
    "ai",
    "productivity"
  ],
  "sorting": "highest-revenue",
  "maxProducts": 30,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `datasetItems` (type: `string`):

All dataset items as JSON.

## `datasetItemsCsv` (type: `string`):

Same data exported to CSV.

## `datasetView` (type: `string`):

Open the run dataset in the Console.

# 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 = {
    "categories": [
        "ai",
        "productivity"
    ],
    "sorting": "highest-revenue",
    "maxProducts": 30,
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("devilscrapes/indie-hackers-products-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 = {
    "categories": [
        "ai",
        "productivity",
    ],
    "sorting": "highest-revenue",
    "maxProducts": 30,
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("devilscrapes/indie-hackers-products-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 '{
  "categories": [
    "ai",
    "productivity"
  ],
  "sorting": "highest-revenue",
  "maxProducts": 30,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call devilscrapes/indie-hackers-products-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,devilscrapes/indie-hackers-products-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/kt1rrDHy4ij4vbe7E/builds/E4MgkygJRqe2BDhSt/openapi.json
