Wellfound Jobs Scraper | Salaries & Job Changes avatar

Wellfound Jobs Scraper | Salaries & Job Changes

Pricing

from $0.30 / 1,000 job or company records

Go to Apify Store
Wellfound Jobs Scraper | Salaries & Job Changes

Wellfound Jobs Scraper | Salaries & Job Changes

Collect public Wellfound jobs with descriptions, salary and equity fields where available. Filter roles and locations, paginate results and track new or changed jobs. Coverage checks help prevent false expired-job alerts.

Pricing

from $0.30 / 1,000 job or company records

Rating

0.0

(0)

Developer

tingyou333 zhuang

tingyou333 zhuang

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

15 hours ago

Last modified

Categories

Share

Collect public Wellfound startup jobs with role, company, location, salary and equity data where available. Use filtered searches or recurring scans to track new and changed listings.

Quick start Β· Workflows Β· Pricing Β· Input reference Β· Output reference Β· API and automation Β· FAQ Β· Technical reference

What you can do

  • Search and detailed records β€” Use public role/search URLs with job and company enrichment controls.
  • Compensation and location filters β€” Filter salary, role, remote eligibility, keywords and other documented fields.
  • Recurring change tracking β€” Use a persistent stateKey to label new, updated and unchanged records.
  • Safer expiry reporting β€” Coverage checks prevent incomplete scans from automatically marking unseen jobs as expired.

Who uses it

  • Recruiting teams: monitor startup hiring by role and location.
  • Job-market analysts: study salary, equity and remote-work signals in public listings.
  • Automation teams: compare recurring scans and send relevant changes downstream.

Quick start

  1. Open Input, enter your target and a small result limit.
  2. Click Start and inspect the dataset and run log.
  3. Export JSON/CSV, or save the input as a Task for recurring runs.
{
"startUrls": [
"https://wellfound.com/role/r/software-engineer"
],
"maxItems": 20,
"maxPages": 1
}

Form prefills, schema defaults and empty API input can differ. Use explicit targets and limits; see the technical reference for source and default behavior.

Import an existing configuration

crawlerbrosInput accepts the Crawler Bros JSON contract, including its string location. blackfalconInput accepts the Black Falcon contract, including array location, and selects its corresponding filter semantics. Paste the entire original configuration into one object and keep overrides inside it. Native form defaults do not override imported values.

{"crawlerbrosInput": {"startUrls": ["https://wellfound.com/role/r/software-engineer"], "location": "San Francisco", "maxItems": 5}}

Import compatibility does not create unavailable company information: missing source values remain null, and collection diagnostics distinguish source restrictions from empty matches.

Common workflows

Find remote software roles

{
"role": "software engineer",
"remoteOnly": true,
"maxItems": 20,
"maxPages": 2
}

Start recurring change monitoring

{
"startUrls": [
"https://wellfound.com/role/r/software-engineer"
],
"incrementalMode": true,
"stateKey": "my-software-jobs",
"maxItems": 20,
"maxPages": 2,
"emitExpired": false
}

Collect company summaries

{
"startUrls": [
"https://wellfound.com/role/r/software-engineer"
],
"companyOnlyMode": true,
"maxItems": 20,
"maxPages": 1
}

Output at a glance

Illustrative field subset below; these are example values, not a captured customer review or a claim about source coverage. Native and compatibility output profiles can differ.

{
"type": "job",
"jobId": "example-job-id",
"title": "Software Engineer",
"companyName": "Example Company",
"salaryMinUsd": null,
"salaryMaxUsd": null,
"coverage": "partial",
"warnings": [
"Illustrative warning: compensation was not disclosed."
]
}

Public Wellfound pages can be restricted or incomplete. Salary, equity, visa and company fields remain unknown when the source does not expose them. An incomplete scan is not evidence of expiry. Notification adapters have not been verified end to end for every customer channel.

Pricing

Pay per delivered event, with automatic discounts based on your Apify subscription plan. These are result fees in USD, not separate subscriptions sold by this Actor. All tiers have the same Actor features. Apify compute, storage, transfers and any proxy usage are charged additionally.

Billable event (1,000 events)FreeStarterScaleBusiness
Job or company record$0.75$0.6$0.45$0.3

Platinum and Diamond currently use the Business rate. The Pricing tab is the source of truth for the active schedule and your applicable plan.

  • Job or company record: One delivered job, change record, or company aggregation row. Internal state-only rows are not delivered or charged. Platform usage is additional.

