Resume-Aware Job Matcher
Under maintenancePricing
from $0.30 / actor start
Resume-Aware Job Matcher
Under maintenanceRank raw job postings against your resume keywords. Takes a batch of job rows plus your core/secondary/bonus skill buckets and returns a scored, filtered and ranked shortlist with match reasons.
Rank a batch of raw job postings against your resume and get back a scored, filtered, ranked shortlist — with the reason every job was kept or dropped.
This actor is a thin, data-driven wrapper around the job-hunt matching
engine (the same classifier that built a daily 9-month React/Software
Engineer/MERN shortlist). It takes the matched + dropped decision logic in
engine/ and exposes it over Apify's pay-per-result API, so you can plug it
into any job pipeline (a scraper's dataset, a CSV export, a webhook, or a
manual paste) without writing a single filter rule.
What it does
Given an array of raw job rows plus your skill buckets, the actor:
- Scores every job against your resume — weighted core / secondary / bonus buckets (60 / 30 / 10 by default).
- Rejects classes of junk using the production filters:
- mis-extracted / garbage titles
- excluded title keywords (QA, Android, iOS, DevOps, Java, Angular, …)
- senior / lead / principal titles
- JDs that declare a senior role
- required foreign stack in the JD (with the title-trust escape: a title that names your target stack survives a python/angular/java/etc. JD requirement; a generic title does not)
- wrong-stack titles
- experience-band mismatch (internships, fresher-only, and above your band)
- scam / engagement-farming / vendor-staffing patterns
- posting-age window (optional)
- Labels each keeper: region, discipline, experience band.
- Ranks the survivors by match score with region / band / discipline tie-breaks, collapses duplicates, and caps the shortlist.
- Charges pay-per-result — you are billed per ranked job delivered, not per run.
Input
| Field | Type | Required | Notes |
|---|---|---|---|
jobs | array | yes | Raw postings. Aliases for common fields (title/role/position, description/body, applyUrl/url, company, location, postedDate, experience, workMode, salary, email, phone, source). Field matching is case-insensitive. |
resumeCore | array | yes | Must-have skills (e.g. React, JavaScript, TypeScript, HTML, CSS) |
resumeSecondary | array | no | Strong signals (e.g. Redux, Node.js, Express, MongoDB) |
resumeBonus | array | no | Nice-to-have (e.g. Next.js, Jest, Vite) |
targetYears | number | no | Your experience, used for band ranking (default 3) |
minScore | integer | no | Drop below this score (default 25) |
maxResults | integer | no | Shortlist cap, 1–500 (default 50) |
maxAgeHours | integer | no | Drop jobs older than this when a date is present (0 = off) |
allowedRegions | array | no | Keep only locations containing any of these |
excludeTitleKeywords | array | no | Overrides the tuned title exclusions |
seniorTitleWords | array | no | Overrides the seniority markers |
jdStackRejects | array | no | Overrides JD-required foreign stacks |
wrongStack | array | no | Overrides wrong-stack words |
excludeInternships | boolean | no | Drop internship/trainee/apprentice (default true) |
acceptFreshers | boolean | no | Keep 0-experience roles (default false) |
includeDropped | boolean | no | Emit a per-job dropped report with reasons |
All list fields ship with sensible tuned defaults, so sending only jobs +
resumeCore gets you a working run.
Output
The dataset is populated with:
- one
ranked_jobrecord per keeper — rank,matchScore, title, company, location, region, discipline, band, matched/missing skills, apply URL, salary, email, phone, notes, and a trimmed description; - one
summaryrecord — counts,minScore,maxResults, top result; - when
includeDroppedis true, onedropped_reportrecord — counts by reason plus up to 200 per-job entries with their drop reason (great for tuning your input).
Pricing model
This actor charges per result: Actor.charge("ranked-jobs", count=kept).
You pay only for jobs that make the shortlist, so a huge input with few
matches is cheap and a dense input is priced per useful row. The Apify
platform revenue share pays the developer (you, on the published actor's
owner payouts). To tune price: pick a per-result event price in Pay per
event & result under the actor's monetization settings, sized against the
value of a matched opening (typical tier: $0.05–$0.20 per ranked result).
Note on the pay-per-event model: with rental-based revenue being retired (no new rentals after April 1, 2026), price via pay per event / pay per result — it is the long-lived revenue surface.
Local development
# 1. Create a venv and install depspython -m venv .venv.venv\Scripts\pip install -r requirements.txt # Windows# or: python -m pip install -r requirements.txt (after venv activate)# 2. Run against a sample input WITHOUT Apify (no push, no charge)python -m src.main --input sample_input.json# 3. Run under the Apify CLI (pulls your input, pushes to local storage)apify loginapify run# 4. Deployapify push
The engine side is self-contained in engine/ (filters, matcher, ranking,
fingerprint, models). At runtime the actor re-synthesizes filters.yaml
from the Apify input before importing the engine, so the exact production
regex tables are rebuilt for your run — the vendored filters.yaml is the
tuned baseline.
Publish checklist
apify loginandapify pushfrom this directory.- In Monetization → Pay per event & result, add event
ranked-jobswith your per-result price and a reasonable run budget. - Write a store listing describing the input shape and the pricing above.
- Add a sample dataset (e.g. from any public job scraper) so trial runs work out of the box.
- Monitor the store page's Cost of results and adjust the per-result price (≈14-day notice, ~once/month) against margin.
Layout
.actor/actor.json actor metadata + output schema.actor/input_schema.json input formengine/ vendored job-hunt engine (filters, matcher, ranking)src/main.py actor entry: synthesize config -> score -> rank -> push+chargesample_input.json local smoke-test inputDockerfile apify/actor-python baserequirements.txt apify + yaml + pydantic-settings + rich