# Wellfound Jobs Scraper With ATS Vendor & Hiring Velocity (`simpleapi/wellfound-jobs-scraper`) Actor

Wellfound Jobs Scraper extracts public job listings from Wellfound, including job titles, company names, locations, salary ranges, skills, job descriptions, employment types, and application URLs. Ideal for recruitment, job market research, salary analysis, and hiring insights.

- **URL**: https://apify.com/simpleapi/wellfound-jobs-scraper.md
- **Developed by:** [SimpleAPI](https://apify.com/simpleapi) (community)
- **Categories:** Jobs, Automation, Developer tools
- **Stats:** 1 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 and usage. You are charged both the fixed price for specific events and for Apify platform usage.

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 With ATS Vendor & Hiring Velocity

Scrape startup job listings from **Wellfound** (formerly AngelList Talent) and get the employer's **recruiting stack** with them: which **applicant tracking system** each startup runs — Greenhouse, Lever, Ashby and others — the company's own **website domain**, the **exact posting timestamp** of every role, and a per-company **hiring-velocity** picture built from those timestamps.

Most job scrapers hand you a title, a company name and a relative date like "3 days ago." This one parses Wellfound's own ATS integration token off every listing, resolves the employer's real domain from the job page's schema.org data, and converts posting timestamps into exact dates and job-age-in-days — so you can tell which startups are actively hiring through which vendor, and how fast.

### What is Wellfound Jobs Scraper With ATS Vendor & Hiring Velocity?

It's an Apify actor that scrapes Wellfound job listings by search URL, talent market, employer name or role keyword, and enriches every row with the employer's recruiting stack. `atsVendor` is parsed directly from Wellfound's own `atsSource` integration token (e.g. `AtsIntegration::Greenhouse::Listing` → `Greenhouse`), with the raw token kept alongside for auditing. `postedAt` and `jobAgeDays` come from Wellfound's `liveStartAt` epoch timestamp, never from a fuzzy "posted X days ago" string. On top of the per-job rows, it can optionally add uncharged rollup rows: one per company summarizing hiring velocity for this run, and one per ATS vendor summarizing that vendor's share of the sample — each stamped with its own `sampleSize` so you know how much data backs the number.

### What data can I extract with Wellfound Jobs Scraper With ATS Vendor & Hiring Velocity?

**Job rows** (charged, one per listing):

| Field | Description |
| --- | --- |
| `job_title` / `company_name` / `location` / `salary` / `job_type` | Standard listing fields. |
| `atsVendor` | Recruiting platform, parsed from Wellfound's own token (e.g. `Greenhouse`, `Lever`, `Ashby`). `null` if the listing publishes no ATS token (~15-20% of listings). |
| `atsSourceRaw` | The raw, unparsed `atsSource` token, kept for auditing every parse. |
| `autoPosted` | `true` if the role was syndicated in from the ATS rather than hand-posted on Wellfound. |
| `companyWebsite` / `companyWebsiteSource` | Employer's own domain, read from the job page's schema.org `hiringOrganization.sameAs` (only when `resolveCompanyWebsite` is on). |
| `postedAt` / `postedAtEpoch` / `jobAgeDays` | Exact posting timestamp and age in days, derived from Wellfound's `liveStartAt` epoch. |
| `URL` / `description` | Job page link and full description text. |

**Company hiring-velocity rows** (uncharged, `rowType: "company_velocity"`, one per employer): `rolesPostedInRun`, `medianJobAgeDays`, `newestJobAgeDays`, `oldestJobAgeDays`, `postingSpanDays`, `rolesPerWeekInRun`, `autoPostedSharePct`, all carrying their own `sampleSize`.

**ATS-vendor share rows** (uncharged, `rowType: "ats_share"`, one per vendor): `jobsWithVendor`, `vendorSharePctOfKnown`, `atsCoveragePct`, `jobsTotalInRun`, `sampleSize`.

### Why not build this yourself?

Wellfound's job **detail pages are server-rendered with no `__NEXT_DATA__` element at all** — a naive scraper that gates on that element (as several implementations do) mislabels every good detail page as anti-bot-blocked. This actor gates detail pages on the schema.org `ld+json` blob actually parsing as a `JobPosting` instead, which is the signal that's actually present. It also parses the ATS vendor from an internal token Wellfound doesn't document, resolves company websites through a rate-limited per-company lookup (never per-job, since Wellfound throttles bursts of detail-page hits hard), and escalates through a no-proxy → datacenter → residential ladder automatically when its anti-bot pushes back.

One honest limit: roughly 15-20% of Wellfound listings publish no `atsSource` token at all — those get `atsVendor: null`, and if you set `atsVendorInclude` to filter by vendor, unknown-vendor listings are always excluded rather than guessed at. Velocity and ATS-share rollups scope strictly to what was captured in that run and are not a market-wide statistic — a one-page run is a small sample and its `sampleSize` reflects that.

### How to use data extracted from Wellfound Jobs Scraper With ATS Vendor & Hiring Velocity?

- **Sales teams selling to recruiting/HR tech** — target startups on a specific ATS (e.g. every Greenhouse customer hiring engineers in NYC) using `atsVendorInclude`.
- **Recruiters and talent researchers** — track which startups are actively posting roles and how fast (`rolesPerWeekInRun`) to prioritize outreach to companies in active hiring sprints.
- **Market researchers** — build an ATS-vendor market-share snapshot for a talent market or role category from the `ats_share` rollup rows.
- **Competitive intelligence teams** — monitor a specific employer's hiring pace and role mix over repeated scheduled runs.

### 🔼 Input sample

| Parameter | Required | Type | Description | Example |
| --- | --- | --- | --- | --- |
| `jobSearchUrls` | No | array | Wellfound search/listing URLs or single job pages. Takes priority over the market filters below. Base key `startUrls` also accepted. | `["https://wellfound.com/location/new-york"]` |
| `talentMarketLocation` | No | string | City/region to profile when no URLs are supplied. Base key `locationName` also accepted. Default `"New York"`. | `"San Francisco"` |
| `roleKeyword` | No | string | Narrow to one role family or technology. Base key `includeKeyword` also accepted. | `"software engineer"` |
| `employerName` | No | string | Profile one startup instead of a whole market. Base key `companyName` also accepted. | `"Acme Inc"` |
| `resultPages` | No | integer | Result pages to walk per URL (1-100). Wider sweeps give velocity/ATS-share a bigger sample. Base key `pagesToFetch` also accepted. Default `1`. | `3` |
| `atsVendorInclude` | No | array | Allow-list of ATS vendors to keep. **Common pitfall:** listings with an unknown ATS token (~15-20%) are always excluded when this is set, never treated as a match. | `["Greenhouse", "Lever"]` |
| `resolveCompanyWebsite` | No | boolean | Open one job page per company to read its real domain. Rate-limited, one load per company, never per job. Default `false`. | `true` |
| `maxCompanyWebsiteLookups` | No | integer | Hard cap on those extra page loads for the whole run. Default `10`. | `10` |
| `includeHiringVelocityRollup` | No | boolean | Append the uncharged per-company and per-ATS-vendor rollup rows. Default `true`. | `true` |
| `employmentType` | No | string | Restrict to one type: `FULLTIME`, `PARTTIME`, `CONTRACTOR`, `INTERN`. Base key `jobType` also accepted. | `"FULLTIME"` |
| `freshnessWindow` | No | string | Posting recency filter: `all`, `today`, `3days`, `week`, `month`. Base key `datePosted` also accepted. Default `"all"`. | `"week"` |
| `captureJobDescriptions` | No | boolean | Keep the full description text (normally free with the listing payload). Base key `fetchDescriptions` also accepted. Default `true`. | `true` |
| `proxyGeoCountry` | No | string | Proxy exit country — geo-targets the proxy only, not which jobs are returned. Base key `countryName` also accepted. Default `"United States"`. | `"United States"` |
| `requestDelay` | No | number | Seconds to pause before each page load. Default `1`. | `1` |
| `headless` | No | boolean | Run the browser headless. Default `true`. | `true` |
| `proxyConfiguration` | No | object | Optional. Default no proxy — the actor escalates to datacenter then residential automatically on an anti-bot block. | `{"useApifyProxy": false}` |

```json
{
  "talentMarketLocation": "New York",
  "roleKeyword": "software engineer",
  "resultPages": 3,
  "resolveCompanyWebsite": true,
  "includeHiringVelocityRollup": true
}
```

### 🔽 Output sample

```json
{
  "job_title": "Senior Backend Engineer",
  "company_name": "Acme Inc",
  "location": "New York, NY",
  "atsVendor": "Greenhouse",
  "atsSourceRaw": "AtsIntegration::Greenhouse::Listing",
  "autoPosted": true,
  "companyWebsite": "https://acme.com/",
  "postedAt": "2026-07-18T09:12:00Z",
  "jobAgeDays": 8.2,
  "URL": "https://wellfound.com/jobs/1234567-senior-backend-engineer"
}
```

### How do you filter and target specific Wellfound job listings?

Combine market/role/employer filters with `atsVendorInclude` and `freshnessWindow` to build a precisely scoped recruiting-stack map.

**Only Greenhouse and Lever roles in one market:**

```json
{ "talentMarketLocation": "San Francisco", "atsVendorInclude": ["Greenhouse", "Lever"] }
```

**One employer's full hiring pace, with website resolution:**

```json
{ "employerName": "Acme Inc", "resolveCompanyWebsite": true, "includeHiringVelocityRollup": true }
```

**Roles posted in the last week only, full-time:**

```json
{ "talentMarketLocation": "Remote", "freshnessWindow": "week", "employmentType": "FULLTIME" }
```

### ▶️ Want to try other scrapers?

| Actor | What it does |
| --- | --- |
| [twitter-x-reply-scraper-with-replier-contact-leads](https://apify.com/SimpleAPI/twitter-x-reply-scraper-with-replier-contact-leads) | Pull X reply-author contact leads. |
| [twitter-x-posts-scraper-replies](https://apify.com/SimpleAPI/twitter-x-posts-scraper-replies) | Scrape X posts with full reply threads. |
| [x-twitter-posts-search-by-advanced-keyword-filters](https://apify.com/SimpleAPI/x-twitter-posts-search-by-advanced-keyword-filters) | Search X posts with advanced keyword filters. |

### How to extract Wellfound jobs programmatically

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_API_TOKEN>")
run = client.actor("SimpleAPI/wellfound-jobs-scraper-with-ats-vendor-hiring-velocity").call(run_input={
    "talentMarketLocation": "New York",
    "roleKeyword": "software engineer",
    "resultPages": 3,
    "atsVendorInclude": ["Greenhouse", "Lever"],
})

for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item.get("company_name"), item.get("atsVendor"), item.get("jobAgeDays"))
```

Every run's dataset can also be exported straight to JSON, CSV, Excel or an API endpoint from the Apify Console — no code needed for a one-off pull.

### Is it legal to scrape Wellfound job listings?

Scraping publicly accessible data — including public job listings — is generally lawful in the US under the precedent set in *hiQ Labs v. LinkedIn*, which held that scraping data not gated behind a login does not violate the Computer Fraud and Abuse Act. This actor reads only Wellfound's public job listings and job detail pages — no login or account is used. If you extract data tied to identifiable individuals (e.g. a named hiring contact), GDPR (EU) and CCPA (California) still govern what you do with it, so review your own use case.

### ❓ FAQ

**Why is `atsVendor` null for some listings?**
Roughly 15-20% of Wellfound listings publish no `atsSource` integration token at all — those roles were posted directly on Wellfound rather than syndicated from an ATS. `atsVendor` is `null` for them, and `atsSourceRaw` will also be empty.

**How exact is `postedAt`?**
It's parsed from Wellfound's own `liveStartAt` epoch timestamp on the listing payload (or the schema.org `datePosted` on detail-only fetches) — an exact timestamp, not a "3 days ago" string parsed into an estimate.

**Why does `companyWebsite` only fill in for some companies?**
Website resolution costs one extra page load per company and is capped by `maxCompanyWebsiteLookups` (default 10) — Wellfound throttles bursts of job-page hits, so the budget stays deliberately small. Raise the budget for broader coverage, but expect the run to take longer.

**What do the hiring-velocity rollup rows actually measure?**
Only what was captured in that specific run — `rolesPostedInRun`, job-age statistics and `rolesPerWeekInRun` are all scoped to the listings this run pulled, not the company's total headcount growth or a market-wide baseline. Every rollup figure ships with its own `sampleSize` so you can judge confidence; a single-page run yields a small sample.

**Why did some job detail pages return no description?**
Wellfound throttles bursts of detail-page hits and starts serving small anti-bot stub pages after repeated fast requests. The actor detects this by document size (a real detail page is roughly 80KB, a stub is ~2.5KB) and disables further per-job description fetches for the rest of the run rather than returning corrupted data — listing fields, ATS vendor and posting date are unaffected since they come from the search-results payload itself.

**Does `atsVendorInclude` ever guess at an unknown vendor?**
No. Listings with no parseable ATS token are always excluded when this filter is set, never matched by default — an unknown vendor cannot be shown to match a named one.

**How is this different from other Wellfound/startup job scrapers on Apify?**
As observed on the Apify Store on 2026-07-26, the job scrapers we reviewed target other ATS platforms directly (e.g. Recruitee) rather than Wellfound's own listing aggregator, and none surface the employer's underlying ATS vendor or build a per-company hiring-velocity view from posting timestamps.

### Conclusion

Wellfound Jobs Scraper With ATS Vendor & Hiring Velocity turns a Wellfound job search into a recruiting-stack map — which ATS each startup runs, its real domain, exact posting timing, and how fast it's hiring, all sourced from the listing's own data rather than guessed.

# Actor input Schema

## `jobSearchUrls` (type: `array`):

Wellfound search/listing URLs (e.g. https://wellfound.com/location/new-york, https://wellfound.com/role/r/software-engineer) or single job pages (https://wellfound.com/jobs/123456-role). Takes priority over the market filters below. The base key `startUrls` is still accepted.

## `talentMarketLocation` (type: `string`):

City or region whose hiring you want to profile (e.g. New York, San Francisco, Remote). Used to build the Wellfound URL when no URLs are supplied. Base key `locationName` is still accepted.

## `roleKeyword` (type: `string`):

Narrow the map to one role family or technology (e.g. "software engineer", "recruiter", "python"). Base key `includeKeyword` is still accepted.

## `employerName` (type: `string`):

Profile one startup's hiring instead of a whole market. Cards whose company name loosely matches are kept. Base key `companyName` is still accepted.

## `resultPages` (type: `integer`):

How many result pages to walk per URL. A wider sweep gives velocity and ATS-share a bigger sample — a one-page run is a small sample and is labelled as such in the output. Base key `pagesToFetch` is still accepted.

## `atsVendorInclude` (type: `array`):

Optional allow-list, e.g. Greenhouse, Lever, Ashby. Leave empty to keep every listing. Listings whose ATS vendor is unknown (~15-20% publish no atsSource) are excluded when this filter is set — unknown is never treated as a match.

## `resolveCompanyWebsite` (type: `boolean`):

Opens ONE job page per company to read the employer's own domain from its schema.org hiringOrganization.sameAs block. Costs one extra page load per company (never per job) and is rate-limited, so keep the budget below modest. Default off.

## `maxCompanyWebsiteLookups` (type: `integer`):

Hard cap on those extra page loads for the whole run. Wellfound throttles bursts of job-page hits, so this stays small on purpose. Example: 10 = at most 10 companies get a website. Default is 10.

## `includeHiringVelocityRollup` (type: `boolean`):

Appends uncharged summary rows: one per company (roles posted in this run, median/newest/oldest job age, roles per week) and one per ATS vendor (share of the run). Every figure carries its sampleSize; anything rate-shaped over a single observation is null. Default on.

## `employmentType` (type: `string`):

Restrict to one employment type. Base key `jobType` is still accepted.

## `freshnessWindow` (type: `string`):

How recently the role went live. Job age itself is always reported exactly, in days, from Wellfound's own posting timestamp. Base key `datePosted` is still accepted.

## `captureJobDescriptions` (type: `boolean`):

Keep the full description text. Descriptions ship with the listing payload, so this normally costs no extra request. Base key `fetchDescriptions` is still accepted.

## `proxyGeoCountry` (type: `string`):

Country the proxy exit nodes should appear in. This geo-targets the PROXY only — it does not filter which jobs are returned. Base key `countryName` is still accepted.

## `requestDelay` (type: `number`):

Pause before each page load. Raising it lowers the chance of anti-bot throttling on long sweeps. Default is 1.

## `headless` (type: `boolean`):

Leave on. Turn off only when debugging locally with a display attached. Default is true.

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

Optional. Default = no proxy; on an anti-bot block the Actor falls back to datacenter and then residential sessions on its own.

## Actor input object example

```json
{
  "jobSearchUrls": [
    "https://wellfound.com/location/new-york"
  ],
  "talentMarketLocation": "New York",
  "roleKeyword": "",
  "employerName": "",
  "resultPages": 1,
  "atsVendorInclude": [],
  "resolveCompanyWebsite": false,
  "maxCompanyWebsiteLookups": 10,
  "includeHiringVelocityRollup": true,
  "employmentType": "",
  "freshnessWindow": "all",
  "captureJobDescriptions": true,
  "proxyGeoCountry": "United States",
  "requestDelay": 1,
  "headless": true,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `results` (type: `string`):

All scraped items in the Actor's default dataset, combined view — job rows, uncharged rollup rows, and any error rows.

## `hiringVelocity` (type: `string`):

Uncharged per-company hiring-velocity rollup rows only (roles posted, median/newest/oldest job age, roles per week), scoped to this run.

## `jobListings` (type: `string`):

Job listing rows only (charged), without the rollup or error rows.

# 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 = {
    "jobSearchUrls": [
        "https://wellfound.com/location/new-york"
    ],
    "atsVendorInclude": [],
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("simpleapi/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 = {
    "jobSearchUrls": ["https://wellfound.com/location/new-york"],
    "atsVendorInclude": [],
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("simpleapi/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 '{
  "jobSearchUrls": [
    "https://wellfound.com/location/new-york"
  ],
  "atsVendorInclude": [],
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call simpleapi/wellfound-jobs-scraper --silent --output-dataset

```

## MCP server setup

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