Wellfound Jobs Scraper avatar

Wellfound Jobs Scraper

Pricing

$0.25 / 1,000 wellfound job listings

Go to Apify Store
Wellfound Jobs Scraper

Wellfound Jobs Scraper

Export public Wellfound startup jobs by role, location or job URL. Get stable job IDs, source salary text, descriptions and CSV or JSON output with explicit coverage limits.

Pricing

$0.25 / 1,000 wellfound job listings

Rating

0.0

(0)

Developer

Cliqto Media

Cliqto Media

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

2

Monthly active users

9 days ago

Last modified

Share

Export public Wellfound startup jobs by role, location or job URL. Get one Dataset row per unique job, with stable IDs and the fields the source provides. Use the small prefilled search to check your first result. Filters or source access limits can leave fewer rows than your requested maximum.

Wellfound Jobs Scraper: role and location search, stable job IDs, and CSV or JSON export

On this page: Quick start · Use cases · Input · Output · Pricing · Limits · Help · FAQ

Quick start

  1. Open Input in Apify Console. A new form suggests a small software engineer search in New York, with maxItems: 3, one page and eight request attempts.
  2. Leave the suggested input for your first run. Use the default 1 GB memory and 300-second platform timeout. Check the maximum cost before you start.
  3. Click Start. Open the Dataset and check jobId, title, companyName and jobUrl. Download CSV or JSON from the Dataset export menu.
  4. Open RUN_SUMMARY in the Key-Value Store. Check status, itemsEmitted, diagnosticCodes and terminationReasons before using the data.

The form suggestion is UI prefill, not an API default. An API input {} reads the bounded public /jobs feed, with maxItems: 50, three pages and includeDescription: true. It does not silently use the New York demo.

{
"startUrls": [
{ "url": "https://wellfound.com/role/l/software-engineer/new-york" }
],
"maxItems": 3,
"maxPagesPerSeed": 1,
"maxRequests": 8
}

This small input returned real job records in the current live check. Source pages change, so the exact jobs and run time can change too.

What it does

  • Reads public /jobs, role, location and exact-job pages with direct HTTPS.
  • Builds role and location URLs from slugs, or accepts supported URLs.
  • Removes duplicate numeric job IDs across pages and URLs. Keeps first-seen order and source information.
  • Applies keyword, remote, employment-type and posting-age filters to fetched fields. It does not search a private API or a global database.
  • Adds a public description from a bounded job-page request when needed.
  • Records valid empty, partial, blocked, not-found and invalid outcomes in KVS.

A good fit is a bounded startup job export, job-board feed or market sample. It is not a fit for a complete global index, candidate contacts, private jobs, historical backfill or data that needs a Wellfound account. There is no login, proxy, browser or CAPTCHA bypass. Company-job URLs are accepted as a route, but company pages can be blocked. This is not a promise of working company coverage.

Use cases

Your taskInputUseful fields and next step
Compare startup hiring in one placeroles and locationsGroup companyName, title and locations in your exported CSV.
Build a public job-board feedstartUrls, bounded maxItemsJoin on jobId; link to jobUrl; show missing values as unknown.
Check remote jobsremoteOnly: trueUse remoteKind and acceptedRemoteLocations to see source limits.
Review published payA small role searchKeep compensationText; use parsed numbers only with their source currency and unit.
Verify a known listingOne exact jobUrlRead description, postedAt and RUN_DIAGNOSTICS. A removed job is not a valid empty search.

Input reference

No top-level input field is required. Every object in startUrls needs url. All slugs must be lowercase letters/numbers with hyphens. Roles and locations are route slugs, not free-text place names. Invalid limits, types or slugs produce INVALID_INPUT before source work. A bad URL in a mixed batch is reported while valid URLs can still return rows.

