# Wellfound (AngelList) Jobs Scraper — Salary, Equity & Geo (`oswaldocarabano/wellfound-jobs-scraper`) Actor

Startup job listings from Wellfound (formerly AngelList Talent) with full descriptions, structured salary, geocoded locations, investor badges and the ATS each company hires through.

- **URL**: https://apify.com/oswaldocarabano/wellfound-jobs-scraper.md
- **Developed by:** [Oswaldo Carabano](https://apify.com/oswaldocarabano) (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

Pay per event

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

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

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

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

# README

## Wellfound Startup Jobs & Companies Scraper

Startup job listings from [Wellfound](https://wellfound.com) (formerly AngelList Talent),
with the **full job description included in every row** — no separate enrichment event.

### What you get

| Field group | Contents |
|---|---|
| Job | `title`, **`description`** (full markdown), `job_type`, `is_remote`, `remote_type`, `posted_at`, `direct_apply` |
| Compensation | `compensation_raw`, `salary_min`/`_max`/`_currency`/`_period`, `equity_offered`, `equity_min_pct`/`_max_pct` |
| Location | `location_names`, `location_locality`/`_region`/`_country`, **`latitude`/`longitude`**, `applicant_location_requirements` |
| Experience | `years_experience_min`, `months_experience_required` |
| ATS | `ats_source` — Greenhouse, Ashby, Lever or Workable |
| Company | `company_name`, `company_size_min`/`_max`, `company_pitch`, `company_badges`, `company_website`, `company_industries`, geocoded HQ |
| Provenance | `scraped_at`, `from_cache`, `data_age_hours`, `source_surface` |

### Measured fill rates

Measured on **2,229 unique jobs across 25 roles and all 13 categories**.

| Field | Fill rate |
|---|---|
| `description` | **100 %** |
| `location_names` | 90.5 % (min 83.7 %) |
| `compensation_raw` | 79.5 % (min 60.3 %) |
| `salary_min` / `_max` | 86.7 % of enriched rows |
| `latitude` / `longitude` | 93.3 % of enriched rows |
| `ats_source` | **21–83 % depending on role category** |
| `years_experience_min` | 31.6 % (12.8–75 % by category) |

🔴 **`ats_source` varies by 62 percentage points across categories** — 83 % in HR and
recruiting, 21 % in Marketing. The average would mislead you, so the range is what we
publish.

**We do not ship `yearsExperienceMax`.** It is filled in 0.1 % of rows (2 of 2,229), so a
column of nulls is all it would give you.

### What this actor does not do

- **No company profile data** — no funding rounds, investors, perks or team. Wellfound
  protects `/company/*` behind a Cloudflare challenge, and that page also exposes named
  founders and employees. We do not scrape people.
- **No personal data.** Job descriptions are delivered exactly as the company wrote them;
  nothing is extracted or derived from them.

### Freshness

Every row reports `from_cache` and `data_age_hours`. That figure **includes Cloudflare's
own cache age**, which we measured at up to 23.4 hours on job detail pages — so it tells
you how old the data is, not how recently we asked for it. Set `maxCacheAgeDays: 0` to
force a fresh fetch.

### Pricing

| Event | Price |
|---|---|
| Job delivered | $3.00 / 1,000 |
| Company delivered (deduplicated) | $1.50 / 1,000 |
| Actor start | $0.01 / 1,000 |

**Full job descriptions and detail-page enrichment are included in the job price**, never
billed separately — so you can estimate a run's cost before starting it. **Error rows are
never charged.**

### Notes on filtering

Wellfound filters by URL path, not query string. `remoteOnly` is ignored when `locations`
is set, because the site returns 404 for the combined form. There is **no site-wide
pagination limit**: each role paginates until its corpus runs out. Use `maxJobs` to cap a
run.

# Actor input Schema

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

Role slugs to scrape, e.g. "software-engineer", "product-manager". Wellfound publishes 518 role slugs across 13 categories; leave empty to use the roles in `roleCategories` instead. Slugs come from wellfound.com/browse/tech-jobs.

## `roleCategories` (type: `array`):

Scrape every role in these categories instead of naming roles one by one. Engineering alone holds 227 roles, so expect long runs.

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

Location slugs, e.g. "san-francisco", "new-york", "london". Wellfound publishes 306 location slugs. Each role is crossed with each location. Leave empty for no location filter.

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

Only jobs Wellfound flags as remote. Note: Wellfound does not allow combining remote with a location filter, so this is ignored when `locations` is set.

## `maxJobs` (type: `integer`):

Hard cap on delivered job rows. There is no unlimited mode. One listing request returns about 28 unique jobs, and a role yields about 1,950 unique jobs on average.

## `maxPagesPerRole` (type: `integer`):

Safety cap on pages per role. There is NO site-wide pagination limit: each role paginates until its corpus is exhausted and then redirects. Measured ceilings vary widely — engineer 130 pages, software-engineer 91, office-manager 37 — so a fixed value delivers a role's full corpus or a third of it depending on the role. Prefer capping with Maximum jobs.

## `enrichFromJobPage` (type: `boolean`):

Fetch each job's own page to add structured salary (min/max/currency), latitude and longitude, benefits, the company website and its geocoded HQ. Costs one extra request per job. The full job description is already included without this.

## `emitCompanies` (type: `boolean`):

Write a deduplicated `companies` dataset. A company with 56 jobs appears once.

## `maxCacheAgeDays` (type: `integer`):

Serve cached rows up to this age. Set to 0 to force a fresh fetch. Every row reports `from_cache` and `data_age_hours` so you always know what you received.

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

Requests in flight per proxy session. Wellfound rate-limits per IP: 16 is the highest value measured without a block, and 24 triggers a 429 with a ~5 minute cooldown. Raising this does not make a run faster once you are rate-limited.

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

Residential proxy strongly recommended. Measured: the same concurrency that returns 24 of 36 requests as HTTP 429 from a datacenter IP returns 48 of 48 as HTTP 200 from a residential IP.

## Actor input object example

```json
{
  "roles": [
    "software-engineer",
    "product-manager"
  ],
  "roleCategories": [],
  "locations": [],
  "remoteOnly": false,
  "maxJobs": 500,
  "maxPagesPerRole": 40,
  "enrichFromJobPage": true,
  "emitCompanies": false,
  "maxCacheAgeDays": 1,
  "maxConcurrency": 6,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

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

Job rows, one per listing, with the full description included.

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

Counters for the run: rows, errors, blocks, truncated bodies and cache hits.

# 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",
        "product-manager"
    ],
    "maxJobs": 500
};

// Run the Actor and wait for it to finish
const run = await client.actor("oswaldocarabano/wellfound-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",
        "product-manager",
    ],
    "maxJobs": 500,
}

# Run the Actor and wait for it to finish
run = client.actor("oswaldocarabano/wellfound-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",
    "product-manager"
  ],
  "maxJobs": 500
}' |
apify call oswaldocarabano/wellfound-jobs-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,oswaldocarabano/wellfound-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/MrQARovQ81uvWhci9/builds/nVlDquG3tMYkXtBiM/openapi.json