Example on the Free plan: 10 / 1,000 / 10,000 events cost $0.0075 / $0.75 / $7.5 in result fees, plus platform usage.

There is no additional developer setup or monthly rental fee. A run with no billable results can still consume platform resources. Start small, cap results and review run costs before scheduling a larger workload. Requested counts are ceilings, not guaranteed deliveries.

Input reference

Fields below are grouped like the Input form. Optional fields need not be supplied. Compatibility input envelopes and transformations are explained in the technical reference.

Targets and collection

FieldTypeDefaultMeaning
startUrlsarrayNot setWellfound /jobs, /role, /role/r, or /role/l URLs. When omitted, role/location/remoteOnly can build a source URL automatically. In blackfalcon compatibility mode these replace query/roles/location/remote search constraints; job and post-fetch filters still apply.
rolestringNot setRole name or slug, for example software engineer. Enables server-rendered role pagination.
jobTitlestringNot setCompatibility filter: title contains this phrase, case-insensitive.
keywordstringNot setCompatibility filter across title, company, locations and description.
locationarrayNot setOne or more location slugs (e.g. ["new-york", "san-francisco", "berlin"]). Each location Γ— role combination spawns a separate search thread. For /jobs?q= searches, use a human-readable city name. Ignored for pasted searchUrls (they carry their own location).
jobTypestring"any"Compatibility filter for the jobType field exposed in Wellfound's public search payload.
experiencestring"any"Best-effort experience filter using title and description terms.
includeNoSalarybooleantrueWhen false, rows without a parseable USD salary range are excluded.
companyCategoriesarrayNot setMigration-compatible filter matching supplied terms against company name/slug, consistent with the head competitor's documented behavior.
includeCompaniesarrayNot setOptional allow-list of company names or slugs.
sincestringNot setISO date/timestamp applied to Wellfound liveStartAt for scheduled incremental runs.
requestDelayMillisinteger0Optional delay before each HTTP attempt.
scanMaxItemsinteger2000Maximum candidate jobs scanned before change selection. maxItems remains the delivery cap.
rolesarrayNot setAdditional source roles or URLs; ignored when startUrls is set.
searchUrlsarrayNot setAdditional source roles or URLs; ignored when startUrls is set.
remotebooleanNot setAlias of remoteOnly; remoteOnly takes precedence.
salaryMinintegerNot setDrop jobs whose upper salary bound is below this value; missing upper bound is excluded. minSalary takes precedence and retains its lower-bound semantics.
salaryMaxintegerNot setDrop jobs whose known lower salary bound exceeds this value. maxSalary takes precedence and retains its upper-bound semantics.
visaSponsorshipbooleanNot settrue requires explicit support. false excludes explicit refusal but keeps unknowns. Omit for no filter.
descriptionFormatstringNot setSelect fields; source Markdown is retained verbatim. Missing source HTML stays null with a warning.
querystringNot setFree-text job search (e.g. "rust engineer", "ML ops", "solidity developer"). Maps to Wellfound's /jobs?q= endpoint. Combine with roles/searchUrls to union multiple searches.
experienceLevelstringNot setFilter by minimum required years of experience. Entry: 0-2, Mid: 2-5, Senior: 5-8, Staff+: 8+. Jobs without experience data are kept.
equityMinnumber0Drop jobs whose max equity grant is below this percent. 0 = no minimum. Jobs without equity data are dropped when minimum is set.
includeKeywordsobjectNot setRequire at least one of these keywords. Example: {"keywords":["rust","golang"],"matchTitle":true,"matchDescription":true,"matchSkills":true}. Matches default to all three fields.
compactbooleanfalseOutput only core fields (id, title, companyName, portalUrl, locationNames, remote, compensation, postedAt). For AI-agent/MCP workflows.
descriptionMaxLengthinteger0Truncate job description to this many characters. 0 = no truncation.
locationTextstringNot setLocation filter. With role, it also builds Wellfound's public role/location route.
skipRepostsbooleanfalseIn incremental mode, suppress newly assigned job IDs whose content matches a previously delivered job confirmed expired by a complete scan. Incomplete scans never expire jobs. Applies to state first recorded with content hashes; older records acquire hashes on their next delivered update.
originLocationstringNot setOrigin city for radius filtering, resolved from offline GeoNames city data. Unknown origins skip the filter with an OUTPUT warning. Unresolved job locations are excluded when the origin is known. Distances use city centres.
radiusMilesinteger0Drop jobs further than N miles from originLocation, using job coordinates where available. US-standard unit.
radiusKminteger0Kilometer alternative to radiusMiles. If both set, radiusMiles wins. Use this for EU-based searches.
cityNamestringNot setOrigin city alias for radius filtering; takes precedence over originLocation and explicit coordinates.
latnumberNot setGPS latitude. Pair with lon. Ignored when cityName is set.
lonnumberNot setGPS longitude. Pair with lat.
telegramChatIdstringNot setChat ID (from @userinfobot) or channel ID (starts with -100 for channels). Required when telegramToken is set.
whatsappPhoneNumberIdstringNot setWhatsApp Business phone number ID from Meta Business Manager (the numeric ID next to your number β€” NOT the phone number itself). Free service-conversation messages within 24 hours of last user-initiated contact.
whatsappTostringNot setRecipient phone number in E.164 format (e.g. +4512345678). The recipient must have messaged your business number within the last 24 hours.
includeRunMetadatabooleantruePrepend a one-line summary (search label + total count) to each notification batch.
whatsappApiVersionstring"v23.0"Graph API version used for the configured WhatsApp business sender. Set a version supported by your Meta application.
enrichDetailbooleanfalseCore job fields β€” title, full description, compensation, location, remote status, experience level, job type, and company name/logo/size β€” are delivered inline for every listing, no toggle needed. This option additionally attempts deep per-listing fields (structured salary currency/period, company website, industry, benefits, skills). Those deep fields are not currently available and will return null, so leave this off unless you specifically need them and accept they may be empty.
enrichCompanybooleanfalseCompany basics β€” name, logo, slug, size β€” are delivered inline for every listing. This option additionally attempts deep firmographics (funding stage, total raised, investors, founders, social links, founded year, markets). Those deep fields are not currently available and will return null.
includeDetailsbooleanfalseFetch the detail page for each listing to retrieve the full description text and contact information. Increases run time and cost. Leave off for a fast, low-cost run.
appConnectorstringNot setOptional Apify MCP connector supporting notion-create-pages or create_issue. Writes one run summary after data delivery; other tools are not called. Configure mcpIssueTeam for issue trackers.
mcpIssueTeamstringNot setTeam name or ID required when the selected connector supports create_issue.

