{
  "openapi": "3.0.1",
  "info": {
    "title": "Wellfound (AngelList) Startup Jobs Scraper Comp, Equity & Email",
    "description": "[Only $0.99💰] Wellfound (AngelList) jobs scraper — paste any /jobs URL or filter by market/industry/location for flat rows: title, compensation band, equity, remote, posted date + company name, size, funding stage & market tags. Optional contact-email enrichment. No start fee — pay per job row.",
    "version": "0.0",
    "x-build-id": "iElIQZxrAk3W2FCBN"
  },
  "servers": [
    {
      "url": "https://api.apify.com/v2"
    }
  ],
  "paths": {
    "/acts/memo23~wellfound-jobs-scraper/run-sync-get-dataset-items": {
      "post": {
        "operationId": "run-sync-get-dataset-items-memo23-wellfound-jobs-scraper",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/acts/memo23~wellfound-jobs-scraper/runs": {
      "post": {
        "operationId": "runs-sync-memo23-wellfound-jobs-scraper",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor and returns information about the initiated run in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/runsResponseSchema"
                }
              }
            }
          }
        }
      }
    },
    "/acts/memo23~wellfound-jobs-scraper/run-sync": {
      "post": {
        "operationId": "run-sync-memo23-wellfound-jobs-scraper",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "inputSchema": {
        "type": "object",
        "properties": {
          "startUrls": {
            "title": "Wellfound URLs — /role/… recommended (optional)",
            "type": "array",
            "description": "Paste one or more Wellfound URLs. Two surfaces work, and they are very different:\n\n**✅ Role search — use this for volume and real filtering**\n• `https://wellfound.com/role/r/software-engineer` — every job for a role\n• `https://wellfound.com/role/l/product-manager/new-york` — role + location\n\nThese are genuinely paginated and genuinely filtered server-side (a single role query can hold **1,800+ jobs across ~47 pages**), and every row comes back with the **full job description**, structured employment type and years-of-experience — no enrichment needed.\n\n**⚠️ Landing page — limited**\n• `https://wellfound.com/jobs` (with or without `?market=` / `?industry=` / `?location=`)\n\nThis is an SEO landing page: it returns the same **~50 curated jobs** whatever filters or `?page=` you give it. Fine for a quick sample, useless for coverage.\n\n**❌ Not supported (Wellfound blocks these for non-browser clients):**\n• `/jobs/{id}-{slug}` · `/company/{slug}`\n\nRows are de-duplicated by job ID across the whole run, so overlapping URLs never bill twice.\n\n**Leave empty to use the structured filter fields below instead.**",
            "items": {
              "type": "string"
            }
          },
          "keyword": {
            "title": "Keyword(s) — comma-separated for OR logic",
            "type": "string",
            "description": "Case-insensitive substring match across job title, role slug, company name, market tags, location tags, badge labels, and (when enrichment is on) job description. Rows that don't match are filtered out before being written to the dataset.\n\nExamples:\n• `rust` → keeps rows mentioning Rust\n• `rust, golang, scala` → keeps rows mentioning ANY of the three (OR logic)\n• `senior` → keeps senior-level roles\n\n**How well this works depends on your start URL.** On a **`/role/…`** URL the scraper searches thousands of jobs and the full job description is included in the match, so even a narrow keyword has a real corpus to hit. On a **`/jobs`** URL it is filtering a curated set of only ~50 jobs, so a narrow keyword (`Cloud Security GRC Specialist`) will usually match nothing at all — not a failure, just a tiny pool. The run log says so explicitly when it happens.\n\n**Cost behaviour**: filtering happens after fetching, so compute is unchanged — but result charges only fire for rows that pass the filter. You never pay for rows you filter out, and `maxItems` counts kept rows, not fetched ones.\n\nLeave empty to disable filtering."
          },
          "remoteOnly": {
            "title": "Remote jobs only",
            "type": "boolean",
            "description": "Keep only jobs flagged remote. Applied after fetching, before charging — you don't pay for filtered-out rows.",
            "default": false
          },
          "minSalary": {
            "title": "Minimum salary (USD / year)",
            "minimum": 0,
            "type": "integer",
            "description": "Keep only jobs whose compensation band reaches at least this amount, e.g. 120000. Jobs with no listed compensation are kept (never dropped just for missing pay). Applied before charging."
          },
          "maxSalary": {
            "title": "Maximum salary (USD / year)",
            "minimum": 0,
            "type": "integer",
            "description": "Keep only jobs whose compensation band starts at or below this amount, e.g. 250000. Jobs with no listed compensation are kept. Applied before charging."
          },
          "jobTitle": {
            "title": "Job title contains",
            "type": "string",
            "description": "Keep only jobs whose <b>title</b> contains this text (case-insensitive). Narrower than <b>Keyword</b>, which also searches company name, tags and description. Comma-separated for OR: <code>engineer, developer</code>."
          },
          "jobType": {
            "title": "Employment type",
            "enum": [
              "any",
              "full-time",
              "part-time",
              "contract",
              "internship"
            ],
            "type": "string",
            "description": "On **`/role/…`** URLs this matches Wellfound's own structured employment-type field, so it is exact. On **`/jobs`** URLs no such field exists, so it falls back to matching the job title/role text.",
            "default": "any"
          },
          "experience": {
            "title": "Seniority",
            "enum": [
              "any",
              "junior",
              "mid",
              "senior",
              "executive"
            ],
            "type": "string",
            "description": "On **`/role/…`** URLs Wellfound supplies structured years-of-experience on many rows, which this uses when present. Otherwise (and on `/jobs` URLs) it matches job-title keywords — senior/lead/principal, head of/VP/chief, junior/entry-level.",
            "default": "any"
          },
          "includeNoSalary": {
            "title": "Include jobs without salary",
            "type": "boolean",
            "description": "Wellfound publishes many roles with no compensation shown. Leave on to keep them; turn off to get only jobs with pay data.",
            "default": true
          },
          "companyCategories": {
            "title": "Company categories",
            "type": "array",
            "description": "Keep only companies whose market tags, one-liner or business type match any of these (case-insensitive). Example: <code>saas</code>, <code>fintech</code>.",
            "items": {
              "type": "string"
            }
          },
          "includeCompanies": {
            "title": "Only these companies",
            "type": "array",
            "description": "Allow-list of company names or Wellfound slugs (case-insensitive substring match).",
            "items": {
              "type": "string"
            }
          },
          "excludeCompanies": {
            "title": "Exclude these companies",
            "type": "array",
            "description": "Block-list of company names or slugs. Takes precedence over the allow-list above.",
            "items": {
              "type": "string"
            }
          },
          "sort": {
            "title": "Sort by posted date",
            "enum": [
              "none",
              "newest",
              "oldest"
            ],
            "type": "string",
            "description": "Orders the <b>whole run</b> (not just each page) — the actor buffers all rows before writing when sorting is on. Jobs with no posted date are placed last.",
            "default": "none"
          },
          "market": {
            "title": "Market filter",
            "type": "string",
            "description": "Wellfound market slug. Examples: `ai`, `fintech`, `web3`, `saas`, `cyber-security`, `developer-tools`, `mobile`, `e-commerce`, `consumer`, `b2b`. Used to build `https://wellfound.com/jobs?market={value}` when no `startUrls` is provided. See the section note above — Wellfound currently ignores this filter server-side."
          },
          "industry": {
            "title": "Industry filter",
            "type": "string",
            "description": "Wellfound industry slug. Examples: `real-estate-tech`, `cyber-security`, `blockchain-cryptocurrency`, `health-care`, `education`, `gaming`. Used to build `https://wellfound.com/jobs?industry={value}` when no `startUrls` is provided. Note: `market` and `industry` overlap; pick one."
          },
          "location": {
            "title": "Location filter",
            "type": "string",
            "description": "Wellfound location slug. Examples: `san-francisco`, `new-york`, `london`, `berlin`, `united-states`, `remote`, `europe`. Used to build `https://wellfound.com/jobs?location={value}` when no `startUrls` is provided. Can be combined with `market` or `industry` (e.g. `market: ai` + `location: san-francisco` → `https://wellfound.com/jobs?market=ai&location=san-francisco`)."
          },
          "maxItems": {
            "title": "Max jobs per listing URL",
            "minimum": 1,
            "type": "integer",
            "description": "Upper bound on job rows emitted **per startUrl** — a ceiling, not a target. Each row is one paid event, and you are only ever charged for **unique** jobs.\n\nHow much data actually exists depends entirely on the URL you paste:\n• **`/role/…` URLs** — thousands. `/role/r/software-engineer` reports 1,855 matching jobs across 47 pages; the scraper reads the page's own page count and stops exactly at the end. Raise `maxItems` here and you really do get more.\n• **`/jobs` URLs** — roughly **50 unique jobs**, and the same 50 regardless of `?page=` or any filter. Setting `maxItems: 5000` on a `/jobs` URL still returns ~50; the scraper stops as soon as a page yields nothing new rather than billing you for repeats.\n\nDefault 1000. Free-tier users are additionally capped at 100 total rows across the whole run.",
            "default": 1000
          },
          "maxConcurrency": {
            "title": "Max parallel requests",
            "minimum": 1,
            "maximum": 10,
            "type": "integer",
            "description": "Parallel HTTP requests across listing URLs. Wellfound's listing surface is friendly; concurrency 3-5 is the sweet spot. Higher values risk rate-limiting on the residential proxy pool, not on Wellfound's edge.",
            "default": 4
          },
          "maxRequestRetries": {
            "title": "Max request retries",
            "minimum": 0,
            "type": "integer",
            "description": "Per-URL retry budget. Each retry rotates the proxy session with mild exponential backoff and re-races three HTTP stacks (impit Firefox / gotScraping / impers Chrome142). Default 10 — Wellfound's listing surface is reliable enough that ≤3 retries usually suffice, but the budget is there for proxy hiccups.",
            "default": 10
          },
          "enrichCompanyProfile": {
            "title": "Add company funding + founders + social URLs  (extra +$1.50 per 1,000 unique companies enriched)",
            "type": "boolean",
            "description": "💰 **Pricing**: ADDITIONAL $1.50 per 1,000 unique companies enriched — on top of the base $1.50/1k row charge. De-duplicated: a company with 56 jobs = 1 enrichment charge, not 56. Failed enrichments are NOT charged. So a 1,000-row run across 150 unique companies costs ~$1.50 (rows) + ~$0.18 (120 of 150 successfully enriched) = ~$1.68 total.\n\nEvery job row also includes the company's:\n\n• Total funding raised (e.g. Airbnb: $11.3B)\n• Funding-round history — Series A/B/C, IPO, dates\n• Founders — names, slugs, avatars (e.g. Brian Chesky, Joe Gebbia)\n• Social URLs — Twitter, LinkedIn, Facebook, blog, ProductHunt\n• Official company website\n• Y Combinator badge (when applicable)\n• Operating / incubator / shell flags + Wellfound's profile-completeness score\n\nBonus: for jobs whose company is successfully enriched, rows ALSO get job-type, equity, required years of experience, ATS source, and (when Wellfound exposes it) the recruiting contact's identity — all free with this toggle.\n\nReliability: typically 70-90% of unique companies enriched per run.",
            "default": false
          },
          "enrichJobDetail": {
            "title": "Add full job description  (legacy — /jobs URLs only)",
            "type": "boolean",
            "description": "⚠️ **You probably don't need this any more.** Jobs scraped from a **`/role/…`** search URL already include the **full job description**, structured employment type and years-of-experience, at no extra charge — this toggle does nothing for them and they are never billed for it.\n\nIt remains only for **`/jobs`** landing-page URLs, where the description is genuinely absent. That surface is DataDome-protected and success there has been measured near 0%, so expect little. Failed enrichments are never charged.\n\n💰 Pricing when it does fire: $1.50 per 1,000 jobs enriched, on top of the base row charge.\n\n**Recommended instead:** paste a role URL such as `https://wellfound.com/role/r/software-engineer` and leave this off.",
            "default": false
          },
          "enrichEmails": {
            "title": "Enrich with company contact emails (experimental, billed per email)",
            "type": "boolean",
            "description": "If enabled, tries to find a contact email for each company by reading its website's contact/about pages (e.g. info@, careers@). Adds contactEmail + contactWebsite columns plus a detailed emailEnrichment object. Best-effort, billed per contact email found; only charged when an email is returned, never for misses.",
            "default": false
          },
          "incrementalMode": {
            "title": "♻️ Incremental mode (only new/changed)",
            "type": "boolean",
            "description": "Track results across runs and only emit (and charge for) NEW, UPDATED, and REAPPEARED records. First run builds the baseline; schedule the same input daily/hourly and pay only for the diff.",
            "default": false
          },
          "stateKey": {
            "title": "🔑 State key",
            "type": "string",
            "description": "Stable name for the tracked search universe. Leave empty to derive one automatically from your search inputs. Set it explicitly if you tweak filters but want to keep the same history."
          },
          "emitUnchanged": {
            "title": "♻️ Also emit unchanged records",
            "type": "boolean",
            "description": "In incremental mode, also push records that haven't changed since the last run (each pushed record is charged as a normal result).",
            "default": false
          },
          "emitExpired": {
            "title": "⚰️ Emit expired records",
            "type": "boolean",
            "description": "In incremental mode, push a small tombstone record when a previously-seen result disappears (charged as a normal result).",
            "default": false
          },
          "skipReposts": {
            "title": "🚫 Skip reposts",
            "type": "boolean",
            "description": "In incremental mode, skip listings that look like reposts of previously-expired ones (same content under a new ID).",
            "default": false
          },
          "maxAgeMinutes": {
            "title": "⏱️ Max age (minutes)",
            "minimum": 1,
            "type": "integer",
            "description": "Only keep results newer than this many minutes (when the source exposes a posted date). Handy for tight scheduled runs, e.g. 70 for an hourly schedule."
          },
          "telegramBotToken": {
            "title": "🤖 Telegram bot token",
            "type": "string",
            "description": "Bot token from @BotFather. Required together with the chat ID for Telegram alerts."
          },
          "telegramChatId": {
            "title": "💬 Telegram chat ID",
            "type": "string",
            "description": "Chat or channel that receives the alerts, e.g. \"-1001234567890\" or \"@mychannel\". The bot must be a member."
          },
          "slackWebhookUrl": {
            "title": "💼 Slack webhook URL",
            "type": "string",
            "description": "Slack incoming-webhook URL (api.slack.com/messaging/webhooks)."
          },
          "discordWebhookUrl": {
            "title": "🎮 Discord webhook URL",
            "type": "string",
            "description": "Discord webhook URL (Server Settings → Integrations → Webhooks)."
          },
          "webhookUrl": {
            "title": "🪝 Generic webhook URL",
            "type": "string",
            "description": "Any endpoint (n8n, Make, Zapier, your API) — receives a JSON POST with run metadata, diff counts, and the notable records."
          },
          "webhookHeaders": {
            "title": "🔑 Webhook headers",
            "type": "object",
            "description": "Optional headers sent with the generic webhook POST, e.g. {\"Authorization\": \"Bearer xyz\"}."
          },
          "notificationLimit": {
            "title": "📊 Max items per notification",
            "minimum": 1,
            "maximum": 20,
            "type": "integer",
            "description": "How many records to list inside each alert message (1–20). Everything is always in the dataset regardless.",
            "default": 5
          },
          "notifyOnlyChanges": {
            "title": "🔕 Notify only on changes",
            "type": "boolean",
            "description": "With incremental mode on, stay silent when a run finds nothing new or updated.",
            "default": false
          },
          "proxy": {
            "title": "Proxy configuration",
            "type": "object",
            "description": "Leave as-is unless you have a reason to change it. Wellfound is US-centric — connections from non-US IPs are occasionally rerouted to a localized version of the site, so a US exit returns the canonical English listing most reliably. Residential groups work best but are only available on some Apify plans, so the default here is the automatic proxy, which every plan can use. If you have Residential access, selecting it (country US) is a small win.",
            "default": {
              "useApifyProxy": true
            }
          }
        }
      },
      "runsResponseSchema": {
        "type": "object",
        "properties": {
          "data": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "actId": {
                "type": "string"
              },
              "userId": {
                "type": "string"
              },
              "startedAt": {
                "type": "string",
                "format": "date-time",
                "example": "2025-01-08T00:00:00.000Z"
              },
              "finishedAt": {
                "type": "string",
                "format": "date-time",
                "example": "2025-01-08T00:00:00.000Z"
              },
              "status": {
                "type": "string",
                "example": "READY"
              },
              "meta": {
                "type": "object",
                "properties": {
                  "origin": {
                    "type": "string",
                    "example": "API"
                  },
                  "userAgent": {
                    "type": "string"
                  }
                }
              },
              "stats": {
                "type": "object",
                "properties": {
                  "inputBodyLen": {
                    "type": "integer",
                    "example": 2000
                  },
                  "rebootCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "restartCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "resurrectCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "computeUnits": {
                    "type": "integer",
                    "example": 0
                  }
                }
              },
              "options": {
                "type": "object",
                "properties": {
                  "build": {
                    "type": "string",
                    "example": "latest"
                  },
                  "timeoutSecs": {
                    "type": "integer",
                    "example": 300
                  },
                  "memoryMbytes": {
                    "type": "integer",
                    "example": 1024
                  },
                  "diskMbytes": {
                    "type": "integer",
                    "example": 2048
                  }
                }
              },
              "buildId": {
                "type": "string"
              },
              "defaultKeyValueStoreId": {
                "type": "string"
              },
              "defaultDatasetId": {
                "type": "string"
              },
              "defaultRequestQueueId": {
                "type": "string"
              },
              "buildNumber": {
                "type": "string",
                "example": "1.0.0"
              },
              "containerUrl": {
                "type": "string"
              },
              "usage": {
                "type": "object",
                "properties": {
                  "ACTOR_COMPUTE_UNITS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_WRITES": {
                    "type": "integer",
                    "example": 1
                  },
                  "KEY_VALUE_STORE_LISTS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_INTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_EXTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_SERPS": {
                    "type": "integer",
                    "example": 0
                  }
                }
              },
              "usageTotalUsd": {
                "type": "number",
                "example": 0.00005
              },
              "usageUsd": {
                "type": "object",
                "properties": {
                  "ACTOR_COMPUTE_UNITS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_WRITES": {
                    "type": "number",
                    "example": 0.00005
                  },
                  "KEY_VALUE_STORE_LISTS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_INTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_EXTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_SERPS": {
                    "type": "integer",
                    "example": 0
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}