FieldTypeRuntime defaultUI prefillValues and behavior
modestringautononeauto detects each URL. search, exact_job and company_jobs require a matching route.
startUrlsarray of {url: string}[]New York URL aboveSupported HTTPS URLs. Empty/omitted falls back to /jobs only when no role/location seed exists. Legacy API string entries also work.
rolesstring[]nonenoneUp to five role slugs, for example software-engineer. With locations, adds every role/location combination.
locationsstring[]nonenoneUp to five location slugs, for example new-york. Without roles, adds /location pages.
remoteOnlybooleanfalsenoneOnly explicit remote=true rows. False and unknown remote signals are excluded when enabled.
querystring""noneLocal keyword match in title, company, role or verified description; not an upstream full-text search. Empty means no keyword filter.
jobTypestringanynoneLocal filter; unknown type cannot match a selected type.
postedWithinDaysintegernonenone0–3650 days. Missing/null disables the filter. Unknown or future dates cannot match.
includeDescriptionbooleantruenoneFetches one public detail page for a missing description, within request, row and time bounds. Existing inline descriptions are kept even when false.
maxItemsinteger5031–1000 output rows. Collection also stops after a page reaches this many unique candidates, before filters. It may return fewer matching rows.
maxPagesPerSeedinteger311–50 pages per seed, with early source/duplicate stops.
maxRequestsinteger20081–1500 total attempts, including redirects, retries and descriptions.
requestTimeoutSecsinteger20none5–60 seconds for headers and response body in each attempt.
requestDelayMsinteger750none500–10000 ms between request starts, with up to 25% jitter. Retry-After can extend the wait.
maxRetriesinteger2none0–2 retries for temporary connection/header timeout, 429 and selected 5xx failures. No retry for 401/403/404 or challenges.
transportstringAUTOnoneAUTO and DIRECT both use direct HTTPS. Legacy API DATACENTER/RESIDENTIAL return TRANSPORT_UNAVAILABLE without source work.
startUrls[].urlstring, required inside objectnoneFrom parent prefillPublic HTTPS URL on exact wellfound.com or legacy angel.co host. No credentials, custom ports or arbitrary query parameters.

Interactions and examples

Explicit URLs and generated role/location URLs are combined and deduplicated. Leave startUrls empty to use only the generated routes. At most 25 unique seeds are accepted. Duplicate URL seeds are removed before requests; duplicate jobs are counted separately. maxItems applies to the whole run, not each URL.

{
"roles": ["software-engineer"],
"locations": ["new-york"],
"remoteOnly": true,
"maxItems": 20,
"maxPagesPerSeed": 1,
"maxRequests": 8
}

To verify one current job, copy a jobUrl from a search result:

{
"mode": "exact_job",
"startUrls": [
{ "url": "https://wellfound.com/jobs/4787364-software-engineer" }
],
"maxItems": 1,
"maxRequests": 3
}

A job can close after you copy its URL. Do not keep an old URL as a permanent demo. The new-form prefill uses a search page for this reason.

Output reference

The default Dataset contains job records only. It has no error or billing rows. Numeric job IDs are stored as strings; id adds the wellfound: prefix. Scalar fields with no verified value are null; list fields are []. All defined job fields are present. No missing salary, date or remote flag is filled from a guess. Rows keep first-seen order and may gain missing fields from a later verified duplicate/detail record.