Filters and ordering

FieldTypeDefaultMeaning
remoteOnlybooleanNot setReturn only remote jobs. With role and no location, uses the public remote-role route.
minSalaryintegerNot setRequire the parsed lower bound of a USD salary range to be at least this value.
excludeCompaniesarrayNot setOptional block-list of company names or slugs.
sortstring"none"Sort accepted rows by Wellfound liveStartAt, or preserve source order.
fromDatestringNot setAlias of since; ISO date or timestamp.
excludeEmptyFieldsbooleanNot setDrop null, empty strings, arrays and objects; keep zero and false.
excludeLocationsarrayNot setDrop jobs whose locationNames match any of these (substring match, e.g. "India" drops "Bangalore, India"). Applied after fetch.
toDatestringNot setOnly include jobs posted on or before this date. Accepts YYYY-MM-DD or ISO-8601.
excludeKeywordsobjectNot setDrop jobs containing any of these keywords. Same shape as includeKeywords.
companyOnlyModebooleanfalseAggregate accepted jobs into one record per company. maxResults bounds jobs scanned before aggregation. jobCount is the number of observed jobs in this run, not all openings. Incremental mode is not applied.
notifyOnlyChangesbooleantrueWhen ON (default) AND incrementalMode is ON, send notifications only for NEW and UPDATED jobs. Without incrementalMode, this has no effect (all emitted jobs are notified).

Result limits and cost

FieldTypeDefaultMeaning
maxSalaryintegerNot setRequire the parsed upper bound of a USD salary range to be at most this value. 0 means unlimited.
maxItemsintegerNot setMaximum job rows saved after filtering.
maxPagesinteger10Maximum public role-search pages to fetch. Wellfound currently exposes pageCount in SSR data.
maxRequestRetriesinteger2Retries for transient 403/429/5xx and network failures.
maxResultsintegerNot setAlias of maxItems; 0 means a bounded 5000-row ceiling, not unlimited. maxPages still applies.
maxAgeMinutesinteger0Only include jobs posted within the last N minutes. 0 = no limit. Pairs well with scheduled runs for real-time monitoring.

Monitoring and state

