Wellfound Jobs Scraper (AngelList) avatar

Wellfound Jobs Scraper (AngelList)

Pricing

from $0.09 / 1,000 job returneds

Go to Apify Store
Wellfound Jobs Scraper (AngelList)

Wellfound Jobs Scraper (AngelList)

Wellfound refuses a share of plain HTTP requests, so every page gets up to six attempts from a new address and fingerprint. A page that lands carries every job in full: Markdown description, salary and equity split apart, company badges, exact posting time. $0.085 per 1,000.

Pricing

from $0.09 / 1,000 job returneds

Rating

0.0

(0)

Developer

Dami's Studio

Dami's Studio

Maintained by Community

Actor stats

0

Bookmarked

1

Total users

1

Monthly active users

10 hours ago

Last modified

Share

Wellfound Jobs Scraper (AngelList Talent)

Wellfound refuses a share of plain HTTP requests outright, with a 403 and no explanation, so this actor gives every listing page up to six attempts, each from a new proxy session and a different browser fingerprint. What lands is worth the retries: a role page carries the complete text of every job on it, not a teaser. Checked on 2026-08-21, all 47 jobs on /role/product-manager came back with a full Markdown description, median around 5,000 characters, none empty.

A role slug like software-engineer is all the input it needs. Each row carries the full job description in Markdown, the compensation string split into salary and equity, the company with its size and Wellfound badges, and the exact posting timestamp.

It runs over plain HTTP. There's no login, no cookie and no headless browser involved, and it never touches residential proxy.

Why the descriptions come back complete

Wellfound's role pages are Next.js pages that ship their own Apollo cache inside __NEXT_DATA__. Every job on the page is already a fully-typed JobListingSearchResult with the whole Markdown description attached, and every company is a StartupResult linked to its jobs. The actor reads that cache straight off the page.

One page request therefore returns roughly 40 to 49 complete jobs. Nothing has to be fetched a second time to get the description, so pulling 200 jobs is 5 requests rather than 205.

Output

FieldNotes
jobId, title, jobUrlthe listing
companyName, companyUrl, companySlug, companyTagline, companySize, companyLogoUrl, companyBadgescompany, joined from the same cache. companySize is Wellfound's enum (SIZE_11_50), companyBadges its own labels ("Actively Hiring", "YC Funded", "Top Investors")
jobTypefull-time, internship, contract, part-time, cofounder
remote, remoteKind, locations, remoteLocationsremoteKind is Wellfound's ONSITE_OR_REMOTE / REMOTE_ONLY enum
compensation, salaryText, equityTextcompensation is Wellfound's raw string ("$135k – $175k • 0.05% – 0.25%"). The other two are that string split in half, never invented
yearsExperienceMinonly when the poster set one
descriptionthe whole description in Markdown, as Wellfound stores it
postedAtexact ISO timestamp from liveStartAt
autoPosted, atsSourcewhether it arrived from an ATS, and which one
sourceUrl, page, scrapedAtprovenance

How often each field is filled

From a 200-row platform run on 2026-08-10: roles software-engineer and product-manager, 3 pages each, 83 distinct companies, 200/200 rows real, 0 diagnostics, 21 seconds.

FieldFilled
jobId, title, jobUrl, companyName, companyUrl, companySlug, companySize, companyLogoUrl, companyBadges, primaryRoleTitle, jobType, description, postedAt100%
companyTagline97.5%
compensation, salaryText92.5%
locations87.0%
atsSource80.0%
remoteKind28.5%
remoteLocations23.5%
yearsExperienceMin21.5%
equityText14.5%

Median description length was 4,663 characters. Those percentages move with the roles you pick. A later 252-row sample across different roles came back with salary on 83% and equity on 10%. Equity is the field to watch: most Wellfound posters simply leave it blank, and nothing here is filled in with a guess.

Input

  • roleSlugssoftware-engineer, product-manager, data-scientist. Plain phrases get slugified ("Data Scientist" becomes data-scientist) and full wellfound.com/role/<slug> URLs are accepted. searchQueries is an alias for the same field.
  • locationSlugs — optional, such as san-francisco, new-york, london. Each role is then crawled once per location via wellfound.com/role/l/<role>/<location>.
  • remoteOnly — switches to wellfound.com/role/r/<role> and keeps only listings flagged remote. Ignored when locationSlugs is set.
  • jobTypes, locationFilters, keywords — filters applied on our side. Rows they drop are not returned and not charged.
  • maxItems (default 100), maxPagesPerSource (default 5), maxRetries (default 6), requestTimeoutSecs (default 25).
  • proxyConfiguration — optional. Leave it empty and the actor uses Apify datacenter proxy with a fresh session per attempt, which is what it's tuned for.

Empty input returns exactly one row labelled _sample: true and charges nothing.

{
"roleSlugs": ["software-engineer", "product-manager"],
"locationSlugs": ["san-francisco"],
"jobTypes": ["full-time"],
"keywords": ["python"],
"maxItems": 200,
"maxPagesPerSource": 5
}