FieldTypeNon-nullSemantics
recordTypeJOB_LISTINGyesstable discriminator
idstringyeswellfound:<numeric-job-id>
jobIdstringyesnumeric Wellfound identifier as string
titlestringyessource title, trimmed; empty title is a parse failure
slugstring/nullnosource job slug when present
jobUrlURLyescanonical https://wellfound.com/jobs/<id>-<slug>
companyIdstring/nullnopublic company ID when present
companyNamestring/nullnopublic employer name; never a candidate name
companySlugstring/nullnopublic company slug
companyUrlURL/nullnocanonical Wellfound company URL when present; not fetched as a child
primaryRolestring/nullnoobserved source role label
jobTypeenum/nullnonormalized full-time, part-time, contract, internship, cofounder or null
locationsstring[]yesall source-listed work locations; empty only when absent
acceptedRemoteLocationsstring[]yessource “Hires Remotely From”/accepted regions; not inferred
remoteboolean/nullnoexplicit source remote-capable signal
remoteKindstring/nullnosource configuration such as REMOTE, ONSITE_OR_REMOTE, ONSITE
wfhFlexibleboolean/nullnosource flag when present
compensationTextstring/nullnooriginal published compensation string
salaryMinnumber/nullnoparsed numeric lower bound only when unambiguous
salaryMaxnumber/nullnoparsed numeric upper bound only when unambiguous
salaryCurrencystring/nullnosource currency code; text parser maps $ to USD, € to EUR and £ to GBP; never converts currencies
equityMinPercentnumber/nullnoparsed percentage lower bound; source unit is percent
equityMaxPercentnumber/nullnoparsed percentage upper bound; source unit is percent
compensationParseStatusenumyesnot_present, parsed, partial, ambiguous
experienceMinYearsnumber/nullnosource lower bound
experienceMaxYearsnumber/nullnosource upper bound
postedAtISO timestamp/nullnoconverted only from observed epoch/ISO value
postedAtSourceenumyesliveStartAt, jsonld, source_text, absent
descriptionstring/nullnopublic description when inline or exact detail was verified; no candidate/private content
descriptionFormatenumyesmarkdown, html_text, absent
atsSourcestring/nullnopublic listing token only; no external ATS request
badgesstring[]yespublic company/job labels, empty when absent
sourceUrlURLyesresult/detail URL that produced this row, canonicalized for provenance
sourceModeenumyessearch, exact_job, company_jobs
sourcePageinteger/nullnopage number when the source exposes one
observedAtISO timestampyesUTC extraction time
coveragepublic_page_subsetyeshonest completeness label

Real example

Selected fields from the New York search above, observed on 9 October 2026. The description below shows only its opening heading; the Dataset row holds the full published description. This listing may later close or change.

{
"recordType": "JOB_LISTING",
"id": "wellfound:4787364",
"jobId": "4787364",
"title": "Software Engineer",
"jobUrl": "https://wellfound.com/jobs/4787364-software-engineer",
"companyName": "Chalk",
"jobType": "full-time",
"locations": ["New York City", "San Francisco"],
"remote": false,
"compensationText": "$170k – $250k",
"salaryMin": 170000,
"salaryMax": 250000,
"salaryCurrency": "USD",
"postedAt": "2026-09-30T23:42:54.000Z",
"description": "**About Chalk**",
"descriptionFormat": "markdown",
"sourceUrl": "https://wellfound.com/role/l/software-engineer/new-york",
"sourceMode": "search",
"sourcePage": 1,
"observedAt": "2026-10-09T14:49:16.673Z",
"coverage": "public_page_subset"
}

descriptionFormat: markdown keeps source Markdown and can keep source HTML entities. html_text strips HTML tags and joins whitespace. absent means the value is null. Descriptions and badges are source text, not a normalized skills or visa dataset. The Actor does not open application or ATS URLs.

Money values keep their source numeric scale. They are not converted to a common yearly amount; use the source text/page to check the pay period. Unsupported money text stays in compensationText with null numeric fields and an ambiguous parse status. Percent equity is stored in percent, not a fraction. Relative posting text is not turned into an exact date. observedAt is the extraction time, while postedAt is the source posting time when known.

Dataset, Output and KVS

The Output tab links to the Dataset and three KVS records. There is no separate stored OUTPUT result object. The Dataset API/export exposes the same job rows. The output-schema properties dataset, runSummary, runDiagnostics and runProgress are strings containing API URLs for these four surfaces, respectively. They are generated by Apify; they are not extra Dataset columns.