FieldTypeDefaultMeaning
incrementalModebooleanfalseEmit NEW/UPDATED jobs using state committed only after delivery. Run the same stateKey serially.
stateKeystringNot setRequired in incremental mode. Changing search filters requires a new key.
emitExpiredbooleanfalseEmit EXPIRED only after a complete, error-free scan; suppress expiration on partial snapshots.
emitUnchangedbooleanfalseIn incremental mode, include unchanged jobs with changeType=UNCHANGED. Delivered rows still commit state only after successful delivery.
slackWebhookUrlstringNot setSlack incoming webhook URL (Slack App β†’ Incoming Webhooks β†’ Add New Webhook). Formats jobs as Slack blocks with company, salary, funding stage, and location.
discordWebhookUrlstringNot setDiscord channel webhook URL (Server Settings β†’ Integrations β†’ Webhooks β†’ New Webhook). Sends rich embeds colour-coded by change type (green=NEW, amber=UPDATED).
webhookUrlstringNot setUniversal escape hatch for n8n / Make / Zapier / custom HTTP backends. Receives a JSON POST { metadata, items } per run. Single attempt, 15 s timeout.
webhookHeadersobjectNot setAdditional HTTP headers sent with the generic webhook POST (e.g. {"Authorization":"Bearer ..."}).
notificationLimitinteger5Maximum number of jobs to send per notification batch. Prevents spam on large result sets.

Compatibility and transformations

FieldTypeDefaultMeaning
customFiltersarrayNot setArray of rules applied to any output field. Each rule: {"field":"","op":"includes|notIncludes|equals|notEquals|gt|gte|lt|lte","value":...}. Example: [{"field":"companyFundingStage","op":"equals","value":"series-b"}, {"field":"description","op":"notIncludes","value":"wordpress"}].
compatibilityModestring"auto"auto detects Blackfalcon-specific input names or array location; ambiguous inputs preserve legacy URL plus client filters. blackfalcon direct URLs ignore search routing constraints; pasted searchUrls ignore location/remote per source. Explicit post-fetch filters remain active.

Connection and advanced settings

FieldTypeDefaultMeaning
proxyConfigurationobjectNot setOptional Apify or custom proxy used for listing, detail and company requests. Omit or disable for direct access. Configured proxy failures do not silently fall back to direct.
telegramTokenstringNot setTelegram bot token from @BotFather. Sends notifications when new jobs are emitted. Pairs well with incrementalMode for real-time alerts.
whatsappAccessTokenstringNot setMeta Cloud API access token with whatsapp_business_messaging scope. Get a permanent token from a system user in Meta Business Manager.

Additional compatibility options

FieldTypeDefaultMeaning
crawlerbrosInputobjectNot setPaste the complete competitor input JSON here to preserve its field types and defaults. Put all options inside this object. This is optional; normal native inputs remain available.
blackfalconInputobjectNot setPaste the complete competitor input JSON here to preserve its field types and defaults. Put all options inside this object. This is optional; normal native inputs remain available.

Output reference

The table describes native fields; fields can be absent or null when the source does not provide them. Error rows and selected compatibility profiles have different shapes. Nested objects remain structured in JSON; CSV exports may serialize them.

FieldTypeMeaning
typestringType
jobIdstringJob ID
titlestringTitle
jobUrlstringJob URL
description['string', 'null']Description
jobType['string', 'null']Job type
primaryRoleTitle['string', 'null']Primary role
compensation['string', 'null']Compensation
salaryMinUsd['number', 'null']Salary min USD
salaryMaxUsd['number', 'null']Salary max USD
equityMinPercent['number', 'null']Equity min %
equityMaxPercent['number', 'null']Equity max %
remote['boolean', 'null']Remote
locationsarrayLocations
acceptedRemoteLocationsarrayAccepted remote locations
postedAt['string', 'null']Posted at
companyId['string', 'null']Company ID
companyName['string', 'null']Company
companyUrl['string', 'null']Company URL
companySize['string', 'number', 'null']Company size
companyBadgesarrayCompany badges
sourceUrlstringSource URL
sourcePageintegerSource page
coveragestringCoverage
retrievalMethodstringRetrieval method
warningsarrayWarnings
scrapedAtstringScraped at
errorCodestringError code
messagestringMessage
id['string', 'null']id
portalUrl['string', 'null']portalUrl
detailUrl['string', 'null']detailUrl
companyTagline['string', 'null']companyTagline
descriptionMarkdown['string', 'null']descriptionMarkdown
salaryEquityMin['string', 'null']salaryEquityMin
salaryEquityMax['string', 'null']salaryEquityMax
source['string', 'null']source
visaSponsorshipSource['string', 'null']visaSponsorshipSource
salaryMin['number', 'null']salaryMin
salaryMax['number', 'null']salaryMax
yearsExperienceMin['number', 'null']yearsExperienceMin
yearsExperienceMax['number', 'null']yearsExperienceMax
visaSponsorship['boolean', 'null']Visa sponsorship
visaSponsorshipEvidence['string', 'array', 'null']Visa statement evidence
locationNamesarraylocationNames
extractedUrlsarrayextractedUrls

