# BetaList Scraper - Startup Profiles, Founders & Contacts (`abotapi/betalist-com-scraper`) Actor

Scrape BetaList.com startup profiles with founder and contact enrichment. Extract startup names, taglines, descriptions, topics, regions, images, websites, social links, public emails, phone numbers, and founder details.

- **URL**: https://apify.com/abotapi/betalist-com-scraper.md
- **Developed by:** [Abot API](https://apify.com/abotapi) (community)
- **Categories:** Lead generation, Jobs, Automation
- **Features:** Limited permissions
- **Stats:** 5 total users, 2 monthly users, 100.0% runs succeeded, 1 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.25 / 1,000 startup results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## BetaList Scraper: Startup Profiles, Websites and Contacts

BetaList Scraper turns BetaList into structured startup data. Discover newly launched and upcoming startups by keyword, topic, region or the latest feed, or paste exact BetaList links, then get each startup's full profile: tagline, description, topics, regions, feature date, screenshots and the resolved company website. Turn on contact enrichment to also pull public emails, phone numbers and social profile links straight from the startup's own site, then export to JSON, CSV or Excel, or read the results through the API.

### Why This Scraper?

- **Two ways to source.** Search by keyword, topic, region or the latest feed, or paste exact BetaList links, including a single startup page.
- **Full startup profile.** Name, tagline, description, topics, regions, feature date, screenshots and logo, plus the resolved company website behind BetaList's own redirect link.
- **Contacts on demand.** Turn on contact enrichment to pull public emails, phone numbers and social profile links (LinkedIn, X, Facebook, Instagram, YouTube, GitHub and more) straight from the startup's own site.
- **Filters that matter.** Boosted-only, and a feature-date window to keep only startups launched in a given range.
- **Deep, budgeted collection.** Walk as many result pages as you choose per source, capped by Max startups so cost stays predictable.
- **Built for monitoring.** Incremental mode tracks the same search over time and returns only what's new or changed, and Resume continues one interrupted run without paying twice.

### Use Cases

- **Investors and scouts:** track newly launched startups by topic or region to build a fresh deal-sourcing list.
- **Sales and growth teams:** build prospect lists with verified websites, emails and social profiles for outbound outreach.
- **Market researchers:** map which topics and regions are trending on BetaList over time.
- **Founders and PR teams:** monitor competitors' launches, taglines and feature dates.
- **Newsletter and content creators:** source fresh startup profiles for roundups and features.

### Data You Get

> Sample shape: values are illustrative placeholders, not from a live listing.

| Field | Example |
| --- | --- |
| `name` | `"Sample Startup"` |
| `one_liner` | `"A short one-line pitch for the product"` |
| `description` | `"Full description text appears here when detail pages are fetched."` |
| `url` | `"https://betalist.com/startups/sample-startup"` |
| `slug` | `"sample-startup"` |
| `website_url` | `"https://sample-startup.example.com"` |
| `website_domain` | `"sample-startup.example.com"` |
| `boosted` | `false` |
| `featured_at` | `"2026-01-01"` |
| `featured_date_label` | `"January 1, 2026"` |
| `topic_names` | `["Artificial Intelligence", "Productivity"]` |
| `regions` | `[{ "name": "California", "slug": "california" }]` |
| `primary_image_url` | `"https://images.betalist.com/startup/sample-startup/cover.jpg"` |
| `makers` / `maker_name` / `maker_link` / `maker_identifier` | `[{ "name": "Sample Maker", "handle": "samplemaker", "url": "https://betalist.com/@samplemaker" }]` (first maker in the flat fields; empty when the profile lists none; needs detail pages) |
| `product_name`, `long_description`, `website`, `featured_date` | Aliases of `name`, `description`, `website_url`, `featured_at` for drop-in compatibility |
| `image_urls` | `["https://images.betalist.com/startup/sample-startup/cover.jpg"]` |
| `logo_url` | `"https://cdn.betalist.com/000000"` |
| `similar_startups` | `[{ "name": "Another Sample Startup", "slug": "another-sample-startup" }]` |
| `contacts.emails` | `["hello@example.com"]` |
| `contacts.phone_numbers` | `["+10000000000"]` |
| `contacts.social_media` | `{ "linkedin": "https://www.linkedin.com/company/example" }` |
| `id` | `10000001` |

BetaList does not publish user reviews, ratings or comments on startup pages, so this actor produces none. The site also does not expose a distinct "launched" or "removed" status on a startup profile itself: whether a startup is still findable on the same search, topic, region or the latest feed run to run is the only lifecycle signal available, and Incremental mode's `changeType` tracks that presence over time (see How to Use below). The only other social signal on a profile page is the "Similar startups" suggestions, captured in `similar_startups`.

### How to Use

1. Pick a **mode**: `search` (by keyword, topic, region or the latest feed) or `url` (paste exact BetaList links, including a single startup page).
2. For search mode, fill in keywords, a topic or a region (names such as `Artificial Intelligence` or `California`, or slugs such as `california`; a topic or region BetaList does not serve fails the run with a clear error), plus any filters; for url mode, paste the links. When `urls` is omitted from the input (API calls), url mode scrapes (a behaviour change from earlier versions) six examples (previously nothing): the latest feed, a keyword search (`ai`), a browse topic (Artificial Intelligence), a region (California), a country region (United States) and a single startup page (Agent UI). The Console form starts pre-filled with example links of every supported shape (including paginated, sorted and `www.`/`http://` variants); replace them with your own links before running.
3. Turn on **Fetch detail pages** and/or **Enrich with contacts** for full profiles and contact signals, and set **Max startups** to control run size and cost.
4. Click **Start**, then download the dataset as JSON, CSV or Excel, or read it through the API.

**Search by keyword:**

```json
{
  "mode": "search",
  "queries": ["ai", "fintech"],
  "maxItems": 50
}
```

**Search a topic with full profiles and contact enrichment:**

```json
{
  "mode": "search",
  "topic": "artificial-intelligence",
  "getContacts": true,
  "fetchDetails": true,
  "maxItems": 25
}
```

**Boosted startups only, for a keyword:**

```json
{
  "mode": "search",
  "queries": ["ai"],
  "boostedOnly": true,
  "maxItems": 20
}
```

**Paste exact BetaList links (a topic page and a single startup page):**

```json
{
  "mode": "url",
  "urls": [
    "https://betalist.com/topics/artificial-intelligence",
    "https://betalist.com/startups/autobound"
  ],
  "maxItems": 30
}
```

#### Run it from your code

Python:

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("abotapi/betalist-com-scraper").call(run_input={"mode": "search", "queries": ["ai"], "maxItems": 20})
for startup in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(startup["name"], startup["website_url"])
```

JavaScript:

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

const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('abotapi/betalist-com-scraper').call({ mode: 'search', queries: ['ai'], maxItems: 20 });
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

Or connect it to Make, Zapier, n8n, Google Sheets or webhooks from the **Integrations** tab.

#### How Max startups and Max pages work together

**Max startups** is the run's real budget: the actor stops pushing new records once it is reached, and splits that budget evenly across every keyword, topic, region or URL you gave it. **Max pages** is only a per-source safety ceiling on how many result pages to walk looking for that budget; leave it at the default unless a single source needs to page unusually deep. Boosted-only and feature-date filters drop cards after they are fetched, so a heavily filtered search may return fewer startups than Max startups even before either limit is reached.

#### Resume and recurring updates

Two different features, both off by default so existing behavior is unchanged:

- **Resume** (`resumeFromRunId`): paste a previous run ID or dataset ID to continue one specific interrupted collection. A startup slug already saved there is never pushed or billed again. For a pasted single-startup URL this also skips fetching it entirely. For search, topic, region and latest-feed sources, the run still walks each source's result pages from the start (or from the page number encoded in a pasted URL), checking every card against the already-saved list, so a resumed run on a large source still fetches as many pages as a fresh one, it just avoids re-pushing and re-billing what it already has. Max startups still caps only the new startups pushed this run. If the pasted ID does not match a run or dataset this Apify account can read, the run fails immediately with a clear message instead of silently starting over.
- **Incremental mode** (`incrementalMode`): turn on for daily or recurring monitoring of the same search. The actor remembers the previous run's results itself (keyed on mode plus keywords/topic/region/URLs, the filters, and `fetchDetails`, or your own `stateKey`) and classifies every startup as `NEW`, `UPDATED` (with `changedFields`), `UNCHANGED`, `REAPPEARED` or `EXPIRED`. `UNCHANGED` startups are suppressed (not returned, not billed) unless `emitUnchanged` is on. A startup is only ever recorded as gone, and only then eligible to come back as `REAPPEARED` later, after a run with `emitExpired` on that was not resumed, did not hit Max startups, and reached the natural end of every tracked source; `emitExpired` also gates whether the `EXPIRED` row itself is returned and billed that run. A capped, resumed or partial run never marks a still-live startup as gone, and leaves its previously saved state untouched. Setting `resumeFromRunId` while `incrementalMode` already has saved state for that same search fails the run rather than silently mixing the two workflows; use a different `stateKey` to bootstrap a separate campaign instead.

A URL pasted with `?page=N` (N above 1) starts mid-list, so it counts as a partial scan and never produces `EXPIRED` rows.

Dataset rows produced by Incremental mode add four bookkeeping fields: `changeType`, `changedFields`, `firstSeenAt`, `lastSeenAt`. These are absent unless `incrementalMode` is on.

#### Output views and run summary

The dataset has four views in the Console: **Startups** (overview), **Startup details**, **Contacts** and **Changes** (incremental fields). Each run also saves a small `OUTPUT` record in the key-value store with startups pushed, sources requested and walked, and whether every source reached its natural end.

#### Filters

`boostedOnly` keeps only Boosted startups (both modes). `featuredAfter` and `featuredBefore` (YYYY-MM-DD) keep startups featured in that window and automatically turn on detail pages. Filters drop cards after they are fetched and are ignored for pasted single-startup URLs.

#### Supported URL shapes

In url mode paste any of: the latest feed (`https://betalist.com/`), search (`/search?q=ai`, with `&sort=` and `&page=`), topic (`/topics/<slug>`, resolved to its `/browse/...` page so `?page=N` is honoured), browse topic (`/browse/<category>/<topic>`), region and country pages (`/regions/<slug>`), or a single startup (`/startups/<slug>`); `www.` and `http://` links work. Index pages (`/browse`, `/browse/<category>`, `/regions`) list no startups and are skipped with a warning.

### Send results into your apps (MCP connectors)

Optionally pipe results into the apps you already use via Model Context Protocol (MCP) connectors. This is an extra delivery step after the scrape: the Apify dataset is never changed. Authorize a connector once under Apify, then Settings, then API & Integrations (Notion, Linear, Airtable or Apify), select it in the `mcpConnectors` input, and set `notionParentPageUrl` for the Notion connector. The connector receives a condensed, human-readable summary per startup (name, tagline, website, topics, one email or phone), not the full JSON; the complete record always stays in the Apify dataset. Leave the field empty to skip; it never changes the dataset output.

### Input Parameters

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `mode` | string | `search` | `search` (keywords/topic/region/latest feed) or `url` (paste BetaList links). |
| `queries` | array | none (input editor prefills `["ai"]` as a starting example) | Keywords to search on BetaList. Search mode only. |
| `topic` | string | none | A topic name, slug or browse path to collect. Search mode only. |
| `region` | string | none | A region name or slug to collect, e.g. `california`. Search mode only. |
| `boostedOnly` | boolean | `false` | Keep only startups flagged Boosted. Applies in both modes. |
| `featuredAfter` | string | none | Keep only startups featured on or after this date (YYYY-MM-DD); setting it automatically turns on detail-page fetching, since the feature date only appears on the profile page. |
| `featuredBefore` | string | none | Keep only startups featured on or before this date (YYYY-MM-DD); setting it automatically turns on detail-page fetching. |
| `urls` | array | six examples: latest feed, keyword search, browse topic, region, country region and a single startup page (used when `urls` is omitted from the input via the API; the Console form starts pre-filled with more example links, so replace them with your own) | BetaList links to collect: the latest feed, search results (including `&sort=` and `&page=`), topic/browse pages, region pages, or a single startup page (`www.` and `http://` links work). A `/topics/...` link is resolved to its canonical `/browse/...` page first, so `?page=N` is honoured. Index pages (`/browse`, `/browse/<category>`, `/regions`) list no startups and are skipped with a warning, as is a `/topics/...` link to a topic BetaList does not serve. An explicit empty list scrapes nothing. URL mode only. |
| `fetchDetails` | boolean | `true` | Open each startup's profile page for the full description, topics, regions, feature date, screenshots and resolved website. |
| `getContacts` | boolean | `false` | Visit each startup's own website for emails, phone numbers and social links. |
| `maxItems` | integer | `20` | Maximum startups to save across the run (`0` = unlimited, stops at Max pages instead). |
| `maxPages` | integer | `0` | Safety ceiling on pages walked per source. `0` uses the built-in limit of 200 pages per source; any positive value is used as given. The run normally stops at Max startups first. |
| `proxy` | object | Apify Proxy | Connection settings. |
| `mcpConnectors` | array | none | Send a summary of each result into apps you authorized under Integrations. Leave empty to skip. |
| `notionParentPageUrl` | string | none | Notion connector only: the page under which item pages are created. |
| `maxNotifyListings` | integer | `50` | Cap on items written to each connector per run. Does not affect the dataset. |
| `resumeFromRunId` | string | none | Continue one interrupted run without re-collecting or re-billing startups it already saved. |
| `incrementalMode` | boolean | `false` | Turn on for recurring monitoring of the same search; later runs return only what changed. |
| `stateKey` | string | none | Name an incremental-mode campaign explicitly instead of the auto-derived key. |
| `emitUnchanged` | boolean | `false` | Incremental mode only: also return and bill startups identical to the last run. |
| `emitExpired` | boolean | `false` | Incremental mode only: also mark and return (and bill for) startups no longer found, after a complete scan. |
| `ignoreFieldsForChanges` | string\[] | none | Incremental mode only: field names (dot for nested, e.g. `source.scraped_time`) that do not count as a change. They stay in the output. These are always ignored: `source.scraped_time`, `similar_startups` and the alias fields `product_name`, `long_description`, `website` and `featured_date` (copies of tracked fields). |

### Output Example

> Sample shape: values are illustrative placeholders, not from a live listing.

```json
{
  "type": "startup",
  "id": 10000001,
  "url": "https://betalist.com/startups/sample-startup",
  "slug": "sample-startup",
  "name": "Sample Startup",
  "one_liner": "A short one-line pitch for the product",
  "short_description": "A short one-line pitch for the product",
  "description": "Full description text appears here when detail pages are fetched.",
  "visit_url": "https://betalist.com/startups/sample-startup/visit",
  "website_url": "https://sample-startup.example.com",
  "website_domain": "sample-startup.example.com",
  "boosted": false,
  "featured_at": "2026-01-01",
  "featured_date_label": "January 1, 2026",
  "topics": [{ "name": "Artificial Intelligence", "slug": "artificial-intelligence", "url": "https://betalist.com/browse/ai/artificial-intelligence" }],
  "topic_names": ["Artificial Intelligence"],
  "makers": [{ "name": "Sample Maker", "handle": "samplemaker", "url": "https://betalist.com/@samplemaker" }],
  "maker_name": "Sample Maker",
  "maker_link": "https://betalist.com/@samplemaker",
  "maker_identifier": "samplemaker",
  "product_name": "Sample Startup",
  "long_description": "Full description text appears here when detail pages are fetched.",
  "website": "https://sample-startup.example.com",
  "featured_date": "2026-01-01",
  "image_urls": ["https://images.betalist.com/startup/sample-startup/cover.jpg"],
  "primary_image_url": "https://images.betalist.com/startup/sample-startup/cover.jpg",
  "logo_url": "https://cdn.betalist.com/000000",
  "regions": [{ "name": "California", "slug": "california", "url": "https://betalist.com/regions/california" }],
  "similar_startups": [{ "slug": "another-sample-startup", "name": "Another Sample Startup", "url": "https://betalist.com/startups/another-sample-startup", "tagline": "A related fictional pitch", "boosted": false }],
  "contacts": {
    "emails": ["hello@example.com"],
    "phone_numbers": ["+10000000000"],
    "social_media": { "linkedin": "https://www.linkedin.com/company/example", "twitter": "https://x.com/example" }
  },
  "contacts_lookup_url": "https://sample-startup.example.com",
  "source": {
    "betalist_url": "https://betalist.com/startups/sample-startup",
    "seed_value": "https://betalist.com/topics/artificial-intelligence",
    "source_url": "https://betalist.com/startups/sample-startup",
    "scraped_time": "2026-01-01T00:00:00.000Z"
  }
}
```

### Plan Requirement

BetaList served plain pages without any challenge, and the default Apify Proxy connection (automatic group) was measured serving its latest feed, search and startup pages, so a paid connection tier is not needed for normal runs. Apify free plans do not include Apify Proxy: on a free plan the default connection is refused, the log shows a warning that residential proxies are unavailable, and the run continues on a backup connection, logging "Served over the backup connection (the default connection was unavailable)." when a backup serves the page. On a paid plan the actor starts on the default connection (or the proxy groups you choose) and, if it is refused, retries on a residential connection and then on a backup connection. Residential traffic is billed to your Apify plan and is your choice; your own proxy URLs also work. If no BetaList page could be loaded at all, the run fails instead of returning an empty dataset, with one of these messages: "The proxy connection was refused for this account, so no page could be read. Your Apify plan does not include proxy access; a paid plan is required", "No results: residential proxies are unavailable on this run (Apify free plans do not include them), and the backup connection could not load the requested BetaList pages", or "Could not load any results page (the requests failed), so nothing was collected. This is a connection problem, not an empty search."

### FAQ

#### How much does it cost?

Three events are charged: **apify-actor-start** once per run, **apify-default-dataset-item** once for every startup row saved to the dataset (the main charge), and **detail-enrichment** once for every saved row whose profile page loaded and/or whose website contact lookup ran. Fetch detail pages defaults to on, and a pasted single-startup URL always opens its profile page, so most rows carry the detail-enrichment charge even when a loaded page or the website lookup finds nothing extra. The detail-enrichment charge is made per row, right after that row is saved, never for suppressed or unchanged rows, and never for a row where the profile page failed to load and no website contact lookup ran. The Pricing tab shows current rates; use Max startups to cap the cost of any run.

#### Is it legal to scrape BetaList?

This actor collects only publicly available startup listing data. You are responsible for how you use it: follow BetaList's terms and the laws that apply to you, and get legal advice if you plan commercial redistribution. Contact details pulled from a startup's own website are the kind of information that site already published publicly, but you should still handle emails and phone numbers responsibly.

#### Can I get only new or changed startups on a schedule?

Yes. Schedule the actor from the Schedules tab and turn on Incremental mode. Later runs then classify each startup as new, updated, unchanged, reappeared or gone against the previous run for that same search, and unchanged ones are not billed unless you ask for them.

#### What is the difference between Resume and Incremental mode?

Resume continues one specific interrupted run using a pasted run or dataset ID, useful right after a run stopped partway through. Incremental mode is for running the same search again and again, for example daily, and remembers state itself so you only get what changed. They solve different problems and are not meant to be combined on the same tracked search.

#### Why did my run return fewer startups than expected, or none at all?

BetaList itself shows only the first 60 startups for a keyword search and a single page of recent startups on the latest feed, so those sources cannot return more. If BetaList pages load but the startup list on them cannot be read, the run fails with a message saying so instead of finishing empty. A boosted-only or feature-date filter can legitimately drop every card fetched, which gives a normal successful run with an empty dataset. A single source (a keyword, topic, region or feed) whose requests are all refused is skipped with a warning while other sources continue. If no BetaList page could be loaded at all, the run fails with a connection message (see Plan Requirement) rather than finishing empty. A pasted single startup URL whose page cannot be read (removed or blocked) is skipped with a warning and is not saved or charged. Check the run log for warnings, then try again or choose a residential connection group.

#### Can I use it with AI agents or MCP?

Yes. Call it from any Apify integration or MCP client, and use the connector field to push results into Notion, Linear or Airtable.

### 🔗 Want more leads data?

Pair this actor with these related scrapers from the same team:

<table>
<tr><td>🛒 <a href="https://apify.com/abotapi/dealroom-co-scraper"><b>Dealroom.net Scraper</b></a><br>Scrape Dealroom.net market maps, company lookup results, live signals, and newly founded...</td><td>📇 <a href="https://apify.com/abotapi/ycombinator-com-scraper"><b>Ycombinator</b></a><br>Pull every ycombinator.com company across every batch, with founders, social URLs...</td></tr>
<tr><td>📇 <a href="https://apify.com/abotapi/product-hunt-launches-scraper"><b>Product Hunt</b></a><br>Extract producthunt.com data including products, launches, keyword search results, maker...</td><td>💼 <a href="https://apify.com/abotapi/f6s-directory-scraper"><b>F6S Scraper</b></a><br>Extract structured data from F6S.com. Scrape funding programs, startup accelerators...</td></tr>
<tr><td>💼 <a href="https://apify.com/abotapi/wellfound-jobs-scraper"><b>Wellfound Jobs Scraper</b></a><br>Scrape startup jobs from Wellfound.com by keyword, location, role, or remote status...</td><td>📇 <a href="https://apify.com/abotapi/thomasnet-supplier-scraper"><b>ThomasNet Scraper</b></a><br>Scrape ThomasNet suppliers and manufacturers by keyword, category, location...</td></tr>
</table>

👉 [Browse all abotapi scrapers](https://apify.com/abotapi)

### 💬 Support

- 🐞 **Found a bug or a missing field?** Open a ticket on the [Issues tab](https://apify.com/abotapi/betalist-com-scraper/issues/open). We usually reply within hours.
- 🛠️ **Need another site, extra fields or a private build?** Email <contact@abotapi.com>.
- ⭐ **Enjoying it?** A quick review on the actor page helps other users find it.

# Actor input Schema

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

Choose how to source startups. 'search' discovers startups from keywords, topics, regions or the latest feed. 'url' collects the exact BetaList pages you paste.

## `queries` (type: `array`):

Keywords to search on BetaList (e.g. 'fintech', 'ai agents'). Each keyword becomes a search feed. Only applies when mode = search.

## `topic` (type: `string`):

A BetaList topic to collect. Accepts a topic name (e.g. 'Artificial Intelligence'), slug (e.g. 'artificial-intelligence') or full browse path (e.g. 'ai/artificial-intelligence'). A topic that BetaList does not serve fails the run with a clear error. Only applies when mode = search.

## `region` (type: `string`):

A BetaList region name or slug to collect (e.g. 'california', 'California' or 'united-states'). A region that BetaList does not serve fails the run with a clear error. Only applies when mode = search.

## `boostedOnly` (type: `boolean`):

Keep only startups flagged as Boosted on BetaList. Applies in both modes.

## `featuredAfter` (type: `string`):

Keep only startups featured on or after this date (YYYY-MM-DD). Requires 'Fetch detail pages' since the feature date lives on the profile page.

## `featuredBefore` (type: `string`):

Keep only startups featured on or before this date (YYYY-MM-DD). Requires 'Fetch detail pages'.

## `urls` (type: `array`):

One or more BetaList URLs to collect. Supported: the latest feed, search result pages, topic/browse pages, region pages, and individual startup pages. Filter fields are ignored for startup URLs; results continue from the page number in the pasted URL. Topic pages (/topics/...) are resolved to their canonical /browse/... page before ?page=N is applied. Index pages (/browse, /browse/<category>, /regions) list no startups and are skipped with a warning. Only applies when mode = url.

## `fetchDetails` (type: `boolean`):

Open each startup profile page to enrich records with the full description, topics, regions, feature date, screenshots and the resolved website. Adds an extra request per startup.

## `getContacts` (type: `boolean`):

Visit each startup's own website to collect public contact signals: emails, phone numbers and social profile links (LinkedIn, X, Facebook, Instagram, YouTube and more). Adds extra requests per startup.

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

Maximum number of startups to save across the whole run. Set 0 for unlimited (stops at Max pages). This is the main limit.

## `maxPages` (type: `integer`):

Safety ceiling on how many pages to walk per search/URL source. 0 (default) uses the built-in limit of 200 pages per source; any positive value is used as given. The run normally stops at Max startups first.

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

Connection settings. The default Apify Proxy was measured serving BetaList pages, so no paid tier is needed for normal runs. Apify free plans do not include Apify Proxy: there the run continues on a backup connection and logs a warning. Residential or your own proxy URLs are optional and billed to you.

## `resumeFromRunId` (type: `string`):

Paste a previous run ID or dataset ID to continue a large collection without returning or charging for startups already collected there. Use this after an interrupted run. For recurring monitoring of the same search, use Incremental mode below instead.

## `incrementalMode` (type: `boolean`):

Turn this on for daily or recurring monitoring. The first run returns all matching startups as NEW. Later runs normally return only NEW, UPDATED, and REAPPEARED startups. Turn on "Emit unchanged" or "Emit expired" only when you also want those startups returned (and billed). State is kept separately per mode/keywords/topic/region/URLs/filters/detail-fetch setup; use State key when you want to name or deliberately share a monitoring campaign. To continue one specific interrupted run instead, use Resume from a previous run above.

## `stateKey` (type: `string`):

Name this monitoring campaign explicitly so Incremental mode's saved state survives input tweaks that don't change what you're tracking, or so two different setups deliberately share one baseline. Leave blank to auto-derive the key from mode, keywords/topic/region/URLs, filters and Fetch detail pages.

## `emitUnchanged` (type: `boolean`):

Incremental mode only. When off (default), a startup identical to the last run is suppressed — not returned and not billed. Turn on to return (and pay for) every unchanged startup too, e.g. to keep a full mirror of the search.

## `emitExpired` (type: `boolean`):

Incremental mode only. When on, a startup previously tracked but no longer found is returned once with changeType EXPIRED (and billed like any other row). Only fires after a complete, uncapped scan with no Resume in effect — a partial run never marks live startups expired.

## `ignoreFieldsForChanges` (type: `array`):

Incremental mode only. Field names (use a dot for nested fields, e.g. source.scraped_time) that must NOT count as a change. Ignored fields stay in the output; they just no longer turn a startup into UPDATED or appear in changedFields. These are always ignored: the scrape time, the randomized similar_startups list and the alias fields product_name, long_description, website and featured_date (copies of name, description, website_url and featured_at, which are tracked).

## `mcpConnectors` (type: `array`):

Optionally send results into the apps you already use, via Model Context Protocol (MCP) connectors. Authorize one under Apify then Settings then API & Integrations, then select it here. Notion gets a rich page-per-item export; other connectors get a best-effort write. Leave empty to skip; never changes the dataset output. Supported: Notion (https://mcp.notion.com/mcp), Linear (https://mcp.linear.app/sse), Airtable (https://mcp.airtable.com/mcp), Apify (https://mcp.apify.com).

## `notionParentPageUrl` (type: `string`):

URL (or id) of the Notion page under which item pages are created. Required to enable the Notion export; ignored by other connectors.

## `maxNotifyListings` (type: `integer`):

Cap on items written to each connector per run. Does not affect the dataset.

## Actor input object example

```json
{
  "mode": "search",
  "queries": [
    "ai"
  ],
  "boostedOnly": false,
  "urls": [
    "https://betalist.com/",
    "https://betalist.com/?page=2",
    "https://betalist.com/search?q=ai",
    "https://betalist.com/search?q=ai&page=2",
    "https://betalist.com/search?q=ai&sort=newest",
    "https://betalist.com/topics/artificial-intelligence",
    "https://betalist.com/browse/ai/artificial-intelligence",
    "https://betalist.com/browse/ai/artificial-intelligence?page=2",
    "https://betalist.com/regions/california",
    "https://betalist.com/regions/california?page=2",
    "https://betalist.com/regions/united-states",
    "https://betalist.com/startups/agent-ui",
    "https://www.betalist.com/startups/agent-ui",
    "http://betalist.com/startups/agent-ui"
  ],
  "fetchDetails": true,
  "getContacts": false,
  "maxItems": 20,
  "maxPages": 0,
  "proxy": {
    "useApifyProxy": true
  },
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "ignoreFieldsForChanges": [],
  "maxNotifyListings": 50
}
```

# Actor output Schema

## `overview` (type: `string`):

No description

## `detail` (type: `string`):

No description

## `contacts` (type: `string`):

No description

## `changes` (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 = {
    "mode": "search",
    "queries": [
        "ai"
    ],
    "urls": [
        "https://betalist.com/",
        "https://betalist.com/?page=2",
        "https://betalist.com/search?q=ai",
        "https://betalist.com/search?q=ai&page=2",
        "https://betalist.com/search?q=ai&sort=newest",
        "https://betalist.com/topics/artificial-intelligence",
        "https://betalist.com/browse/ai/artificial-intelligence",
        "https://betalist.com/browse/ai/artificial-intelligence?page=2",
        "https://betalist.com/regions/california",
        "https://betalist.com/regions/california?page=2",
        "https://betalist.com/regions/united-states",
        "https://betalist.com/startups/agent-ui",
        "https://www.betalist.com/startups/agent-ui",
        "http://betalist.com/startups/agent-ui"
    ],
    "proxy": {
        "useApifyProxy": true
    },
    "incrementalMode": false,
    "ignoreFieldsForChanges": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("abotapi/betalist-com-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 = {
    "mode": "search",
    "queries": ["ai"],
    "urls": [
        "https://betalist.com/",
        "https://betalist.com/?page=2",
        "https://betalist.com/search?q=ai",
        "https://betalist.com/search?q=ai&page=2",
        "https://betalist.com/search?q=ai&sort=newest",
        "https://betalist.com/topics/artificial-intelligence",
        "https://betalist.com/browse/ai/artificial-intelligence",
        "https://betalist.com/browse/ai/artificial-intelligence?page=2",
        "https://betalist.com/regions/california",
        "https://betalist.com/regions/california?page=2",
        "https://betalist.com/regions/united-states",
        "https://betalist.com/startups/agent-ui",
        "https://www.betalist.com/startups/agent-ui",
        "http://betalist.com/startups/agent-ui",
    ],
    "proxy": { "useApifyProxy": True },
    "incrementalMode": False,
    "ignoreFieldsForChanges": [],
}

# Run the Actor and wait for it to finish
run = client.actor("abotapi/betalist-com-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 '{
  "mode": "search",
  "queries": [
    "ai"
  ],
  "urls": [
    "https://betalist.com/",
    "https://betalist.com/?page=2",
    "https://betalist.com/search?q=ai",
    "https://betalist.com/search?q=ai&page=2",
    "https://betalist.com/search?q=ai&sort=newest",
    "https://betalist.com/topics/artificial-intelligence",
    "https://betalist.com/browse/ai/artificial-intelligence",
    "https://betalist.com/browse/ai/artificial-intelligence?page=2",
    "https://betalist.com/regions/california",
    "https://betalist.com/regions/california?page=2",
    "https://betalist.com/regions/united-states",
    "https://betalist.com/startups/agent-ui",
    "https://www.betalist.com/startups/agent-ui",
    "http://betalist.com/startups/agent-ui"
  ],
  "proxy": {
    "useApifyProxy": true
  },
  "incrementalMode": false,
  "ignoreFieldsForChanges": []
}' |
apify call abotapi/betalist-com-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,abotapi/betalist-com-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/dzdzefSEftXLhkLyC/builds/8dZlax1uulWNDbi32/openapi.json