SurfaceWhat to read
Default DatasetOne saved row per unique jobId. itemsEmitted matches its row count.
RUN_SUMMARYProduct status, coverage, seed/page counts, source total when known, emitted/duplicate/filtered/unverifiable counts, request attempts, retries, bytes, timing, limits reached, per-seed accounting and row billing.
RUN_DIAGNOSTICSDiagnostic code, stage, severity, safe url, HTTP status, attempts, transport, message, terminal flag and observedAt. No raw error pages or credentials.
RUN_PROGRESSLatest status, current seed/page, request and row counters, last event and update time. This is progress, not a saved resume checkpoint.
Product statusMeaning
SUCCEEDEDUseful rows without an affecting source/output diagnostic.
EMPTYVerified source, with no rows after filters and no affecting failure.
PARTIALUseful rows remain, but a source/detail, time, output or charge limit affected the run. Read diagnostics.
FAILEDNo useful verified output because input, resources or source work failed. A block is never renamed an empty search.

Normal and valid empty runs can have platform status SUCCEEDED. RUN_SUMMARY.status gives the product outcome; inspect it even after a green platform run. A failed product run exits with an error. Optional source fields can be null in a valid row without being a technical failure.

The billing object in RUN_SUMMARY counts row events only. Its chargedUsd is a row subtotal. The Run API's chargedEventCounts and applied event prices show the total, including the automatic start event. maxTotalChargeUsd is a platform run option that includes both start and rows; it is not an input field or a promise to retrieve a certain number of jobs.

Pricing

Each saved wellfound-job event costs $0.00025 — $0.25 per 1,000 rows. Until October 23, 2026 at 15:01:16 UTC, the published price has no start event. A notified price change adds the standard apify-actor-start event at $0.0133 per start from that time, with the row price unchanged. At supported 256–1024 MB memory, Apify counts one automatic start event. Platform usage stays included; it is not added as a separate usage fee. Check the current Pricing tab before you run.

After that price change, empty, fully filtered, blocked, invalid or failed runs have a start charge. They have no row charge if no useful row was saved. Partial runs charge only the useful rows saved, plus the start. Duplicates and diagnostics have no row charge. Each repeated run is a new run with its own start and saved-row charges. This Actor has no changed-only monitor mode.

The table separates the current row-only rate from the notified future rate.

Saved rowsUntil the change, USDFrom the change: start + rows, USD
0$0$0.0133
3$0.00075$0.01405
50$0.0125$0.0258
1,000$0.25$0.2633

These are price calculations, not coverage promises. Source access and filters can return fewer rows. Set the total maximum cost high enough to cover the start and your intended rows. If it is too low, the platform can stop the run. At 1 GB the start event is counted once; Apify uses one event per GB, with a minimum of one, for Actors that support larger memory.

Limits and source access

  • Supported search routes are /jobs, /role/r/<slug>, /role/l/<role>/<location> and /location/<slug>. Exact jobs use /jobs/<numeric-id>-<slug>; legacy company-prefixed exact URLs normalize to this route. Legacy angel.co must end on an allowed Wellfound route.
  • A company route /company/<slug>/jobs can return a login/access challenge. The current Chime check returned 403. Successful company listing coverage is not supported by that evidence. Use a public search or exact-job URL instead.
  • Only exact wellfound.com and legacy angel.co HTTPS hosts are accepted. Up to five allowlisted redirects are allowed. Private/service routes, credentials, arbitrary hosts and unsupported query parameters are rejected.
  • The row cap is 1–1,000, but it is not a promise of 1,000 results. Pages stop on empty source state, repeated IDs, source page count or explicit limits. Collection counts unique candidates before local filters. A narrow filter can return fewer rows without scanning later pages.
  • Source work has an internal 240-second deadline. A larger platform timeout does not extend it. A shorter timeout reserves 15 seconds for output. Valid rows collected before a deadline can survive with PARTIAL diagnostics.
  • Default memory is 1 GB; 256–1024 MB is accepted. Lower memory can be slower. Invalid resources are recorded as INVALID_RESOURCES, not INVALID_INPUT.
  • Normal output is a public_page_subset. Source totals are observations, not verified global totals. Jobs, order, page sizes and fields can change between requests. No permanent freshness or global completeness is promised.

