Y Combinator Jobs — Enriched & Normalized
Pricing
from $0.90 / 1,000 y combinator job results
Y Combinator Jobs — Enriched & Normalized
Extract public Work at a Startup openings into nomad-agent-job-v1. Complete Inertia details provide descriptions, salary, equity, visa and company facts; optional owner-managed AI fills only missing description-backed fields.
Y Combinator Work at a Startup — normalized job Actor
This Actor reads complete YC Work at a Startup jobs from an owner-maintained
durable PostgreSQL inventory and emits strict nomad-agent-job-v1 records. It
is the normalized successor to the legacy flat
ycombinator-was-scraper; it deliberately retains
identity.source: "ycombinator_was" so downstream job keys remain stable.
This public Actor never calls Work at a Startup. An owner-only scheduled
ycombinator-private-collector performs fixed-scope discovery and identity-
bound detail retrieval, then stores complete normalized records in the Netcup
position inventory. Public runs fail closed if that inventory is unavailable
or stale; there is no live-source fallback. The public host owns user filters,
optional null-only enrichment, selected-field translation, transactional
delivery inventory, PPE billing, delivery, analytics, and RUN-SUMMARY.
Source and completeness boundary
Work at a Startup exposes a public JSON search endpoint and public Inertia job pages. The private collector's hourly task fans out over 62 fixed ordered terms, deduplicates by source job ID, and durably queues new, changed, or refresh-due cards. A separate detail task writes only recognized, complete, identity-bound jobs. Neither collector mode performs enrichment or translation.
A search card is never exposed by the public Actor. Every inventory row has a
recognized detail payload whose embedded job ID matches the requested URL and
whose descriptionHtml produces a non-empty complete description. The public
Actor scans a frozen inventory cursor and applies keyword, location, YC
taxonomy, normalized, work-arrangement, and unseen filters locally.
YC's session-bound authentication links contain regenerated application codes
and placeholder emails. They are neither used for candidate change detection
nor stored as durable application URLs; when no stable direct apply URL is
published, data.application.url points to the stable YC job page.
Input
The required shared contract marker is:
{"schemaVersion": "nomad-agent-job-search-input-v1","keyword": "founding engineer","postedWithin": "24h","maxItems": 25}
keyword is matched locally against cached normalized content. When blank, no
keyword filter is applied. Multiple local search terms and YC taxonomy filters
use the separately versioned extension:
{"schemaVersion": "nomad-agent-job-search-input-v1","postedWithin": "7d","ycSearch": {"schemaVersion": "nomad-agent-ycombinator-search-v1","queries": ["python", "founding engineer"],"companyBatches": ["S23", "W24"],"roleTypes": ["engineering"],"jobTypes": ["Full-time"]}}
A non-empty keyword and non-empty ycSearch.queries are mutually exclusive.
The queries array is an OR-list over cached content; unlike LinkedIn search
partitions, it does not create separate upstream requests and its order has no
meaning. companyBatches matches YC accelerator cohorts such as W24,
roleTypes matches substrings in YC's published role category, and jobTypes
matches YC's published job-type label with common full-time, part-time,
contract, and internship aliases normalized.
YC exposes no source posting timestamp. The owner inventory assigns
data.application.postedAt once, at the UTC hour when it first observes a new
job, and preserves that timestamp across detail refreshes. postedWithin
supports 1h, 24h, 7d, 30d, and any against that cache-admission time;
the omitted default is any, so a first search is not silently restricted to
jobs first observed during the last day.
companyLastActiveAt remains separate company metadata. Top-level orderBy
accepts newest or oldest and uses the same cache-admission hour, with
unknown times last. maxItems accepts up to 1,000; 0 means the bounded
1,000-result window, never unlimited.
Other shared inputs are location, workArrangements, dedupe, filters,
aiEnrichment, translateToEnglish, includeRaw, and analyticsEnabled.
firstRunMode is an explicit opt-in to the same five-result paid evaluation
preset as LinkedIn while
preserving all search, taxonomy, ordering, and normalized-filter choices.
Owner policy such as inventory freshness, query bounds, models, and credentials
lives in scraper.yaml, not caller input.
Output semantics
Every dataset row has exactly these roots:
schemaVersion, identity, data, custom, llm, raw
Static source facts win. Optional AI enrichment may fill only still-null
description-backed normalized paths. Translation touches only configured short
display fields. Raw job description text and HTML remain unchanged unless the
caller sets includeRaw: false, which produces raw: null after processing.
Important source rules:
nullmeans unknown, unavailable, or not safely parsed.[]means the complete detail explicitly supplied an empty collection.Remote (...)is an explicit remote signal; a physical location does not prove onsite or hybrid.- A dollar symbol alone does not prove USD, and an amount without period text does not prove annual compensation.
- One explicit salary amount maps to
compensation.exact; a range maps tominimum/maximum. - Founders remain company metadata and never become
hiringContacts. - YC
roleTyperemains a job function/custom role label, not seniority. - Skills remain source tags, with provenance in
custom.data.skills. When no stronger typed preference already exists, the Actor also mirrors non-empty tags todata.requirements.preferredSkillsas positive-only ranking context. It never asserts these generic tags asrequiredSkills. - Cleaned interview-process text maps to
data.application.selectionProcess;custom.data.interviewProcessHtmlpreserves the YC-authored rich text because the canonical application contract has no HTML sibling.
The versioned custom contract is
../job_custom_schemas/ycombinator-v2.schema.json.
It retains batch, slug, source location/job-type labels, raw equity/visa labels,
source skills, interview HTML, bounded opaque structured custom questions,
company hiring/technology descriptions, team size, last-active metadata, and
founder profiles. Role category, salary text, and interview-process text live
only in their canonical data fields.
YC company identity is already canonical where the shared contract has an
exact destination: name, numeric YC source ID, website, logo, and industry map
to data.company/data.classification. The remaining custom.data.company
facts are company-wide metadata—tagline, headquarters, team size, activity,
hiring/technology prose, and founders—with no safe v1 canonical destination.
The private collector already reads the complete YC company block from each
detail page, so LinkedIn's optional companyProfileEnrichment fetch is neither
needed nor exposed. LinkedIn's companyFilters also remain source-specific:
they filter facts from that optional LinkedIn company-page fetch, whereas
shared filters operate only on canonical normalized fields.
Run summary and retries
The Actor writes the closed, minimal nomad-agent-run-summary-v4 contract under
RUN-SUMMARY. It contains status, startedAt, finishedAt,
resultsLimited, delivered, and one bounded retry recommendation. It never
automatically starts another paid run and never falls back to YC when inventory
access fails. A deadline or summary-persistence failure fails the run; it cannot
be published as a successful empty result. The reviewed stage reserves leave
search time at a 600-second caller cap even when enrichment is enabled.
Local verification
From the repository root:
apify/.venv/bin/python \apify/ycombinator-enrich-translate-normalize-scraper/scripts/verify_core_wheel.pycd apify/ycombinator-enrich-translate-normalize-scraperPYTHONPATH=wheels/nomad_job_scraper_core-1.1.31-py3-none-any.whl \../.venv/bin/python -m pytest -qPYTHONPATH=wheels/nomad_job_scraper_core-1.1.31-py3-none-any.whl \../.venv/bin/python \scripts/generate_dataset_schema.py --check
After exporting one 600-second AI-enabled, translation-disabled canary as an
object containing run, input, items, and runSummary, verify terminal
classification plus complete text/HTML length and SHA-256 receipts with:
$python scripts/verify_canary_evidence.py canary-evidence.json
Local tests and schema checks do not authorize an Actor build, push, pricing change, deployment, or Store publication.