API and automation

The API tab provides snippets for this Actor. For scheduled or larger collections, start a run asynchronously and poll its status instead of holding a synchronous request open. Keep your API token in an environment variable.

import os, time, requests
base = "https://api.apify.com/v2"
headers = {"Authorization": "Bearer " + os.environ["APIFY_TOKEN"]}
actor_input = {'startUrls': ['https://wellfound.com/role/r/software-engineer'], 'maxItems': 20, 'maxPages': 1}
response = requests.post(
base + "/acts/BiUgcZcjV20Fno1wV/runs",
headers=headers, json=actor_input, timeout=30,
)
response.raise_for_status()
run = response.json()["data"]
for _ in range(720):
if run["status"] not in ("READY", "RUNNING", "TIMING-OUT", "ABORTING"):
break
time.sleep(5)
response = requests.get(base + "/actor-runs/" + run["id"], headers=headers, timeout=30)
response.raise_for_status()
run = response.json()["data"]
else:
raise TimeoutError("Polling ended; inspect the existing run before starting another.")
if run["status"] != "SUCCEEDED":
raise RuntimeError(run.get("statusMessage", run["status"]))
response = requests.get(
base + "/datasets/" + run["defaultDatasetId"] + "/items",
headers=headers, params={"format": "json", "limit": 1000, "offset": 0}, timeout=30,
)
response.raise_for_status()
rows = response.json()
print(rows) # Paginate with offset for larger datasets; inspect per-row coverage.

Scheduled monitoring and integrations

  1. Save a working input as an Apify Task.
  2. Attach a Schedule in your timezone; keep runs sharing an incremental state key sequential.
  3. Add a successful-run webhook or connect your downstream workflow to the dataset export/API.
  4. Inspect missing/partial data before sending alerts or treating absence as removal.

Use the platform Integrations/API options to connect external workflows such as Make, n8n or a data warehouse. An external sentiment or summarization step is separate from this Actor and may have its own charges. The platform also supports Actor tools through Apify MCP; availability and client setup depend on your integration.

FAQ

How should I run monitoring?

Save one input as a Task, enable incrementalMode and keep stateKey stable. Run jobs sharing a stateKey serially.

Why is a job not marked expired?

Expiry is suppressed when coverage is insufficient. Absence from a capped, failed or partial scan cannot establish that a job was removed.

Are salary and visa fields guaranteed?

No. Values are reported from source evidence and remain null or unknown when they cannot be established.

Can I export without writing code?

Yes. Run from the Input form, open the dataset and use the export controls for JSON or CSV. Select your preferred output profile before running when migrating an integration.

Does a successful run guarantee every field?

No. Inspect coverage, warnings and source-specific status fields. Optional enrichment may be missing even when usable rows were delivered.

Troubleshooting

SymptomWhat to check
Empty datasetValidate the target, source availability and filters; try the small quick-start input.
Fewer rows than requestedCheck source limits, pagination and filtering. Increasing a cap cannot create unavailable source records.
Partial fieldsInspect coverage/warnings and the relevant source or enrichment status.
Access or throttling errorsCheck the run log and documented proxy configuration; retry at a lower request volume.
Migration mismatchCheck the explicit input/output profile and technical field mapping below.
Unexpected billSeparate result events from platform resources; inspect enrichment, result caps and the Pricing tab.

Technical reference

Why this Actor exists

Wellfound's role search pages expose paginated startup/job search results in __NEXT_DATA__. In the currently validated public surface, those rows include full job descriptions, job type, compensation, remote configuration, accepted remote locations, startup metadata, and page-count information. This allows multi-page collection without opening each DataDome-protected job-detail page.

Migration-compatible input

Existing Wellfound Actor users can keep common fields such as:

  • startUrls
  • jobTitle
  • keyword
  • location
  • remoteOnly
  • jobType
  • experience
  • minSalary / maxSalary
  • includeNoSalary
  • companyCategories
  • includeCompanies / excludeCompanies
  • sort
  • maxItems
  • proxyConfiguration (optional Apify or custom proxy, applied to listing, detail and company requests; omitted or disabled means direct)