That crawls wellfound.com/role/l/software-engineer/san-francisco and the product-manager equivalent, up to 5 pages each, keeps full-time listings whose title, company or description mentions "python", and stops at 200 rows.

Remote instead of a city:

{ "roleSlugs": ["data-scientist"], "remoteOnly": true, "maxItems": 100 }

A real output row

{
"ok": true,
"_sample": false,
"jobId": "3317746",
"title": "Software Engineer",
"jobUrl": "https://wellfound.com/jobs/3317746-software-engineer",
"companyName": "Keeper",
"companyUrl": "https://wellfound.com/company/keeper-tax",
"companySlug": "keeper-tax",
"companyTagline": "File your complex taxes confidently",
"companySize": "SIZE_11_50",
"companyLogoUrl": "https://photos.wellfound.com/startups/i/keeper-tax-logo.png",
"companyBadges": ["Actively Hiring", "YC Funded"],
"primaryRoleTitle": "Software Engineer",
"jobType": "full-time",
"remote": true,
"remoteKind": "ONSITE_OR_REMOTE",
"locations": ["San Francisco"],
"remoteLocations": ["United States"],
"compensation": "$135k – $175k • 0.05% – 0.25%",
"salaryText": "$135k – $175k",
"equityText": "0.05% – 0.25%",
"yearsExperienceMin": 0,
"description": "### Mission\n\nWe are building…",
"postedAt": "2026-08-01T00:00:00.000Z",
"autoPosted": false,
"atsSource": "greenhouse",
"sourceUrl": "https://wellfound.com/role/software-engineer",
"page": 1,
"scrapedAt": "2026-08-10T09:14:02.113Z"
}

Diagnostic rows have the opposite shape: ok: false, _diagnostic: true, an errorCode of BAD_INPUT, BLOCKED, RATE_LIMITED, NOT_FOUND, SERVER_ERROR, NETWORK, PARSE_ERROR or NO_RESULTS, and a plain-English error. Filter on ok === true if you only want jobs.

Reliability

Wellfound 403s a share of requests no matter what address they come from. Rotation is what fixes that, not a better address: every attempt uses a new proxy session over rotating datacenter addresses and a different browser TLS fingerprint. On the 200-row run above, 4 of the 5 listing pages parsed on the first attempt and the fifth on the third. None failed outright. A page that never parses becomes an uncharged BLOCKED diagnostic row, so a bad day costs you the start fee and nothing more.

Retries back off, 300 ms doubling to a 5 s ceiling with jitter, so a temporary refusal doesn't turn into a hammering loop. maxRetries caps attempts per page and requestTimeoutSecs caps a single request, which gives a run a predictable worst case.

An earlier build drove a headless browser and fell back to a per-gigabyte proxy whenever it got blocked. It was slower, much more expensive, and still got refused. Both are gone. The direct HTTP path turned out to be cheaper and more reliable, and that's what makes the price below workable.

Billing

Pay per event, event name job: $0.085 per 1,000 jobs, plus a $0.001 start fee per run.

One event per unique job row. Duplicates across pages are charged once, because the runner keeps a set of jobIds, so overlapping pages and two roles that surface the same listing bill once rather than twice. Never charged: the empty-input sample row, rows dropped by jobTypes / locationFilters / keywords, and every diagnostic row.

So 1,000 jobs in one run costs $0.001 + $0.085 = $0.086. A run that finds nothing costs the $0.001 start fee alone. Because descriptions arrive with the listings, there's no second charge for "detail" rows.

What this does not do

  • No company-jobs pages. wellfound.com/company/<slug>/jobs sits behind Wellfound's login wall and returned 403 on 8 straight attempts across 4 fingerprints, so that input isn't offered rather than offered and broken.
  • No per-job detail requests. They'd be pointless; the listing cache already has the full description.
  • No recruiter contact details, no candidate profiles, and it doesn't apply to anything.
  • No salary normalisation into numbers. Wellfound publishes a string, so you get the string plus its salary and equity halves.
  • No residential proxy.

FAQ

What's a role slug? The last path segment of a Wellfound role page. wellfound.com/role/software-engineer gives you software-engineer. Pasting the whole URL works too.

How many jobs per page? Roughly 40 to 49. maxPagesPerSource sets how deep it goes, and deep pagination does work: page 20 and page 60 both returned jobs in testing.

Do I really get the full description? Yes, in Markdown, on every row. Median length in the measured run was 4,663 characters.

Can I filter to remote jobs? Set remoteOnly: true. That switches to Wellfound's remote role pages and drops any listing not flagged remote.

Can I search one city? Put the city slug in locationSlugs. Use locationFilters instead if you'd rather do a plain substring match over whatever locations a job lists.

What if Wellfound blocks it? Each page is retried up to maxRetries times on a fresh session and fingerprint. If every attempt fails you get one uncharged BLOCKED row explaining what happened, and no job charges.

Is the sample row charged? No. Empty input returns one _sample: true row and zero billable events.