# YC Jobs Lookup from $1/1k (`accountable_eel/yc-jobs-lookup`) Actor

Search Y Combinator startup jobs by role and get every matching posting: title, company, YC batch, location, salary if listed, and the apply link. Filter by job title, location, or remote-only, or watch a role for postings new since your last run. Charged per posting, never per search.

- **URL**: https://apify.com/accountable\_eel/yc-jobs-lookup.md
- **Developed by:** [Adrian Voss](https://apify.com/accountable_eel) (community)
- **Categories:** Jobs, Lead generation
- **Stats:** 1 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.50 / 1,000 job posting returneds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## YC Jobs Lookup: Y Combinator Startup Jobs Scraper

You give this actor a list of role categories — software engineer, designer, product manager,
and so on. For each one, it reads Y Combinator's own **WorkAtAStartup** job board — the site
every YC-backed company posts its openings to — and returns every open posting in that category:
title, company, YC batch, location, salary if listed, and the apply link, one row per posting.
No login, no headless browser: the board's own page ships the job list as a JSON payload
alongside the HTML, and this actor reads that directly. Optional filters narrow results by job
title, hiring location, or remote-only, and a delta mode returns only postings that appeared
since your last run.

### Who it's for

Anyone building a YC-company lead list or candidate pipeline without clicking through 30 postings
by hand — recruiters sourcing at funded startups, founders scouting who's hiring (and who might be
a customer), and investors or analysts tracking hiring velocity across the YC portfolio as a
signal. It also suits an outbound list: "every YC startup currently hiring a product manager" is a
company list with a reason to reach out attached. Pricing is pay-per-event and per-posting: a
fraction of a cent per returned row, and nothing for a role category with no open postings, so
watching a quiet category costs you next to nothing.

### Why this one

- **Reads WorkAtAStartup's own job data, not a scrape of rendered text.** The board serves its job
  list as a JSON payload embedded in the page (an Inertia.js `data-page` attribute) — this actor
  parses that directly, so results don't depend on brittle CSS selectors that break on a redesign.
- **You pay per posting returned, not per search.** A role category with 30 open postings and one
  with 2 both bill only for the postings actually delivered — a category with none is a free miss.
- **Honest about being a preview, not the whole board.** WorkAtAStartup's logged-out view caps each
  role category at roughly 25-30 postings with no further pagination — see "Tips" below for what
  that means and doesn't mean.
- **A delta mode for watching a category over time.** Turn on `onlyNewSinceLastRun`, put the actor
  on a schedule, and every run after the first returns only postings you haven't seen — a quiet
  week still gets a row, so you can tell "nothing new" from "the run broke."

### What you get

One row per matching job posting by default (toggle "One row per job posting" off in the Input
tab to get one row per role category instead, with the full list nested in `jobs`). Every row
carries these fields, whether or not you've turned on filters or delta mode — the columns never
move:

| Field | Type / format | Description |
| --- | --- | --- |
| `query` | text | The role category you passed in, unchanged. |
| `found` | boolean | `true` if the category matched at least one open posting, `false` otherwise. |
| `status` | text | `OK` or `NOT_FOUND` (no postings currently match this role category). |
| `role` | text | The role category actually searched (normalized to WorkAtAStartup's slug format). |
| `jobCount` | number | How many postings matched, after your filters (and after delta filtering, if `onlyNewSinceLastRun` is on). |
| `totalJobCount` | number | How many postings WorkAtAStartup returned for this role before any filtering — lets you see what a filter dropped. |
| `newJobs` | number | How many postings are new since your last scheduled run. Empty unless `onlyNewSinceLastRun` is on. |
| `firstRun` | boolean | `true` if this was the baseline run for this watchlist. Empty unless `onlyNewSinceLastRun` is on. |
| `jobs` | array | The full list of matching postings, each with the fields below. Present in every row; it's what gets expanded into separate rows in "one row per posting" mode. |
| `jobId` | text | WorkAtAStartup's own posting ID. |
| `title` | text | Job title. |
| `company` | text | Hiring company. |
| `companyBatch` | text | The company's YC batch, e.g. `W26`, `S14`. |
| `companySlug` | text | The company's WorkAtAStartup slug. |
| `companyOneLiner` | text | The company's one-line description, as listed on its profile. |
| `jobType` | text | Employment type, e.g. `Fulltime`. |
| `roleType` | text | WorkAtAStartup's own sub-type for the role, e.g. `Full stack`, `UI / UX`. |
| `location` | text | Where the role is based, as listed — `"Remote"`, a city, or several cities joined by `/`. |
| `remote` | boolean | `true` if `location` reads as remote. |
| `salaryText` | text | The salary range as listed (prose, e.g. `"$124K - $188K CAD"`) — not every posting lists one. |
| `applyUrl` | link | Link to apply (routes through YC's own sign-up flow). |
| `companyLastActiveAt` | text | How recently the company was last active on Bookface (YC's internal network) — a rough proxy for how actively they're checking applications, not the posting's own age. |
| `scrapedAt` | date (ISO) | When this actor fetched the row. |

A role category with no current matches still comes back as one row with `found: false`, and that
row is never charged.

### Price

$1 per 1,000 job postings, plus a $0.00005 start fee. Misses (`found:false`) are never charged.

1,000 postings through this actor: **~$1** (plus a $0.00005 actor-start charge), less on a paid
Apify plan. A role category with zero open postings is never billed — you only pay for postings
actually delivered.

### How to use

1. **In the Apify Console.** Open the actor page and click **Start** — the `roles` field is already pre-filled with a working example. Results land in the run's dataset as soon as each item is found.
2. **Via the API.** Call it directly with a POST request — no Console needed once you have an API token:
   ```bash
   curl "https://api.apify.com/v2/acts/accountable_eel~yc-jobs-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>" \
     -X POST \
     -H "Content-Type: application/json" \
     -d '{"roles":["software-engineer"]}'
   ```
3. **On a schedule.** Save this actor as an Apify **Task** with the input you want, then add a **Schedule** (hourly, daily, weekly) so it runs on its own — no server of your own required.

Paste one role category per line: `software-engineer`, `designer`, `recruiting`, `science`,
`product-manager`, `operations`, `sales-manager`, `marketing`, `legal`, or `finance` — these are
WorkAtAStartup's own top-level categories. Each one is matched against that category's job board.

To narrow the postings you get back, open the **🎯 Job filters** section of the Input tab. All of
these are optional and combine with AND across fields, OR within a field:

| Input | What it does |
| --- | --- |
| `titleKeywords` | Keep only postings whose title contains at least one of these — `["backend","platform"]`. Recommended for a broad category like `software-engineer`. |
| `excludeTitleKeywords` | Drop postings whose title contains one of these — `["intern","unpaid"]`. Applied after `titleKeywords`. |
| `locations` | Keep only postings whose location field contains one of these — `["San Francisco","New York"]`. Drops postings with no location listed at all — leave empty to keep them. |
| `remoteOnly` | Keep only postings whose location reads as remote. Most WorkAtAStartup roles are on-site or hybrid, so this usually removes rows. |

Leave every filter empty and you get every posting WorkAtAStartup returned for that role category,
exactly as before filters existed. `jobCount` (matched) and `totalJobCount` (before filtering) both
come back on every row, so a filtered result is never ambiguous.

For a standing watchlist, turn on **`onlyNewSinceLastRun`** in the **🔁 Watch for new postings**
section and put the actor on a schedule. The first run baselines (returns everything matching your
filters, `firstRun: true`); every run after that returns only postings it hasn't shown you before,
counted in `newJobs`. A quiet run still returns a row per role category (`jobCount: 0`,
`newJobs: 0`) so you can tell "nothing new" from "the run failed."

### Input

```json
{
  "roles": [
    "software-engineer"
  ]
}
```

One per line. Matched against WorkAtAStartup's own role categories: software-engineer, designer, recruiting, science, product-manager, operations, sales-manager, marketing, legal, finance. Add a "Job title must contain" filter below for tighter results.

### Sample output

One row per posting (default view). From a live run against `roles: ["software-engineer"]`:

| query | found | status | role | jobCount | totalJobCount | newJobs | firstRun | jobId | title | company | companyBatch | companySlug | companyOneLiner | jobType | roleType | location | remote | salaryText | applyUrl | companyLastActiveAt | scrapedAt |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| software-engineer | true | OK | software-engineer | 30 | 30 |  |  | 93806 | Senior Software Developer | Hive | S14 | hive | Marketing automation for event promoters (email, sms, ads, CRM) | Fulltime | Full stack | Remote | true | $124K - $188K CAD | https://account.ycombinator.com/authenticate?continue=... |  | 2026-09-08T01:17:10.000Z |

*(This table is hand-inserted from the pre-publish live verification run — it will be replaced by
a real generated sample the first time a canary task runs against this actor.)*

### Use it from Clay, n8n, Make, or an AI agent

This actor runs synchronously over plain HTTP — call it directly from a script, a workflow tool, or an AI agent, no Apify Console needed once you have an API token.

```bash
curl "https://api.apify.com/v2/acts/accountable_eel~yc-jobs-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>" \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{"roles":["software-engineer"]}'
```

**n8n.** Add an HTTP Request node: Method `POST`, URL `https://api.apify.com/v2/acts/accountable_eel~yc-jobs-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>`, Body Content Type `JSON`, JSON Body `{"roles":["software-engineer"]}` (swap in an expression from an earlier node for a real value).

**Clay.** Add an "HTTP API" column: Method `POST`, URL `https://api.apify.com/v2/acts/accountable_eel~yc-jobs-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>`, Body `{"roles":["{{role category}}"]}`, mapping the row's role category into the `roles` array.

**MCP.** In Claude, Cursor, or any MCP client with the Apify MCP server, ask for "YC Jobs Lookup | Apify" — the agent will find and run this actor.

### Tips

- **Add a `titleKeywords` filter on broad categories.** `software-engineer` is WorkAtAStartup's
  default/largest category and returns a wide mix of seniorities and specializations — narrow it
  to the titles you actually want.
- **This is a preview, not the whole board.** WorkAtAStartup's logged-out job pages return roughly
  25-30 postings per role category with no pagination in the anonymous view — verified live on two
  categories (30 for `software-engineer`, 25 for `designer`). A YC-backed company hiring in a
  category isn't guaranteed to appear if that category's preview page is full; there is no
  official count of how many postings exist beyond what the preview returns.
- **`salaryText` and `companyBatch` aren't always present.** Not every posting lists a salary
  range, and pre-seed or very early-batch companies sometimes have a sparser profile.
- **No posting date is available.** WorkAtAStartup's public payload carries `companyLastActiveAt`
  (the company's last activity on Bookface, YC's internal network) but no per-posting publish
  date — so this actor doesn't offer a `postedAfter` filter or a `postedAt` column. See the FAQ.

### vs. alternatives

| | What it costs | What you get | Trade-off |
|---|---|---|---|
| **This actor** (`yc-jobs-lookup`) | $0.001 per posting returned (FREE tier), $0.00005 actor start, nothing for a category with no matches | Every open posting WorkAtAStartup's public board shows for a role category — title, company, YC batch, location, salary if listed, apply link — filtered by title, location, or remote, with `onlyNewSinceLastRun` for deltas | Reads the same logged-out preview a browser sees: roughly 25-30 postings per category, no full-board count, no posting date. |
| **`parsebird/yc-jobs-scraper`** | Pricing not disclosed publicly; comparable job-scraper actors in this niche range $0.05-$2.50 per 1,000 results | The established incumbent for this niche — 118 users in the last 30 days, 4.06 rating (3 reviews) | If you already use it and it fits your workflow, there's no urgent reason to switch. This one is per-posting priced and documents its own preview-cap limitation up front. |
| Doing it yourself | Your time + finding and parsing the Inertia `data-page` JSON payload, handling role-slug routing, and re-checking the page structure as WorkAtAStartup changes its frontend | The same data | This actor's parsing, filters, delta mode, and retry handling are the maintenance burden it absorbs. |

Prices for third-party tools are their published list prices (where disclosed) as of September
2026 and are not tracked here — check the vendor before relying on the comparison.

### FAQ

**Why does `found` say false, or a row come back empty?**
The role category currently has zero open postings in WorkAtAStartup's public view
(`status: NOT_FOUND`). Try a different category, or check the site directly — a category can also
be temporarily empty between hiring waves.

**Was I charged for that row?**
Only for postings actually returned — `jobCount` postings per role category, at the per-posting
price. A category matching zero postings is free.

**I got charged for a role category that returned `jobCount: 0` — why?**
That can only happen with filters or delta mode: WorkAtAStartup returned postings
(`totalJobCount` > 0), but your `titleKeywords`/`locations`/`remoteOnly` filtered all of them out,
or none were new since your last delta run. Zero *rows* means zero *charge* either way — this
question only applies to the "one row per role category" display mode, where a category still
gets a summary row even when its `jobs` list is empty.

**Why is there no `postedAt` or salary-as-a-number field?**
WorkAtAStartup's public job payload doesn't include either. The only date-like field it exposes is
`companyLastActiveAt` (the company's own last activity, not the posting's age), and salary is a
prose range (`salaryText`) rather than min/max numbers — some postings omit it entirely. Rather
than fabricate a value the source doesn't provide, both are left out or reported as-is: see
docs/ats-filters.md's "Auditing a port" rule, which this actor follows even though it isn't an ATS
actor.

**Does this return every YC startup's job, or just a sample?**
A sample. This reads the same logged-out preview page a browser without a WorkAtAStartup account
sees, capped at roughly 25-30 postings per role category with no further pages available. It's
current and accurate for what it shows, but it isn't a claim of full board coverage.

**How does the delta / "new since last run" state work, and can I reset it?**
It lives in a named key-value store, `yc-jobs-lookup-state`, inside your own Apify account. Delete
that store to reset every watchlist. Changing your filters also starts a fresh watchlist
automatically, since filter values are part of what identifies a given seen-list.

**Does this actor hit rate limits or need a proxy?**
It calls WorkAtAStartup's own public pages once per role category, through Apify's default proxy,
with its own timeout and retry handling. No special proxy configuration is required.

**Can an AI agent call this directly?**
Yes — it's registered with the Apify MCP server. See "Use it from Clay, n8n, Make, or an AI agent"
above.

### Related actors

- [Greenhouse Jobs Lookup](https://apify.com/accountable_eel/greenhouse-jobs-lookup) — the same
  per-company job-board pattern for any company hiring on Greenhouse, YC-backed or not.
- [ATS Jobs Unified Lookup](https://apify.com/accountable_eel/ats-jobs-unified-lookup) — checks a
  company against six different ATS platforms at once, including a hiring-signal mode.
- [RemoteOK Jobs Lookup](https://apify.com/accountable_eel/remoteok-jobs-lookup) — the same
  per-posting pricing model for RemoteOK's broader, non-YC-specific remote job feed.

# Actor input Schema

## `roles` (type: `array`):

One per line. Matched against WorkAtAStartup's own role categories: software-engineer, designer, recruiting, science, product-manager, operations, sales-manager, marketing, legal, finance. Add a "Job title must contain" filter below for tighter results. You're only charged for the ones we actually find — a miss costs nothing.

## `testRun` (type: `boolean`):

Turn this on to test your input on a small sample before running the full list. Turn it off to process everything.

## `onlyFound` (type: `boolean`):

Only keep rows where something was actually found. Misses are always free, whether or not you show them here.

## `includeKeywords` (type: `array`):

Optional. Only keep results that mention at least one of these words (e.g. a job title, a city, a product name). Leave empty to keep everything.

## `excludeKeywords` (type: `array`):

Optional. Drop any result that mentions one of these words. Leave empty to skip nothing.

## `maxResults` (type: `integer`):

Optional. Stop the run once this many results have been found — useful for a quick, cheap sample. Leave blank for no limit.

## `titleKeywords` (type: `array`):

Recommended for a role like software-engineer that returns a broad mix. Keep only postings whose title contains at least one of these words. Case doesn't matter, partial words work. Leave empty to keep everything WorkAtAStartup returned for this role.

## `excludeTitleKeywords` (type: `array`):

Optional. Drop any posting whose title contains one of these words — e.g. "intern", "unpaid". Applied after the include list above, so a title matching both is dropped.

## `locations` (type: `array`):

Optional. Keep only postings whose location mentions one of these — e.g. "San Francisco", "New York", "Remote". Postings with no location at all are dropped by this filter, since they can't be shown to mention any specific place — leave this empty to keep them.

## `remoteOnly` (type: `boolean`):

Keep only postings whose location is remote. Unlike a remote-only job board, most WorkAtAStartup roles are on-site or hybrid, so this filter usually removes rows — check the Location column to see why a row was kept or dropped.

## `onlyNewSinceLastRun` (type: `boolean`):

Turn this on for a scheduled watchlist: the first run returns everything that matches and remembers it, and every run after that returns only postings it hasn't shown you before. A role with nothing new still gets a row (with 0 postings), so a quiet schedule never looks like a broken one. Changing the filters above starts a fresh watchlist.

## `columns` (type: `array`):

Choose which pieces of information to include in each result row. All are included by default.

## `expandRows` (type: `boolean`):

When on, each job posting found gets its own row instead of being grouped under its role. You're still only charged once per role, no matter how many rows it produces.

## `maxConcurrency` (type: `integer`):

Parallel requests. Keep conservative — this target has no browser fallback, so getting blocked costs more than slow-and-steady.

## `proxyConfiguration` (type: `object`):

Apify Proxy config. Residential recommended for anti-bot-sensitive targets.

## Actor input object example

```json
{
  "roles": [
    "software-engineer"
  ],
  "testRun": false,
  "onlyFound": false,
  "includeKeywords": [],
  "excludeKeywords": [],
  "titleKeywords": [],
  "excludeTitleKeywords": [],
  "locations": [],
  "remoteOnly": false,
  "onlyNewSinceLastRun": false,
  "columns": [
    "role",
    "jobCount",
    "totalJobCount",
    "newJobs",
    "firstRun",
    "jobs",
    "jobId",
    "title",
    "company",
    "companyBatch",
    "companySlug",
    "companyOneLiner",
    "jobType",
    "roleType",
    "location",
    "remote",
    "salaryText",
    "applyUrl",
    "companyLastActiveAt"
  ],
  "expandRows": true,
  "maxConcurrency": 5,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `results` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "roles": [
        "software-engineer"
    ],
    "includeKeywords": [],
    "excludeKeywords": [],
    "titleKeywords": [],
    "excludeTitleKeywords": [],
    "locations": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("accountable_eel/yc-jobs-lookup").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "roles": ["software-engineer"],
    "includeKeywords": [],
    "excludeKeywords": [],
    "titleKeywords": [],
    "excludeTitleKeywords": [],
    "locations": [],
}

# Run the Actor and wait for it to finish
run = client.actor("accountable_eel/yc-jobs-lookup").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "roles": [
    "software-engineer"
  ],
  "includeKeywords": [],
  "excludeKeywords": [],
  "titleKeywords": [],
  "excludeTitleKeywords": [],
  "locations": []
}' |
apify call accountable_eel/yc-jobs-lookup --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,accountable_eel/yc-jobs-lookup"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/SttZ3tWxTMuVqRf4t/builds/QoSI7WXdtvJNBKRXG/openapi.json