This Actor also adds role, since, maxPages, request retry controls, and direct pagination of public role pages.

For scalable results, specify a role, for example:

{
"role": "software engineer",
"remoteOnly": true,
"maxItems": 500,
"maxPages": 20
}

startUrls is useful when migrating an existing workflow or targeting a known Wellfound role/location URL.

Output

Each job row can include job ID/title/URL, full description, job type, compensation, parsed USD salary range, equity range, remote status and locations, posting time, company metadata and explicit source/coverage telemetry.

The Actor marks missing source fields as partial instead of inventing values. Source failures are emitted as free error rows.

Source boundary

The launch path uses public Wellfound HTML and the embedded __NEXT_DATA__ Apollo cache. It does not require login/session cookies or private APIs. Website behavior can change; the Actor exposes source diagnostics and retry counts so scheduled users can detect degradation.

Incremental delivery

Set incrementalMode=true with a unique stateKey for a stable search/filter scope. scanMaxItems controls scan depth independently of the delivery maxItems. Only successfully delivered NEW/UPDATED jobs are committed to the named store wellfound-delivered-state; undelivered changes are eligible next run. emitExpired=true emits EXPIRED identities only when source coverage passes the expiry gate described below. Traversing all pages alone does not establish complete coverage. Insufficient coverage suppresses both emitted EXPIRED records and internal expiry for skipReposts, while NEW/UPDATED delivery continues. Use serial schedules for the same stateKey: no atomic lock is available, and a crash between delivery and state commit can cause duplicates (at-least-once delivery). Source changes outside the scan are not detectable. Changing filters requires a new stateKey.

Additional output fields

The original job fields remain available. Each job also includes id, portalUrl, detailUrl, salaryMin, salaryMax, salaryEquityMin, salaryEquityMax, locationNames, companyTagline, and descriptionMarkdown. Equity aliases are percentage strings; the original numeric fields remain numeric. yearsExperienceMin and yearsExperienceMax come directly from public page data, including zero. Missing source values remain null.

visaSponsorship uses a source boolean when available, otherwise a conservative explicit statement in the description. visaSponsorshipSource and visaSponsorshipEvidence show how the result was obtained. Ambiguous or conflicting statements return null. This describes the listing, not immigration eligibility. extractedUrls contains unique explicit HTTP(S) links in description order, retaining source paths and trailing slashes. It does not fetch those links. These additions are not a promise of full compatibility with every competitor field. Existing incremental state may emit one UPDATED record after this schema upgrade because new job fields participate in content fingerprints.

Additional migration inputs

roles and searchUrls union multiple public sources, deduplicating by job ID. Explicit startUrls takes precedence. remote, maxResults, salaryMin, salaryMax, and fromDate are compatibility inputs; explicitly supplied remoteOnly, maxItems, minSalary, maxSalary, and since take precedence. maxResults=0 uses a bounded 5,000-row ceiling, with maxPages still applied. visaSponsorship=true requires explicit support; false removes explicit refusal and retains unknowns. Omit it to disable that filter.

descriptionFormat selects description fields without changing source text: text keeps description, markdown keeps descriptionMarkdown, and html keeps source HTML only (null with a warning when unavailable). all retains the existing representation. excludeEmptyFields removes null/empty values while retaining false and zero. Output formatting does not modify incremental state fingerprints. Unsupported formats fail before requests or billable job output. These output-format controls do not themselves change query routing, enrichment or notification settings.

Additional migration controls

query adds a free-text jobs URL alongside roles and searchUrls (explicit startUrls takes precedence). excludeLocations, includeKeywords / excludeKeywords with field selection, experienceLevel, equityMin, toDate, and maxAgeMinutes filter the fetched jobs. Unknown experience is retained; unknown equity is excluded when an equity minimum is requested. Date-only toDate includes the entire UTC day; the age cutoff is fixed once per run.

compact emits the eight core job fields; descriptionMaxLength truncates presentation fields only. Canonical incremental hashes remain unchanged. emitUnchanged includes unchanged jobs in incremental mode with changeType: UNCHANGED, and state still advances only for delivered jobs. New filters at their inactive defaults preserve existing state scopes; changing an active filter requires a new stateKey.

