Wellfound Jobs Scraper
Pricing
$0.25 / 1,000 wellfound job listings
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
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
2
Monthly active users
9 days ago
Last modified
Categories
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.

On this page: Quick start · Use cases · Input · Output · Pricing · Limits · Help · FAQ
Quick start
- 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. - 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.
- Click Start. Open the Dataset and check
jobId,title,companyNameandjobUrl. Download CSV or JSON from the Dataset export menu. - Open
RUN_SUMMARYin the Key-Value Store. Checkstatus,itemsEmitted,diagnosticCodesandterminationReasonsbefore 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 task | Input | Useful fields and next step |
|---|---|---|
| Compare startup hiring in one place | roles and locations | Group companyName, title and locations in your exported CSV. |
| Build a public job-board feed | startUrls, bounded maxItems | Join on jobId; link to jobUrl; show missing values as unknown. |
| Check remote jobs | remoteOnly: true | Use remoteKind and acceptedRemoteLocations to see source limits. |
| Review published pay | A small role search | Keep compensationText; use parsed numbers only with their source currency and unit. |
| Verify a known listing | One exact jobUrl | Read 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.
| Field | Type | Runtime default | UI prefill | Values and behavior |
|---|---|---|---|---|
mode | string | auto | none | auto detects each URL. search, exact_job and company_jobs require a matching route. |
startUrls | array of {url: string} | [] | New York URL above | Supported HTTPS URLs. Empty/omitted falls back to /jobs only when no role/location seed exists. Legacy API string entries also work. |
roles | string[] | none | none | Up to five role slugs, for example software-engineer. With locations, adds every role/location combination. |
locations | string[] | none | none | Up to five location slugs, for example new-york. Without roles, adds /location pages. |
remoteOnly | boolean | false | none | Only explicit remote=true rows. False and unknown remote signals are excluded when enabled. |
query | string | "" | none | Local keyword match in title, company, role or verified description; not an upstream full-text search. Empty means no keyword filter. |
jobType | string | any | none | Local filter; unknown type cannot match a selected type. |
postedWithinDays | integer | none | none | 0–3650 days. Missing/null disables the filter. Unknown or future dates cannot match. |
includeDescription | boolean | true | none | Fetches one public detail page for a missing description, within request, row and time bounds. Existing inline descriptions are kept even when false. |
maxItems | integer | 50 | 3 | 1–1000 output rows. Collection also stops after a page reaches this many unique candidates, before filters. It may return fewer matching rows. |
maxPagesPerSeed | integer | 3 | 1 | 1–50 pages per seed, with early source/duplicate stops. |
maxRequests | integer | 200 | 8 | 1–1500 total attempts, including redirects, retries and descriptions. |
requestTimeoutSecs | integer | 20 | none | 5–60 seconds for headers and response body in each attempt. |
requestDelayMs | integer | 750 | none | 500–10000 ms between request starts, with up to 25% jitter. Retry-After can extend the wait. |
maxRetries | integer | 2 | none | 0–2 retries for temporary connection/header timeout, 429 and selected 5xx failures. No retry for 401/403/404 or challenges. |
transport | string | AUTO | none | AUTO and DIRECT both use direct HTTPS. Legacy API DATACENTER/RESIDENTIAL return TRANSPORT_UNAVAILABLE without source work. |
startUrls[].url | string, required inside object | none | From parent prefill | Public 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.
| Field | Type | Non-null | Semantics |
|---|---|---|---|
| recordType | JOB_LISTING | yes | stable discriminator |
| id | string | yes | wellfound:<numeric-job-id> |
| jobId | string | yes | numeric Wellfound identifier as string |
| title | string | yes | source title, trimmed; empty title is a parse failure |
| slug | string/null | no | source job slug when present |
| jobUrl | URL | yes | canonical https://wellfound.com/jobs/<id>-<slug> |
| companyId | string/null | no | public company ID when present |
| companyName | string/null | no | public employer name; never a candidate name |
| companySlug | string/null | no | public company slug |
| companyUrl | URL/null | no | canonical Wellfound company URL when present; not fetched as a child |
| primaryRole | string/null | no | observed source role label |
| jobType | enum/null | no | normalized full-time, part-time, contract, internship, cofounder or null |
| locations | string[] | yes | all source-listed work locations; empty only when absent |
| acceptedRemoteLocations | string[] | yes | source “Hires Remotely From”/accepted regions; not inferred |
| remote | boolean/null | no | explicit source remote-capable signal |
| remoteKind | string/null | no | source configuration such as REMOTE, ONSITE_OR_REMOTE, ONSITE |
| wfhFlexible | boolean/null | no | source flag when present |
| compensationText | string/null | no | original published compensation string |
| salaryMin | number/null | no | parsed numeric lower bound only when unambiguous |
| salaryMax | number/null | no | parsed numeric upper bound only when unambiguous |
| salaryCurrency | string/null | no | source currency code; text parser maps $ to USD, € to EUR and £ to GBP; never converts currencies |
| equityMinPercent | number/null | no | parsed percentage lower bound; source unit is percent |
| equityMaxPercent | number/null | no | parsed percentage upper bound; source unit is percent |
| compensationParseStatus | enum | yes | not_present, parsed, partial, ambiguous |
| experienceMinYears | number/null | no | source lower bound |
| experienceMaxYears | number/null | no | source upper bound |
| postedAt | ISO timestamp/null | no | converted only from observed epoch/ISO value |
| postedAtSource | enum | yes | liveStartAt, jsonld, source_text, absent |
| description | string/null | no | public description when inline or exact detail was verified; no candidate/private content |
| descriptionFormat | enum | yes | markdown, html_text, absent |
| atsSource | string/null | no | public listing token only; no external ATS request |
| badges | string[] | yes | public company/job labels, empty when absent |
| sourceUrl | URL | yes | result/detail URL that produced this row, canonicalized for provenance |
| sourceMode | enum | yes | search, exact_job, company_jobs |
| sourcePage | integer/null | no | page number when the source exposes one |
| observedAt | ISO timestamp | yes | UTC extraction time |
| coverage | public_page_subset | yes | honest 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.
| Surface | What to read |
|---|---|
| Default Dataset | One saved row per unique jobId. itemsEmitted matches its row count. |
RUN_SUMMARY | Product 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_DIAGNOSTICS | Diagnostic code, stage, severity, safe url, HTTP status, attempts, transport, message, terminal flag and observedAt. No raw error pages or credentials. |
RUN_PROGRESS | Latest status, current seed/page, request and row counters, last event and update time. This is progress, not a saved resume checkpoint. |
| Product status | Meaning |
|---|---|
SUCCEEDED | Useful rows without an affecting source/output diagnostic. |
EMPTY | Verified source, with no rows after filters and no affecting failure. |
PARTIAL | Useful rows remain, but a source/detail, time, output or charge limit affected the run. Read diagnostics. |
FAILED | No 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 rows | Until the change, USD | From 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. Legacyangel.comust end on an allowed Wellfound route. - A company route
/company/<slug>/jobscan 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.comand legacyangel.coHTTPS 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
PARTIALdiagnostics. - Default memory is 1 GB; 256–1024 MB is accepted. Lower memory can be slower.
Invalid resources are recorded as
INVALID_RESOURCES, notINVALID_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
| Symptom | Check and safe action |
|---|---|
| Empty Dataset | Read RUN_SUMMARY.status. With EMPTY, check filters and fetched page limits. With FAILED, read diagnostic codes first. |
SOURCE_BLOCKED or login wall | Use an accessible public route. Do not add cookies or bypass the challenge. |
HTTP_404 | Check that the exact job still exists and that its numeric ID is correct. |
MALFORMED_STATE or DETAIL_FETCH_FAILED | The source changed or an optional detail could not be verified. Keep valid partial rows; report the run ID. |
INVALID_INPUT | Check types, bounds, slugs, URL route and mode. Keep one small known public input. |
INVALID_RESOURCES | Use supported memory and enough timeout for output; input fields are not the cause. |
| Fewer rows than requested | Check maxItems, page/request stops, local filters and unverifiable. Increasing a limit cannot remove a source access restriction. |
| Missing pay, remote or description | Treat null as unknown. Check the raw source text and description setting; values are not inferred. |
| Charge limit reached | Check 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.
Related Actor and support
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.