# Wellfound Jobs Scraper (AngelList) — Salary & Equity Data (`spool/angellist-jobs-scraper`) Actor

Scrape Wellfound (AngelList) startup jobs with salary and equity as sortable numbers, plus company size, YC badge and the ATS each company uses.

- **URL**: https://apify.com/spool/angellist-jobs-scraper.md
- **Developed by:** [Spool](https://apify.com/spool) (community)
- **Categories:** Jobs, Lead generation, Developer tools
- **Stats:** 1 total users, 0 monthly users, 91.9% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.00 / 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/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

## Wellfound Jobs Scraper (AngelList) — Salary & Equity Data

**Get startup jobs from Wellfound — formerly AngelList Talent — with pay ranges
you can actually sort, filter and average.**

Wellfound shows compensation as text: `"$130k – $210k • 0.05% – 0.2%"`.
Every other scraper hands you that string and leaves the parsing to you.
This one hands you five fields:

```json
"salaryMin": 130000, "salaryMax": 210000,
"equityMin": 0.05,   "equityMax": 0.2,  "hasEquity": true
```

No regex. No cleanup. Sort by pay the moment the run finishes.

***

### Three things you won't get elsewhere

**1. Compensation as numbers — on ~92% of listings.**
Sort, filter, compute medians, build a benchmark. Equity too, which matters
because at startups it's a real part of the offer.

**2. The hiring system each company uses.**
`atsProvider` tells you whether a company posts through **Greenhouse, Lever,
Ashby, Workable or SmartRecruiters** — on ~72% of listings. If you sell
recruiting software, that's a qualified lead list, not job data.

**3. Company signals you can filter on.**
Every row carries the company's headcount band, one-line pitch, and badges like
**Y Combinator**, **Top investors**, **Growing fast**, **$1B+ valuation**. Ask
for "YC companies paying $150k+" in one form.

***

### Quick start

Three fields, and only the first is required:

```
1. What jobs?    →  Software Engineer     (pick from the list or type your own)
2. Where?        →  leave empty for remote, or pick cities
3. How many?     →  200
```

Press **Start**. That's it — everything else has sensible defaults.

***

### What you get back

```json
{
  "id": "3272257",
  "url": "https://wellfound.com/jobs/3272257-software-engineer",
  "title": "Software Engineer",

  "companyName": "Agave API",
  "companySize": "SIZE_11_50",
  "companyTagline": "Field and finance, finally in sync",
  "companyBadges": ["ACTIVELY_HIRING", "TOP_INVESTORS", "YC", "GROWING_FAST"],

  "compensationRaw": "$130k – $210k • 0.05% – 0.2%",
  "salaryMin": 130000,
  "salaryMax": 210000,
  "equityMin": 0.05,
  "equityMax": 0.2,
  "hasEquity": true,

  "jobType": "full-time",
  "remote": false,
  "locationNames": ["San Francisco"],
  "yearsExperienceMin": 3,
  "atsProvider": "Ashby",
  "postedAt": "2026-08-21T16:30:58.000Z",
  "description": "Full job description, several thousand characters..."
}
```

The dataset also comes with three ready-made views — **Overview**,
**Salary benchmarking**, and **Companies & ATS** — so you can look at the right
columns without building a spreadsheet first.

***

### Recipes — copy, paste, run

**Well-funded YC startups paying $150k+, no interns**

```json
{
  "roles": ["software-engineer", "product-manager"],
  "requiredBadges": ["YC"],
  "salaryMinimum": 150000,
  "excludeKeywords": ["intern"],
  "maxItems": 300
}
```

**Fresh listings only, posted this month**

```json
{
  "roles": ["data-scientist"],
  "locations": ["new-york", "boston"],
  "datePosted": "30",
  "maxPagesPerSearch": 25
}
```

**Salary benchmark for one role across five cities**

```json
{
  "roles": ["backend-engineer"],
  "locations": ["san-francisco", "new-york", "austin", "seattle", "london"],
  "onlyWithSalary": true,
  "includeDescription": false,
  "maxItems": 2000
}
```

**Everyone hiring through Greenhouse — a lead list**

```json
{
  "roles": ["software-engineer", "sales", "marketing"],
  "atsProviders": ["Greenhouse"],
  "includeDescription": false
}
```

**Early-stage companies open to juniors**

```json
{
  "roles": ["software-engineer"],
  "companySizes": ["SIZE_1_10", "SIZE_11_50"],
  "maxYearsExperience": 3
}
```

***

### Every option, explained

#### Search — what to look for

| Option | Default | What it does |
|---|---|---|
| **`roles`** | `software-engineer` | **Required.** Pick from 18 suggestions or type any Wellfound role slug. Each role is searched separately. |
| **`locations`** | *(empty)* | Leave empty for **remote** jobs. Pick cities for on-site roles. Every role pairs with every location — 2 roles × 3 cities = 6 searches. |
| **`maxItems`** | `200` | Stop once this many **unique** jobs are saved. Duplicates never count, so you get what you asked for. |

Both dropdowns accept custom text. Type `backend-engineer` or `netsuite` and it
works — the lists are suggestions, not limits.

**If Wellfound doesn't have your role, the search is rejected rather than
silently answered.** Wellfound responds to an unknown role by returning *every*
remote job — about 11,700 of them — with no error. This scraper checks what
Wellfound actually searched for and tells you, so you never get thousands of
unrelated rows believing they matched.

Roles that work but aren't in the dropdown include specific tools and skills
like `netsuite` (73 jobs) or `salesforce`. Broad words like `analyst` do not —
use `data-analyst` or `financial-analyst` instead.

#### Filters — all optional, all combine

A job must satisfy **every** filter you set. Leave them blank to get everything.

| Option | What it does |
|---|---|
| `datePosted` | How recent. Dropdown from 24 hours to 6 months, or type any number of days. **See the note below — this one has a catch.** |
| `onlyWithSalary` | Drop listings that publish no pay. Essential for benchmarking so blanks don't skew averages. |
| `salaryMinimum` | Pays at least this. Listings with no published pay are excluded. |
| `salaryMaximum` | Pays at most this. Useful for targeting a band. |
| `onlyWithEquity` | Only listings stating an equity range. |
| `onlyRemote` | Only listings marked remote. |
| `keywords` | Job title must contain at least one of these. Case-insensitive. |
| `excludeKeywords` | Job title must contain none of these. Good for removing `intern`, `senior`, `contract`. |
| `requiredBadges` | Company must carry **every** badge you pick. |
| `companySizes` | Headcount bands, from 1-10 up to 5000+. |
| `jobTypes` | Full-time, part-time, contract, internship, cofounder. Most listings are full-time. |
| `atsProviders` | Greenhouse, Lever, Ashby, Workable, SmartRecruiters. |
| `maxYearsExperience` | Cap the experience asked for. Listings that state no requirement are **kept**. |

**Company badges you can filter on:**
`YC` · `TOP_INVESTORS` · `GROWING_FAST` · `ACTIVELY_HIRING` · `HIGHLY_RATED` ·
`QUICK_RESPONDER` · `TOP_RESPONDER` · `STRONG_LEADERSHIP` ·
`WORK_LIFE_BALANCE` · `VALUATION_1B` · `VALUATION_500M` · `B2B` · `B2C`

#### Output

| Option | Default | What it does |
|---|---|---|
| `includeDescription` | `true` | Full descriptions run several thousand characters. Turn off for a much lighter dataset when you only need titles, companies and pay. |

#### Advanced — you usually won't need these

| Option | Default | What it does |
|---|---|---|
| `maxPagesPerSearch` | `10` | How deep to go per search. **1 page ≈ 45 jobs, 10 ≈ 300, 48 ≈ 1,900.** The scraper stops early on its own when a search runs out. |
| `startUrls` | — | Paste Wellfound URLs to scrape as-is, alongside everything above. |
| `maxConcurrency` | `3` | Searches in parallel. Lower it if you see blocking errors. |
| `maxRetries` | `4` | Retries per page. Blocked retries switch to residential proxy automatically. |
| `proxyConfiguration` | Apify Proxy | Wellfound blocks plain datacenter IPs, so proxy is on by default. |

***

### Two things worth knowing

#### Old listings stay live

Wellfound doesn't expire postings quickly. Measured across a real 400-job run:

| Window | Share of listings |
|---|---|
| Last 24 hours | **~0%** |
| Last 3 days | ~4% |
| Last week | ~8% |
| Last 2 weeks | ~19% |
| **Last month** | **~39%** |
| Last 3 months | ~67% |

Median listing age is about **50 days**; the oldest was over six years.

So **"Last 24 hours" will return nothing** — that's Wellfound, not a bug.
**"Last month" is the sweet spot**: genuinely active listings, still good volume.

Because the date filter is applied after fetching, narrow windows need more
pages. If you set `datePosted` to a week, raise `maxPagesPerSearch` to 20–30.

#### How many jobs are actually available

Each search has a real ceiling, and the log tells you what it is:

```
✓ software-engineer / remote — 7 page(s), 174 kept of ~1928 available
```

Roughly **1,900** remote software-engineer jobs, **744** in New York. Want more?
Add roles and cities — duplicates across searches are removed automatically, so
overlapping searches never cost you twice.

***

### When something fails

Failures come back as rows with a typed `error`, never dropped silently:

| `error` | What it means |
|---|---|
| `page_not_found` | No such role or city on Wellfound — check the spelling |
| `role_not_recognised` | Wellfound doesn't have that role. It silently returns every remote job for unknown roles, so the search is rejected instead of handing you thousands of unrelated rows. Pick from the dropdown or use a broader term |
| `location_not_recognised` | Same trap, for cities. An unknown city is dropped from the search and Wellfound answers with jobs in *every* city — about 5,000 instead of 770. Rejected rather than handed to you as a match |
| `no_jobs_found` | Page loaded but was empty. Usually means the search ran out |
| `all_filtered_out` | Every job found was removed by your filters. Filters combine with **AND**, so one over-narrow setting empties the result. The row names every active filter and how many jobs were found before filtering |
| `blocked` | Wellfound challenged the request. Retried automatically on residential proxy |
| `rate_limited` | Too many requests. Backed off and retried |
| `no_data_payload` | Wellfound changed their page structure — please open an issue |
| `network_error` | Timeout or connection failure |

A full breakdown is saved to the key-value store as `RUN_SUMMARY`.

***

### Good to know

- **Public listings only.** No login, no candidate data, no personal information.
- **Runs are resumable.** If Apify moves your run to another server, or you stop
  it, progress is saved and it continues where it left off — you're never
  charged twice for the same jobs.
- **No headless browser**, so it's fast and cheap. 400 jobs in about 20 seconds.
- **Duplicates removed across every search**, automatically.

***

### FAQ

**How do I scrape jobs from Wellfound?**
Pick a role, optionally a city, and press Start. No login and no API key —
Wellfound job pages are public.

**Is Wellfound the same as AngelList?**
Yes. AngelList Talent was renamed Wellfound. Same listings, same companies.

**Can I get salary data from Wellfound?**
That is the point of this one. Wellfound publishes pay as text like
`"$130k – $210k • 0.05% – 0.2%"`; this returns `salaryMin`, `salaryMax`,
`equityMin`, `equityMax` and `hasEquity` as numbers you can sort, filter and
average. Roughly 92% of listings carry pay.

**How do I find only Y Combinator startups?**
Set `requiredBadges` to `["YC"]`. It combines with `salaryMinimum`, company size
and the rest — a job must satisfy every filter you set.

**Can I tell which applicant tracking system a company uses?**
Yes, on about 72% of listings — Greenhouse, Lever, Ashby, Workable or
SmartRecruiters. If you sell recruiting software, that is a qualified lead list
rather than job data.

**What happens if I type a role or city Wellfound doesn't have?**
The search is rejected with `role_not_recognised` or `location_not_recognised`.
Wellfound answers an unknown role or city with *every* job instead of an error —
about 5,000 unrelated rows — so this checks what it actually searched for rather
than handing you a false match.

**How fresh are the listings?**
Median age is about 50 days, and roughly 39% were posted in the last month.
`datePosted` filters by age, but "Last 24 hours" genuinely returns almost
nothing — that is Wellfound, not a bug.

**How many jobs can I get in one run?**
Each search has a real ceiling and the log prints it — around 1,900 remote
software-engineer roles, 744 in New York. Add roles and cities for more;
duplicates across searches are removed automatically.

**Is scraping Wellfound legal?**
It reads public job listings only. No login, no candidate data, no personal
information. You are responsible for how you use the output.

***

### Support

Open an issue on the **Issues** tab and you'll get a reply, usually the same day.
Bug reports and requests for extra fields are equally welcome — tell me what you
need and I'll try to add it.

# Actor input Schema

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

Pick from the list, or type any role Wellfound uses (e.g. `backend-engineer`). Each role is searched separately.

## `locations` (type: `array`):

Leave empty for REMOTE jobs. Pick cities for on-site roles, or type any city slug. Every role is paired with every location — 2 roles × 3 cities = 6 searches. Note that "Remote" and an empty list are the same search.

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

Stops as soon as this many unique jobs are saved. Duplicates never count toward it, so you always get what you asked for.

## `onlyWithSalary` (type: `boolean`):

About 92% of Wellfound listings publish pay. Turn this on for salary benchmarking so blank rows don't skew your averages.

## `onlyRemote` (type: `boolean`):

Keep only listings marked remote by the company.

## `onlyWithEquity` (type: `boolean`):

Keep only listings that state an equity range. Equity is common at early-stage startups and rarer at large ones.

## `salaryMinimum` (type: `integer`):

Listings that publish no salary are excluded when you set this.

## `salaryMaximum` (type: `integer`):

Keep only jobs whose published minimum salary is at or below this. Useful for finding roles in a specific band.

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

Case-insensitive. e.g. `senior`, `staff`, `machine learning`.

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

e.g. `intern`, `contract`, `senior`.

## `requiredBadges` (type: `array`):

Company must have every signal you pick. Great for sourcing — `Y Combinator` finds YC companies, `Top investors` finds well-funded ones.

## `companySizes` (type: `array`):

Keep only companies in these headcount bands. Pick several to widen the range.

## `jobTypes` (type: `array`):

Most Wellfound listings are full-time.

## `atsProviders` (type: `array`):

Which system the company posts through. Useful if you sell recruiting tools.

## `maxYearsExperience` (type: `integer`):

Listings that state no requirement are kept.

## `datePosted` (type: `string`):

How recent the listing must be. Wellfound keeps listings live a long time — the median is around 50 days old — so short windows return few results. You can also type a custom number of days.

## `includeDescription` (type: `boolean`):

Descriptions run to several thousand characters. Turn off for a much lighter dataset when you only need titles, companies and pay.

## `maxPagesPerSearch` (type: `integer`):

Each page adds roughly 30 more jobs. 1 page ≈ 45 jobs, 10 pages ≈ 300, and a busy role has ~48 pages (~1,900 jobs). Raise this to go deeper; the scraper stops early on its own when a search runs out.

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

Paste Wellfound search URLs to scrape them as-is, in addition to anything above.

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

Searches run in parallel. Lower this if you see blocking errors.

## `maxRetries` (type: `integer`):

Blocked retries switch to residential proxy automatically.

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

Wellfound blocks plain datacenter IPs, so Apify Proxy is on by default.

## Actor input object example

```json
{
  "roles": [
    "software-engineer"
  ],
  "maxItems": 200,
  "onlyWithSalary": false,
  "onlyRemote": false,
  "onlyWithEquity": false,
  "datePosted": "",
  "includeDescription": true,
  "maxPagesPerSearch": 10,
  "maxConcurrency": 3,
  "maxRetries": 4,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

All job records with parsed compensation, description, company and ATS provider.

## `salaryData` (type: `string`):

Just the fields needed for compensation analysis: title, company, location, salary and equity ranges.

## `runSummary` (type: `string`):

Pages scraped, jobs saved, and a breakdown of any failures by type.

# 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 = {
    "roles": [
        "software-engineer"
    ],
    "maxItems": 200,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("spool/angellist-jobs-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 = {
    "roles": ["software-engineer"],
    "maxItems": 200,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("spool/angellist-jobs-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 '{
  "roles": [
    "software-engineer"
  ],
  "maxItems": 200,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call spool/angellist-jobs-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,spool/angellist-jobs-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/KQgIeDXKKMVrqBMEA/builds/8rv0d1JJewIncadhR/openapi.json