This tool is independent of Wellfound and AngelList. Check the current Wellfound site and the rules that apply to your use. It does not grant permission to collect or reuse data beyond your rights.

API, export and repeat runs

Use the Apify Run API with the same JSON input. Keep your token in your normal credential store or environment; never put it in the job URLs or Dataset. A small request uses the current default build:

curl --request POST \
'https://api.apify.com/v2/acts/cliqtomedia~wellfound-jobs-scraper/runs?memory=1024&timeout=300&maxTotalChargeUsd=0.05' \
--header "Authorization: Bearer $APIFY_TOKEN" \
--header 'Content-Type: application/json' \
--data '{"roles":["software-engineer"],"locations":["new-york"],"maxItems":3,"maxPagesPerSeed":1,"maxRequests":8}'

Read the run's default Dataset after it finishes. The Dataset export menu supports CSV, JSON and other Apify export formats. Use those files in your spreadsheet or job-feed process. No separate spreadsheet connection is needed. See the Run API for request options.

You can create a schedule yourself in Apify Console to rerun a saved task. Each run returns a new snapshot and deduplicates only within that run. Use jobId in your own system to compare snapshots. A scheduled run does not add monitoring, notifications or history features to this Actor.

Troubleshooting

SymptomCheck and safe action
Empty DatasetRead RUN_SUMMARY.status. With EMPTY, check filters and fetched page limits. With FAILED, read diagnostic codes first.
SOURCE_BLOCKED or login wallUse an accessible public route. Do not add cookies or bypass the challenge.
HTTP_404Check that the exact job still exists and that its numeric ID is correct.
MALFORMED_STATE or DETAIL_FETCH_FAILEDThe source changed or an optional detail could not be verified. Keep valid partial rows; report the run ID.
INVALID_INPUTCheck types, bounds, slugs, URL route and mode. Keep one small known public input.
INVALID_RESOURCESUse supported memory and enough timeout for output; input fields are not the cause.
Fewer rows than requestedCheck maxItems, page/request stops, local filters and unverifiable. Increasing a limit cannot remove a source access restriction.
Missing pay, remote or descriptionTreat null as unknown. Check the raw source text and description setting; values are not inferred.
Charge limit reachedCheck the total start-plus-row limit and CHARGE_LIMIT_REACHED. Start a new bounded run only if needed.

FAQ

Is this an official Wellfound API?

No. It reads the public pages supported above. It is not affiliated with Wellfound and does not use a private Wellfound API.

Does the keyword search all Wellfound jobs?

No. query filters the fetched title, company, role and verified description. Use role/location routes to choose the source window. remoteOnly also filters fetched rows; it does not prove global remote-job coverage.

Why can a job have no posting date or salary?

Those fields may not be published in the fetched source. Unknown values stay null. Pay is not converted across currencies or pay periods, and relative dates are not guessed as exact timestamps.

Can I fetch company pages or use a proxy?

Company routes are accepted for honest diagnostics, but the current challenge check does not prove working company coverage. Proxy transports, login and CAPTCHA solving are unavailable. No proxy subscription is needed for direct public routes.

Will repeating a run return only new jobs?

No. Each run is a fresh bounded snapshot. It may return the same IDs, and each saved row is charged again. Compare jobId in your own storage for changes.

Is a green run enough to trust the export?

Check both the Dataset and RUN_SUMMARY. PARTIAL can have useful rows; a valid EMPTY has none. A requested maximum and a source-reported total are not proof of complete coverage.

For job listings on public Lever boards, use our Lever Jobs Scraper. It reads a different source and is not a replacement for Wellfound filters.

Use the Issues tab on this Actor's Apify Store page for support. Include the run ID, product status, diagnostic code and a small sanitized input. Remove tokens, cookies, private URLs and personal information. Do not post raw error pages or account credentials.