location now accepts the competitor's array of location slugs, generating each role Γ— location combination and deduplicating jobs across sources. Use locationText for the earlier single-string form. customFilters accepts declarative includes/notIncludes/equals/notEquals/gt/gte/lt/lte rules, including dotted field paths; numeric comparisons exclude unknown data. No executable expressions are evaluated.

companyOnlyMode aggregates accepted jobs into one record per company, with jobCount, sampleJobTitles and jobIds. Counts describe observed jobs in this run, not all openings at the company. maxResults bounds jobs before aggregation; delivery accounting counts company records actually pushed. Incremental mode is ignored in this mode with an explicit warning, without reading or changing its saved state. Compact company output retains company identity and aggregates.

skipReposts uses content fingerprints of previously delivered jobs confirmed expired only after the source coverage gate passes. It suppresses a new ID with matching title, company, description, job type, locations and compensation. Insufficient coverage prevents both EXPIRED output and internal expiry. If notifications are disabled, eligible expirations can update internal state only. The fingerprint archive is bounded to 20,000 entries; legacy state is retained conservatively until refreshed through delivery.

Radius filtering supports originLocation / cityName, lat + lon, radiusMiles and radiusKm (positive miles take precedence). It uses the offline GeoNames gazetteer via geonamescache without a paid geocoding API. Distances are between city centres, not exact office addresses. Unknown origins skip filtering with a warning; unresolved job locations are excluded when the origin is known, with the exclusion count in OUTPUT. cityName takes precedence over coordinates. For duplicate city names, the most populous match is selected; a country/state code suffix can narrow it, e.g. Paris, FR.

Optional notifications

Telegram, Slack, Discord, WhatsApp and generic JSON webhooks accept the competitor's notification input names. No notification request is made without a configured destination. Only successfully delivered dataset rows are eligible; notificationLimit defaults to 5 (maximum 20), and notifyOnlyChanges restricts incremental notifications to NEW/UPDATED records. Generic webhooks receive {metadata, items}.

Adapters make one attempt per destination with a 15-second timeout, matching the reference webhook contract. OUTPUT reports api_accepted or failed without credentials or endpoint URLs; acceptance is not proof the recipient read or received the message. A failure retains all scraped data in the dataset, and is not automatically retried. Redirects are refused so credentials are not forwarded to another endpoint. Slack/Discord payloads suppress unexpected mentions from scraped text. WhatsApp requires the customer's own eligible business account and messaging session; API version is configurable.

Notification adapters have been validated with injected transports; end-to-end customer-channel delivery is not established. An API acceptance status does not guarantee receipt.

enrichDetail / includeDetails attempts the public job detail page and reads identity-matched JobPosting metadata or SSR details. enrichCompany attempts public company SSR fields. Missing or blocked deep fields stay null, original listing content is retained, and status/warnings identify incomplete enrichment. Requests are cached per job/company within the run. Currency/period are taken only from explicit structured source fields, never guessed from a currency symbol.

appConnector accepts an Apify-managed MCP connector and writes one Notion page (notion-create-pages) or issue (create_issue, with mcpIssueTeam) summarizing the delivered results. The proxy uses the run token; upstream service credentials remain with Apify. Tool discovery is paginated, tool arguments are validated against the advertised schema, and no other tool names are called. Unsupported tools, missing teams and unavailable connector runtime are reported without losing dataset rows. See Apify's connector development guide.

Connector behavior has been validated with mocked sessions. End-to-end writes to a customer connector are not established and require the customer’s configured connection.

Configured proxies are used for every Wellfound request in the run. A proxy initialization failure stops the run instead of silently switching to direct access. OUTPUT reports requestRouting=direct|proxy; proxy URLs and credentials are never included. This does not guarantee source access from every proxy provider.

Company field coverage

Company output retains companyUrl and the explicit public companyBadges. companyFieldCoverage and companyMissingFields report availability across the 11 deep company fields, independently of coverage=observed_jobs_only (the job count scope). parsed means some enrichment data was parsed, not that all fields are available. Company aggregation retains companyEnrichmentStatuses and company enrichment warnings, including source failures and missing company URLs.

Company SSR matching accepts Startup and StartupResult entities, requires an explicit ID or slug, rejects conflicting identities, and caches by URL plus identity. Explicit deep fields in listing SSR are retained without an extra request. Existing nonempty listing values survive company enrichment. Unknown fields remain null/empty; unresolved Apollo references are not exported. Stage badges and job descriptions are not used to guess funding, founders or industry.

Successful retrieval of every deep-company field is not established. Blocked or missing enrichment remains explicit; no account or challenge bypass is used.

Filter contracts and URL precedence

