# Wellfound Startup Job Scraper (`corvuslab/wellfound-scraper`) Actor

Scrape startup jobs from Wellfound (AngelList Talent) with full job descriptions, salary and equity bands, company size, funding stage and YC/top-investor signals — plus incremental change tracking and notifications. No account needed.

- **URL**: https://apify.com/corvuslab/wellfound-scraper.md
- **Developed by:** [Corvuslab](https://apify.com/corvuslab) (community)
- **Categories:** Jobs, Lead generation, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.99 / 1,000 results

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/platform/actors/running/actors-in-store#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

## Wellfound Startup Job Scraper 🚀

Extract structured startup job data from **[Wellfound](https://wellfound.com)**
(formerly AngelList Talent) — with **full job descriptions**, salary **and equity**
bands, company size, funding stage, and Y Combinator / top-investor signals.

**No Wellfound account, cookie or session token required.** Every job comes back with
its complete description included — not truncated, not empty, at no extra per-job fee.

Great for job-market research, startup lead generation, compensation and equity
benchmarking, recruiting intelligence, and feeding live startup jobs into dashboards or
AI agents.

### ✨ Key features

- 📄 **Full descriptions on every job, for free** — every listing arrives with its
  complete description, in text, markdown and HTML. No enrichment toggle, no second
  per-job fee, no empty `description` field.
- 💰 **Salary *and* equity, parsed** — `"$135k – $175k • 0.05% – 0.25%"` becomes
  `salaryMin`, `salaryMax`, `salaryCurrency`, `equityMin`, `equityMax`, plus a
  `salaryHidden` flag. Filter on any of them.
- 🏢 **Startup firmographics** — company size band, funding stage (early / growth /
  scale), $1B+ / $500M valuation flags, tagline, logo and profile URL.
- 🟠 **Y Combinator & top-investor flags** — `isYCombinator`, `hasTopInvestors`,
  `isGrowingFast`, `isActivelyHiring`, `isQuickResponder` as first-class booleans you
  can filter on.
- 🎯 **Role × location searches** — combine any number of roles and locations; every
  pair runs as its own search and results are deduplicated by job ID.
- 🔍 **Filters that actually apply to descriptions** — because the full text is already
  in hand, keyword, salary, equity and experience filters are exact, and filtered-out
  jobs never reach your bill.
- ♻️ **Incremental mode** — recurring runs emit only what changed. Unchanged jobs are
  recognised before any enrichment, so you skip the work *and* the per-result fee.
- 🔁 **Repost & change tracking** — every record carries `changeType`
  (`NEW` / `UPDATED` / `UNCHANGED` / `REAPPEARED` / `EXPIRED`), plus repost detection.
- 🏢 **Lead-generation mode** — `companyOnlyMode` emits one record per company with
  `jobCount` and sample titles, ideal for building a startup prospect list.
- 🔔 **Notifications** — push matching jobs to Telegram, Slack, Discord, WhatsApp or any
  webhook (n8n, Make, Zapier).
- 🪶 **AI-friendly output** — `compact` mode, description truncation, single-format
  descriptions and `excludeEmptyFields` for lean records in LLM and MCP pipelines.

### 📥 Input

Everything is optional — run it with no input at all and it scrapes software engineering
jobs.

| Field | Type | Description |
|---|---|---|
| `query` | string | Keyword, e.g. `"rust engineer"`. Picks the closest role pages **and** is enforced against every title and description. |
| `roles` | array | Wellfound role pages: `software-engineer`, `product-manager`, `data-scientist`, … Each runs as its own search. |
| `location` | array | `san-francisco`, `new-york`, `remote`, `united-states`, … Paired with every role. |
| `remote` | boolean | Search the remote-only variant of each role page. |
| `searchUrls` | array | Paste any Wellfound search URL to reach slugs not in the dropdowns. |
| `startUrls` | array | Alias of `searchUrls`; both are merged and deduplicated. |
| `maxResults` | integer | Total jobs to output. `0` = no limit (capped at 5,000). Default 100. |
| `maxPages` | integer | Depth bound per search. Each page returns ~25–56 jobs. Default 25. |
| `includeKeywords` / `excludeKeywords` | array | Keep or drop jobs matching these terms (title, description, role, company). |
| `jobType` | enum | `full-time`, `part-time`, `contract`, `internship`, `cofounder`. |
| `experienceLevel` | enum | `entry` (0–2 yrs), `mid` (2–5), `senior` (5–8), `staff` (8+). |
| `salaryMin` / `salaryMax` | integer | Salary band bounds in the listed currency. `0` = no bound. |
| `equityMin` | number | Minimum maximum-equity grant, in percent. |
| `remoteOnly` | boolean | Drop anything not flagged remote. |
| `excludeLocations` | array | Drop jobs whose locations mention these, e.g. `India`. |
| `companySize` | array | Headcount bands, e.g. `SIZE_11_50`, `SIZE_51_200`. |
| `yCombinatorOnly` | boolean | Only companies carrying Wellfound's YC badge. |
| `fromDate` / `toDate` | string | Posting-date window (`YYYY-MM-DD`, `toDate` inclusive). |
| `maxAgeMinutes` | integer | Only jobs posted within N minutes — pairs with a schedule for a live feed. |
| `customFilters` | array | Rules over any output field, e.g. `[{"field":"companyStage","operator":"equals","value":"early_stage"}]`. |
| `enrichDetail` | boolean | Adds company website, industry, employment type and map coordinates. **Not needed for descriptions.** Costs one extra request per job, so large runs take considerably longer — budget roughly `maxResults × requestDelayMs`. |
| `includeDetails` | boolean | Alias of `enrichDetail`. |
| `extractContacts` | boolean | Best-effort `extractedEmails` / `extractedPhones` from descriptions. |
| `companyOnlyMode` | boolean | One record per company instead of per job. |
| `descriptionFormat` | enum | `all`, `text`, `markdown`, `html`. |
| `descriptionMaxLength` | integer | Truncate descriptions (`0` = full text). |
| `compact` | boolean | Core fields only, for AI agents and MCP. |
| `excludeEmptyFields` | boolean | Drop null and empty fields. |
| `incrementalMode` | boolean | Emit only new / changed jobs on recurring runs. |
| `stateKey` | string | Names the tracking state; auto-derived if blank. |
| `emitUnchanged` / `emitExpired` | boolean | Also emit `UNCHANGED` / `EXPIRED` records. |
| `skipReposts` | boolean | Drop jobs re-listed under a new ID. |
| `telegramToken` + `telegramChatId` | string | Notify Telegram. |
| `slackWebhookUrl` / `discordWebhookUrl` | string | Notify a Slack / Discord webhook. |
| `whatsappPhoneNumberId` + `whatsappAccessToken` + `whatsappTo` | string | Notify WhatsApp via Meta Cloud API. |
| `webhookUrl` + `webhookHeaders` | string / object | POST structured JSON anywhere. |
| `notificationLimit` | integer | Jobs listed per message (1–20, default 5). |
| `notifyOnlyChanges` | boolean | With incremental mode, alert only on changes. Default `true`. |
| `requestDelayMs` | integer | Delay between requests — this is what sets the request rate. Default 600 ms; raise it if you ever see blocks, lower it to trade safety margin for speed. |
| `pageConcurrency` | integer | Search pages fetched at once (1–20, default 4). Overlaps the waiting between pages; `requestDelayMs` still caps the rate. |
| `proxyConfiguration` | object | Leave as-is unless you have a specific reason to change it. |

#### Example input

```json
{
    "roles": ["software-engineer"],
    "location": ["san-francisco", "new-york"],
    "maxResults": 200,
    "salaryMin": 150000,
    "equityMin": 0.1,
    "excludeKeywords": ["intern"],
    "yCombinatorOnly": true
}
```

### 📤 Output

Each job is pushed to the run's default dataset. Example record (abridged):

```json
{
    "id": "3317746",
    "title": "Software Engineer",
    "url": "https://wellfound.com/jobs/3317746-software-engineer",
    "compensation": "$135k – $175k • 0.05% – 0.25%",
    "salaryMin": 135000,
    "salaryMax": 175000,
    "salaryCurrency": "USD",
    "salaryHidden": false,
    "equityMin": 0.05,
    "equityMax": 0.25,
    "jobType": "full-time",
    "postedAt": "2026-07-26T09:38:57Z",
    "locationNames": ["San Francisco"],
    "acceptedRemoteLocationNames": ["United States"],
    "remote": true,
    "remoteKind": "ONSITE_OR_REMOTE",
    "wfhFlexible": false,
    "yearsExperienceMin": 3,
    "primaryRoleTitle": "Software Engineer",
    "atsSource": "AtsIntegration::Greenhouse::Listing",
    "autoPosted": false,
    "description": "Mission\n\nKeeper is an AI-powered human-in-the-loop service…",
    "descriptionMarkdown": "### Mission\n\nKeeper is an AI-powered…",
    "descriptionHtml": "<h3>Mission</h3><p>Keeper is an AI-powered…</p>",
    "companyId": "6809417",
    "companyName": "Keeper",
    "companySlug": "keeper-tax",
    "companyUrl": "https://wellfound.com/company/keeper-tax",
    "companyLogo": "https://photos.wellfound.com/startups/i/6809417-….jpg",
    "companyTagline": "File your complex taxes confidently",
    "companySize": "SIZE_11_50",
    "companySizeLabel": "11-50",
    "companyStage": "early_stage",
    "companyValuation": null,
    "companyBadges": ["Actively Hiring", "B2C", "Early Stage", "Top Investors", "Y Combinator"],
    "isYCombinator": true,
    "hasTopInvestors": true,
    "isGrowingFast": false,
    "isActivelyHiring": true,
    "matchedSearch": "role=software-engineer",
    "source": "wellfound",
    "scrapedAt": "2026-07-29T09:15:22Z"
}
```

With `enrichDetail` on, each record also gains `companyWebsite`, `companyIndustry`,
`employmentType`, `experienceRequirements`, `directApply`, `latitude`, `longitude` and
the postal address fields.

With `incrementalMode` on, each record gains `changeType`, `trackedHash`, `stateKey`,
`firstSeenAt`, `lastSeenAt`, `isRepost` and `repostOfId`.

#### 🔄 Switching from another Wellfound scraper?

Records also carry `portalUrl`, `detailUrl` and `salaryEquityMin` — aliases of `url`,
`url` and `equityMin` — and the input accepts `startUrls` and `includeDetails`. Point
your existing pipeline at this actor and it should run unchanged, with the descriptions
and company fields filled in rather than `null`.

### ♻️ Recurring monitoring

Wellfound expires inactive postings aggressively, so most listings are days old at most
— which makes it a good target for scheduled runs.

Turn on `incrementalMode`, give it a `stateKey`, and schedule the actor. The first run
stores a baseline; every run after that emits only jobs that are **new, updated or
reappeared**.

Measured back-to-back on a 117-job search: the first run billed **117 results**, the
second billed **0** — and cost 43 % less to run, because unchanged jobs are recognised
before any enrichment. Over a day, what you pay for is the genuine churn (typically
5–15 % of listings) instead of the whole result set again.

> ⚠️ **Set `maxResults` high enough to cover your entire search.** Wellfound reshuffles
> its listing pages between requests, so a run that stops at 300 of 5,000 jobs sees a
> different slice every time and everything looks new. Incremental mode only pays off
> when each run covers the same, complete result set.

Pair it with `maxAgeMinutes: 60` on an hourly schedule for a near-real-time feed, and
with notifications to get the jobs pushed straight to Slack or Telegram.

### 💡 Use cases

- 🔎 **Job hunting** — track new startup roles matching your stack, salary floor and
  equity expectations, delivered to Telegram the moment they appear.
- 🏢 **Lead generation** — `companyOnlyMode` turns a search into a list of startups that
  are actively hiring, with size, stage and investor quality attached.
- 💰 **Compensation research** — parsed salary *and* equity bands across roles,
  locations and funding stages.
- 🧠 **Recruiting intelligence** — see which companies are hiring, how fast they're
  growing, and which ATS they use (`atsSource`).
- 📊 **Market analysis** — role demand, remote share and pay trends over time.
- 🤖 **AI agents & MCP** — `compact` output with truncated descriptions keeps LLM
  context small and cheap.

### 💰 How much does it cost to scrape Wellfound?

**Pay-per-event — two events, nothing else.** A small fee when the run starts, then a fee
per job record you actually receive.

| Event | Price |
|---|---|
| Actor start | **$0.005** per run |
| Job record | **$2.99 per 1,000** results ($0.00299 each) |

| Run | Cost |
|---|---|
| 10 results | $0.035 |
| 25 results | $0.080 |
| 100 results | $0.304 |
| 200 results | $0.603 |
| 500 results | $1.500 |
| 1,000 results | $2.995 |

There is **no separate compute or proxy bill** — platform usage is on us, so the numbers
above are the whole cost of a run.

$0.00299 buys the **complete record**, including the full job description in text,
markdown and HTML. There is no enrichment surcharge and no per-description fee:
`enrichDetail` adds company website, industry and coordinates at the same per-result
price. You only pay for jobs you receive — every filter (keywords, salary, equity,
experience, location, `customFilters`) discards non-matching jobs *before* they are
billed, and Apify's per-run cost limit is honoured — the run stops cleanly at your
ceiling rather than overshooting it.

### 📉 Example: recurring monitoring savings

Monitoring a search of **250 jobs, once a day for 30 days**. Without incremental mode
every run re-emits all 250 records:

**30 × ($0.005 + 250 × $0.00299) = $22.58 / month**

With `incrementalMode` on, the first run stores a baseline and every later run emits —
and charges for — only jobs that are new, updated or reappeared. What you pay for is the
genuine churn:

| Daily churn | Records billed per run | Cost per run | Month (1 baseline + 29 runs) | Saving |
|---|---|---|---|---|
| *No incremental mode* | 250 | $0.75 | $22.58 | — |
| 5% — stable niche | ~13 | $0.04 | **$1.98** | **91%** |
| 15% — moderate | ~38 | $0.12 | **$4.15** | **82%** |
| 30% — fast-moving | ~75 | $0.23 | **$7.40** | **67%** |

Most role × location searches sit in the 5–15% band, so a daily watch on a 250-job
search typically lands between **$2 and $4 a month** instead of $22.58.

This is measured, not modelled: on a back-to-back test of a 117-job search the first run
billed **117 results** and the second billed **0**, because unchanged jobs are recognised
before any enrichment happens. A run that finds nothing new costs the **$0.005 start fee
and nothing else**.

> ⚠️ Keep `maxResults` high enough to cover the whole search and identical between runs —
> see [Recurring monitoring](#-recurring-monitoring) above for why.

### ❓ FAQ

**Do I need a Wellfound account?**
No. Everything scraped is publicly visible job data.

**Are job descriptions really included by default?**
Yes — and this is the main difference from the alternatives. Every job comes back with its
complete description at no extra request and no extra fee. You do not need `enrichDetail`
to get it, and it is never truncated unless you ask for that with `descriptionMaxLength`.

**Why do some jobs have no salary?**
Because Wellfound has none to show. Those records carry `salaryHidden: true` and null
salary bounds. Setting `salaryMin` drops them, since they cannot be shown to clear it.

**How many results can I get?**
Up to 5,000 per run. Individual searches vary — a broad role page can hold thousands of
jobs, a narrow role × city pair a few hundred. The log prints each search's true total
before it starts walking.

**Can I search for any keyword?**
Yes, via `query`. Note that Wellfound exposes no public keyword search, so the keyword
selects the closest role pages and is then enforced against every title and description.
For an exact taxonomy match prefer `roles`; to reach an unusual page, paste its URL into
`searchUrls`.

**Can I run this on a schedule?**
Yes. Combine Apify's scheduler with `incrementalMode` so each run costs only the changes.

**Can I use this scraper through an MCP server?**
Yes. Apify's [Actors MCP Server](https://apify.com/apify/actors-mcp-server) exposes this
actor as a tool that Claude, ChatGPT, Cursor, VS Code and any other
[MCP client](https://modelcontextprotocol.io/clients) can call directly — no wrapper code
to write.

- **Endpoint** — point your client at the [hosted Apify MCP server](https://mcp.apify.com),
  then select this actor with the `tools` parameter or in the
  [visual configurator](https://mcp.apify.com/configure).
- **Auth** — sign in with OAuth, or send your Apify token as a bearer token in the
  Authorization header.
- **Per-client setup** — see the
  [Apify MCP documentation](https://docs.apify.com/platform/integrations/mcp).

This actor is built for that pipeline: turn on `compact`, set a `descriptionMaxLength`,
pick a single `descriptionFormat` and add `excludeEmptyFields`, and each job comes back
as a lean record that will not blow up a context window.

**Does it work with the Apify API and integrations?**
Yes — REST API, JavaScript and Python clients, plus Zapier, Make, Google Sheets, Slack
and the Apify MCP server for AI assistants.

**Is scraping Wellfound legal?**
This actor collects only publicly available data. Scraping public information is
generally lawful, but you remain responsible for how you use it — review Wellfound's
terms and any applicable law (GDPR and similar) before you do.

# Actor input Schema

## `query` (type: `string`):

Free-text keyword, e.g. "rust engineer", "ML ops", "solidity". Wellfound has no public keyword search, so the keyword is used two ways: it picks the closest role pages to walk, and it is enforced as a required term against every job title and description. For an exact taxonomy match, use **Roles** instead.

## `roles` (type: `array`):

Wellfound role pages to walk. Each role runs as its own search. Combine with **Locations** to get every role × location pair.

## `location` (type: `array`):

Locations to search. Used on its own, or paired with each role. Choose `remote` to search remote-only listings.

## `remote` (type: `boolean`):

Search the remote-only variant of each role page. Ignored when **Locations** already contains specific cities.

## `searchUrls` (type: `array`):

Paste Wellfound search URLs, e.g. `https://wellfound.com/role/l/software-engineer/new-york`. Use this to reach any role or location slug not offered in the dropdowns above.

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

Alias of **Wellfound URLs** above, accepted so input written for other Wellfound scrapers runs here unchanged. Both are merged and deduplicated — use whichever you already have.

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

Maximum job records to output across all searches. `0` means no limit (capped at 5,000).

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

Safety bound on how deep each individual search is walked. Each page returns roughly 25–56 jobs.

## `includeKeywords` (type: `array`):

Keep only jobs mentioning at least one of these terms. Matched against title, description, role and company name.

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

Drop jobs mentioning any of these terms.

## `jobType` (type: `string`):

Employment type.

## `experienceLevel` (type: `string`):

Filter on the years of experience Wellfound publishes. Jobs that don't state a requirement are kept.

## `salaryMin` (type: `integer`):

Drop jobs whose published salary maximum is below this. Jobs with no published band are dropped when this is set. `0` = no minimum.

## `salaryMax` (type: `integer`):

Drop jobs whose published salary minimum is above this. `0` = no maximum.

## `equityMin` (type: `number`):

Drop jobs whose maximum equity grant is below this percentage. `0` = no minimum.

## `remoteOnly` (type: `boolean`):

Drop anything not flagged remote, whichever pages were searched.

## `excludeLocations` (type: `array`):

Drop jobs whose locations mention any of these, e.g. `India` also drops `Bangalore, India`.

## `companySize` (type: `array`):

Keep only companies in these headcount bands.

## `yCombinatorOnly` (type: `boolean`):

Keep only jobs at companies carrying Wellfound's YC badge.

## `fromDate` (type: `string`):

Keep jobs posted on or after this date (`YYYY-MM-DD`).

## `toDate` (type: `string`):

Keep jobs posted on or before this date (`YYYY-MM-DD`, inclusive).

## `maxAgeMinutes` (type: `integer`):

Keep only jobs posted within this many minutes before the run. Pairs well with a schedule — `60` on an hourly run gives you a live feed. `0` = no limit.

## `customFilters` (type: `array`):

Rules over any output field, e.g. `[{"field":"companyStage","operator":"equals","value":"early_stage"}]`. Operators: `includes`, `notIncludes`, `equals`, `notEquals`, `gt`, `gte`, `lt`, `lte`, `exists`, `notExists`.

## `enrichDetail` (type: `boolean`):

Adds company website, industry, normalised employment type and map coordinates by fetching each job's page. **Descriptions do not need this** — they already come with the search results. Costs one extra request per job.

## `includeDetails` (type: `boolean`):

Alias of **Fetch job detail pages** above, accepted for compatibility with input written for other Wellfound scrapers. Turning on either one enables enrichment.

## `pageConcurrency` (type: `integer`):

Search pages fetched at the same time. This overlaps the waiting between pages; the request delay still caps how fast requests go out. If you see blocks, raise the request delay rather than lowering this.

## `detailConcurrency` (type: `integer`):

Parallel detail requests when enrichment is on. Note that the request delay caps the overall rate, so values above about 2 make little difference — with enrichment on, run time is roughly (number of jobs) × (request delay).

## `extractContacts` (type: `boolean`):

Best-effort scan of each description for contact details, emitted as `extractedEmails` and `extractedPhones`.

## `companyOnlyMode` (type: `boolean`):

Lead-generation mode: emit a single record per company (with `jobCount` and sample titles) instead of one per job.

## `descriptionFormat` (type: `string`):

Which description variants to keep. Dropping unused ones makes records much smaller.

## `descriptionMaxLength` (type: `integer`):

Cut descriptions to this many characters. Useful for controlling LLM prompt cost. `0` = keep full text.

## `compact` (type: `boolean`):

Emit only the core fields (id, title, company, URL, location, remote, compensation, posted date). Ideal for AI agents and MCP.

## `excludeEmptyFields` (type: `boolean`):

Omit null and empty fields from every record.

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

On repeat runs, emit only jobs that are new or changed. Unchanged jobs are recognised before any enrichment, so you skip both the work and the per-result fee. **Set `maxResults` high enough to cover your whole search** — a run that stops early sees a different slice each time and saves nothing.

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

Names the tracking state, e.g. `us-remote-engineers`. Leave blank to derive one from your search settings. Use a fresh key when you change what you're tracking.

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

Include `UNCHANGED` records too. Turns incremental mode into a full snapshot that is still change-labelled.

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

Emit an `EXPIRED` record for jobs tracked last run that have since disappeared.

## `skipReposts` (type: `boolean`):

Drop jobs that look like a re-listing of something already seen under a different ID.

## `telegramToken` (type: `string`):

Bot token from @BotFather.

## `telegramChatId` (type: `string`):

Target chat or channel ID (channels start with `-100`).

## `slackWebhookUrl` (type: `string`):

Slack incoming-webhook URL.

## `discordWebhookUrl` (type: `string`):

Discord channel webhook URL.

## `whatsappPhoneNumberId` (type: `string`):

Numeric phone-number ID from Meta Business Manager (not the phone number itself).

## `whatsappAccessToken` (type: `string`):

Meta Cloud API token with the `whatsapp_business_messaging` scope.

## `whatsappTo` (type: `string`):

Recipient in E.164 format, e.g. `+4512345678`. They must have messaged your business number within the last 24 hours.

## `webhookUrl` (type: `string`):

Any HTTPS endpoint (n8n, Make, Zapier). Receives a JSON body with run metadata and the matching jobs.

## `webhookHeaders` (type: `object`):

Extra request headers, e.g. `{"Authorization": "Bearer …"}`.

## `notificationLimit` (type: `integer`):

How many jobs to list in the notification.

## `notifyOnlyChanges` (type: `boolean`):

With incremental mode on, notify only about new, updated or reappeared jobs.

## `includeRunMetadata` (type: `boolean`):

Prepend a one-line summary to each notification.

## `requestDelayMs` (type: `integer`):

Milliseconds between page requests. Because pages are fetched in parallel, this is what sets the request rate — lowering it speeds runs up but raises the chance Wellfound throttles you, and raising it is the fix if that happens.

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

Leave this alone unless you have a reason. The default uses Apify's datacenter proxies, which are the cheapest that reliably clear Wellfound. **If they ever start getting blocked, the run switches itself to residential automatically** — you do not need to do anything. To force residential from the start, pick it here.

## Actor input object example

```json
{
  "query": "software engineer",
  "remote": false,
  "maxResults": 100,
  "maxPages": 25,
  "jobType": "",
  "experienceLevel": "",
  "salaryMin": 0,
  "salaryMax": 0,
  "equityMin": 0,
  "remoteOnly": false,
  "yCombinatorOnly": false,
  "maxAgeMinutes": 0,
  "enrichDetail": false,
  "includeDetails": false,
  "pageConcurrency": 4,
  "detailConcurrency": 5,
  "extractContacts": false,
  "companyOnlyMode": false,
  "descriptionFormat": "all",
  "descriptionMaxLength": 0,
  "compact": false,
  "excludeEmptyFields": false,
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "skipReposts": false,
  "notificationLimit": 5,
  "notifyOnlyChanges": true,
  "includeRunMetadata": true,
  "requestDelayMs": 600,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `jobs` (type: `string`):

Every scraped job, newest search page first.

## `companies` (type: `string`):

Company-level view — most useful with 'One record per company' enabled.

## `allFields` (type: `string`):

The complete records, every field included.

# 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 = {
    "query": "software engineer"
};

// Run the Actor and wait for it to finish
const run = await client.actor("corvuslab/wellfound-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 = { "query": "software engineer" }

# Run the Actor and wait for it to finish
run = client.actor("corvuslab/wellfound-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "query": "software engineer"
}' |
apify call corvuslab/wellfound-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=corvuslab/wellfound-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/acts/kVAljn0d7KKZQkHdY/builds/mvSt01nECyoO4RIkB/openapi.json