salaryMin compares the job's upper salary bound with the requested minimum; salaryMax compares its lower bound with the requested maximum (range intersection, equality included). A minimum requires a known upper bound. The maximum alias only excludes a known lower bound above its ceiling; unknown salary still obeys includeNoSalary. minSalary and maxSalary retain the older lower/upper-bound containment semantics and override their respective aliases, including explicit zero. Active alias semantics participate in incremental scope hashing; reuse of an old scope with changed semantics requires a new state key.

fulltime and parttime normalize to full-time and part-time. maxPages accepts 1–200. Traversal stops at the source last page or result cap, whichever comes first; the configured ceiling does not promise that many source pages.

compatibilityMode accepts auto (default), blackfalcon, or crawlerbros. Auto selects Blackfalcon URL semantics when a Blackfalcon-specific input is present (remote, salaryMin, salaryMax, roles, query, searchUrls, array location, or an unhyphenated job type). Ambiguous inputs retain legacy behavior; use an explicit mode for mixed-client inputs.

In Blackfalcon mode, nonempty startUrls overrides query/role/location/remote search constraints, including jobTitle and keyword. Employment, salary, visa, radius, date, exclude-location, include/exclude-keyword and custom post-fetch filters remain active. This distinction follows the reference's separate search routing and job/post-fetch filter sections. Pasted searchUrls ignore location/remote only for those sources, including subsequent pages; role-generated sources in the same run still apply them. Crawlerbros mode keeps URL plus client-side filtering.

Source selection diagnostics

SSR role searches are selected by the requested page/role/remote scope instead of the first Apollo cache entry. A conflicting or ambiguous search cache fails explicitly. SOURCE_DIAGNOSTICS now records per-page requested URL, HTML SHA-256, selected search key, reported company page size/count, parsed job IDs and accepted job IDs, plus fetch status/final URL. This adds no requests and does not expand maxPages or mix a default remote source into a pasted URL.

maxResults is a filtered delivery budget, including in Blackfalcon compatibility mode. maxPages stays an explicit page ceiling; this Actor does not copy undocumented competitor page expansion. Set maxPages=2 to request up to two pages. Unknown source booleans remain null rather than being inferred as false.

Source coverage and conservative expiry

Every run, including non-incremental collection, reports OUTPUT.sourceCoverage (also exposed as coverage), expirySuppressedReason, and emitExpiredRequested. scanComplete retains its traversal/budget meaning; it does not claim that all source jobs were observed. sourceCoverage.expiryEligible requires traversal success, raw unique job IDs matching each source's declared total, every page from 1 through its declared last page, consistent per-page totals/page counts, selected search metadata, complete company identities, and no repeated job or company IDs within that source. Missing evidence fails closed. Counts are before user filters and delivery limits. Sources are checked separately; overlap between different sources does not by itself invalidate coverage.

SOURCE_DIAGNOSTICS.sources[].pages[] includes reportedTotalJobCount, reportedPageCount, and sourceCompanyIds. Request cacheHeaders persist only date and age response headers when present; cookies and other response headers are not stored. Cache timing is explanatory, not proof of complete coverage. A passing gate means these necessary checks passed, not an atomic server snapshot or independent confirmation that an individual vacancy closed.

Legacy state without the required page evidence cannot authorize expiry. Natural job expiry has not been established through long-term live observation.

For a small incremental workflow, run this input serially with a unique stateKey shared only by that schedule. The first run seeds delivered state; later runs return eligible changes.

{
"startUrls": ["https://wellfound.com/role/r/software-engineer"],
"maxItems": 5,
"maxPages": 1,
"scanMaxItems": 100,
"incrementalMode": true,
"stateKey": "well-expiry-smoke-REPLACE-WITH-UNIQUE-ID",
"emitExpired": true,
"skipReposts": true,
"requestDelayMillis": 0
}

For this multi-page source a one-page scan is insufficient for expiry and must produce zero EXPIRED records. A later run can still return NEW records because only successfully delivered observations are committed. Keep each state key scoped to one stable search and filter set. This example does not force a disappearance or demonstrate natural job closure.

Support

Open an issue from this Actor’s Issues tab with the run ID, expected result and a redacted input. Do not include tokens, cookies or private credentials. Include the output profile and affected field when reporting a migration mismatch.

Presentation update

The current listing adds workflow examples, grouped input documentation, output-field explanations and plan-based result discounts. Source-specific limitations above still apply. This is an independent tool and is not affiliated with the source platform.