AI Resume Job Matcher: Ranked Shortlist + Fit Scores avatar

AI Resume Job Matcher: Ranked Shortlist + Fit Scores

Pricing

$20.00 / 1,000 retained job fit results

Go to Apify Store
AI Resume Job Matcher: Ranked Shortlist + Fit Scores

AI Resume Job Matcher: Ranked Shortlist + Fit Scores

Search 10 public developer-job sources or score up to 200 supplied jobs for one candidate. Get a ranked shortlist with fit scores, hard-requirement checks, evidence, gaps, and posting links. Returned shortlist results cost $0.02 each; no model key is needed.

Pricing

$20.00 / 1,000 retained job fit results

Rating

0.0

(0)

Developer

Job Atlas

Job Atlas

Maintained by Community

Actor stats

0

Bookmarked

1

Total users

1

Monthly active users

4 hours ago

Last modified

Share

AI Job Search & Fit Scorer — 10 Sources + AI Matching

Example provenance: All observed run IDs, immutable build IDs and historical build examples in this README come from nomad-agent/ai-job-fit-scorer. They demonstrate the primary Actor; none is evidence of a Job Atlas run. Calls below use the Job Atlas Actor’s latest selector.

Start with one input and output

An Apify account is required. Use your Apify API token when calling the API; Console runs do not need that token pasted into the input. No AI-provider key is required: the Actor owner pays for scoring. Supply exactly one candidate profile or résumé; sourceApifyToken is optional for reading your own source dataset.

Free-tier event rates checked 2026-10-04: job-fit-result $0.02.

Cost: Total = Actor event charges + platform usage if separately billed + any BYO-provider charges. See “Current costs” for your event rates, plan tiers and a calculation. A sample does not promise future billable volume.

Input and output below were read from the same successful run ZMoMBfLEl0cPGIHcc (2026-10-04, immutable build 93Chil9Dg8n0C4QGd). This controlled example scored a fictional candidate against one supplied fictional job, using the owner’s AI provider. It did not search source sites. Apify added the unused search default to retained INPUT; mode: score-jobs selects only the supplied jobs. This is an observed example, not a guarantee that repeating it returns this row. It used the Nomad Agent source Actor build at capture time.

Input JSON

{
"mode": "score-jobs",
"jobs": [
{
"schemaVersion": "nomad-agent-job-v1",
"identity": {
"source": "example",
"externalId": "readme-fixture-20261004",
"url": "https://example.com/jobs/platform-engineer"
},
"data": {
"title": "Platform Engineer",
"company": {
"name": "Example Labs",
"sourceId": null,
"department": null,
"url": null,
"logoUrl": null
},
"classification": {
"industries": null,
"jobFunctions": null
},
"domains": null,
"domainsRaw": null,
"locations": [
{
"raw": "Madrid, Spain",
"countryName": "Spain",
"countryCode": "ES",
"city": "Madrid",
"region": null,
"postalCode": null,
"streetAddress": null,
"facilityName": null,
"positionsAvailable": null,
"latitude": null,
"longitude": null
}
],
"employment": {
"workArrangements": [
"hybrid"
],
"applicantLocationRequirements": null,
"workSchedules": null,
"contractTypes": [
"full-time"
],
"durationMonths": null,
"hoursPerWeek": null,
"hoursPerWeekRaw": null,
"startDate": null,
"startDateRaw": null
},
"application": {
"postedAt": null,
"deadline": null,
"referenceNumber": null,
"referenceNumberIssuer": null,
"applicantSnapshot": null,
"url": null,
"email": null,
"directApply": null,
"applyMethodRaw": null,
"availability": null,
"hiringContacts": null,
"eligibilityCriteria": null,
"selectionProcess": null
},
"seniority": {
"raw": null,
"levels": [
"mid"
]
},
"requirements": {
"education": null,
"experience": null,
"languages": null,
"requiredSkills": [
{
"name": "Python",
"yearsRequired": null
},
{
"name": "PostgreSQL",
"yearsRequired": null
}
],
"preferredSkills": null,
"certifications": null,
"skillsQualifications": null,
"specificRequirements": null
},
"benefits": null,
"funding": {
"programme": null
},
"compensation": {
"currency": null,
"exact": null,
"minimum": null,
"maximum": null,
"period": null,
"raw": null
},
"constraints": {
"visaSponsorship": null,
"workAuthorization": null,
"securityClearance": null,
"locationPreference": null
}
},
"custom": null,
"llm": {
"status": "not_requested",
"requestedFields": [],
"filledFields": [],
"provider": null,
"model": null,
"promptVersion": null,
"completedAt": null
},
"raw": {
"description": "Build reliable Python services and PostgreSQL data pipelines. Five years of backend experience preferred. Hybrid in Madrid, Spain.",
"descriptionHtml": null
}
}
],
"candidateProfile": {
"primaryRole": "Platform Engineer",
"targetTerms": [
"Platform Engineer",
"Backend Engineer"
],
"skills": [
"Python",
"PostgreSQL",
"Docker"
],
"seniorityLevels": [
"mid"
],
"yearsExperience": 5,
"workableLanguages": [
{
"language": "English",
"level": "C1",
"evidence": "CV: English C1"
}
],
"workableLanguagesComplete": false,
"acceptedWorkArrangements": [
"hybrid"
],
"remoteLocations": [],
"hybridLocations": [
"Madrid, Spain"
],
"onsiteLocations": [],
"workArrangementPreferencesComplete": true,
"acceptedCountryCodes": [
"ES"
],
"acceptedContractTypes": [
"permanent"
],
"acceptedContractTypesComplete": true,
"freeText": "Fictional example candidate: individual-contributor product engineering roles."
},
"maxItems": 1,
"resultMode": "audit",
"minDeliveryScore": 2,
"minRankToForward": 30,
"recoverHolds": true,
"aiConcurrency": 1,
"maxAiItems": 1,
"search": {
"sources": [
"linkedin",
"remote_boards",
"builtin",
"justjoinit",
"nofluffjobs",
"hackernews",
"ycombinator_was",
"wttj",
"infojobs",
"tecnoempleo"
],
"keywords": [
"software engineer"
],
"location": "Madrid",
"remoteOnly": false,
"titleExclude": [
"intern",
"junior"
],
"postedWithinDays": 7,
"maxItemsPerSource": 10,
"cacheTtlSeconds": 1800,
"concurrency": 3,
"sourceTimeoutSecs": 120
}
}

Matching output excerpt

Selected fields from the captured output row are shown below. Long strings and arrays are shortened for display; this is not the complete output contract.

{
"schemaVersion": "nomad-ai-job-fit-v1",
"jobKey": "example:readme-fixture-20261004",
"title": "Platform Engineer",
"company": "Example Labs",
"location": "Madrid, Spain",
"fitScore": 90,
"deliveryScore": 5,
"recommendation": "exceptional",
"evaluationStatus": "scored",
"blockingGates": [],
"why": "Excellent match for a mid-level Platform Engineer role: Python, PostgreSQL, five years of backend experience, and hybrid work in Madrid align closely with the posting and preferences.",
"gapSummary": "Permanent employment and work authorization are not explicitly confirmed; the posting only states full-time and provides no eligibility details.",
"url": "https://example.com/jobs/platform-engineer"
}

Give it one résumé or a short candidate profile. It searches available postings from up to 10 public developer-job sources, removes duplicates, uses their complete original descriptions, and returns a ranked shortlist with two scores per job: a raw 0–100 fit and a 0–5 delivery score that drops whenever a hard requirement is contradicted. Already have a job list? Skip the search and score your own jobs instead.

No model key is required. Each returned match costs $0.02. In default shortlist mode, jobs that miss the threshold, hard drops, holds, AI failures, and clean empty searches have no result charge. Audit mode charges retained non-failure rows.

Independent, unofficial integration. Not affiliated with or endorsed by LinkedIn or any source-site operator. Review source terms, privacy rules, and applicable law for your use case.

What a result looks like

One real row from a run on 2026-09-05 that scored the built-in example candidate, a generalist senior software engineer:

{
"schemaVersion": "nomad-ai-job-fit-v1",
"jobKey": "linkedin:4436371317",
"title": "Senior Software Engineer, Java",
"company": "CLEAR",
"location": "New York, NY",
"postedAt": "2026-09-05",
"fitScore": 78,
"deliveryScore": 4,
"recommendation": "strong",
"evaluationStatus": "scored",
"blockingGates": [],
"why": "Strong match for a senior full-stack software engineering role: eight years of backend/full-stack experience, React, TypeScript, AWS, Docker, Kubernetes, APIs, and CI/CD align well with the posting.",
"gapSummary": "The posting highlights Java, which is not listed in the candidate’s skills. Work arrangement, New York logistics, and work authorization are not specified.",
"url": "https://www.linkedin.com/jobs/view/senior-software-engineer-java-at-clear-4436371317"
}

How to read it:

  • fitScore (0–100) says how well the posting matches the candidate's skills and target roles.
  • deliveryScore (0–5) is the number to act on. Hard gates such as work mode, location, language, work authorization, seniority, or role family lower it, so a great skills match with an impossible location never reaches the top.
  • blockingGates lists any hard gate that failed. Empty means none did.
  • why and gapSummary are the model's short evidence for and against the match.

The full row also carries the normalized job, every gate verdict, the deterministic pre-screen decision, the model and provider that produced the verdict, source provenance, and stable hashes. The complete example is public: fit-row.example.json.

Run it in one minute

Apify Console. Press Start. The form is prefilled with a bounded search: all ten sources, software-engineer roles posted in the last seven days around Madrid with intern and junior titles excluded, at most three jobs, the example candidate, and a result-charge cap of $0.06. The log, the run status message, and candidate.usedExampleProfile in the run summary all say the results describe that example candidate, not you. Then replace the profile with your own facts, or clear it and upload a résumé. Exactly one candidate source may be set; a run that keeps the profile and adds a résumé is rejected before any paid work starts.

API. This call selects latest, searches one source, and returns at most one match for $0.02:

curl --request POST \
--header "Authorization: Bearer $APIFY_TOKEN" \
--header "Content-Type: application/json" \
--data '{
"mode": "search",
"search": {
"sources": ["linkedin"],
"keywords": ["platform engineer"],
"location": "Madrid",
"postedWithinDays": 7,
"maxItemsPerSource": 1
},
"candidateProfile": {
"primaryRole": "Platform Engineer",
"targetTerms": ["Platform Engineer", "Backend Engineer"],
"skills": ["Python", "PostgreSQL", "Docker"],
"seniorityLevels": ["mid"],
"remoteLocations": ["Spain", "European Union"],
"hybridLocations": ["Madrid, Spain"],
"onsiteLocations": [],
"workArrangementPreferencesComplete": true
},
"maxItems": 1,
"resultMode": "shortlist",
"minDeliveryScore": 2,
"aiConcurrency": 1
}' \
"https://api.apify.com/v2/actors/jobatlas~ai-job-fit-scorer/runs?build=latest&waitForFinish=120&maxItems=1&maxTotalChargeUsd=0.02"

Build 0.1.22 produced the real example above. New calls use latest; record the numeric build number and immutable build ID returned by Apify. Keep the returned run ID, wait for that run to reach SUCCEEDED with exit code 0, then read its default dataset and its RUN-SUMMARY record. Storage counts can settle a moment after the run finishes, so re-read the same run when they have not reconciled yet. A ten-source version of this input is public: search-input.json.

Two ways to use it

Search + score (default)

Set mode to search, give at least one search term, and pick sources. The Actor never invents a query from a résumé, so search.keywords is required. Only the search object narrows what is fetched: put the city or country in search.location, or use ISO country codes in search.countryCodes when no location is set, and remote-only work in search.remoteOnly. A location written in preferences or the candidate profile affects scoring only.

Score jobs you already have

Selecting sourceDatasetId grants this run read-only access to that dataset. For sourceActorRunId outside the Actor's limited access, supply sourceApifyToken with read access to the run and its dataset. The token is an encrypted input; it is never included in results or run summaries. Using the dataset ID avoids needing a separate token.

Set mode to score-jobs, provide one candidate source, and exactly one job source:

  • jobs: inline nomad-agent-job-v1 records (up to 200);
  • sourceDatasetId: an existing Apify dataset, read only;
  • sourceActorRunId: one exact successful upstream Actor run. Add expectedSourceBuild when the upstream build must match.
{
"mode": "score-jobs",
"sourceDatasetId": "YOUR_DATASET_ID",
"candidateProfile": {
"primaryRole": "Data Engineer",
"targetTerms": ["Data Engineer", "Analytics Engineer"],
"skills": ["Python", "SQL", "dbt"],
"remoteLocations": ["European Union"],
"hybridLocations": [],
"onsiteLocations": [],
"workArrangementPreferencesComplete": true
},
"maxItems": 25,
"resultMode": "shortlist",
"minDeliveryScore": 2
}

A complete inline job record is public: inline-input.json.

Candidate input: choose exactly one

  • candidateProfile: structured facts and preferences. Fill only what the candidate actually stated and leave unknowns empty; the scorer treats an empty field as unknown, never as a contradiction.
  • resume: a text-based PDF or TXT uploaded in the Apify Console.
  • resumeText: plain text for API clients.

For a conditional profile, set candidateProfile.schemaVersion to 8 and include both matchingPolicy (profile-matching-policy-v1) and tenureBounds (profile-tenure-bounds-v1). Each alternative is one complete acceptable combination; every rule within it must hold. A missing job fact leaves a hard condition unresolved and holds the job from AI scoring. Omit these fields for the existing flat candidate profile format. Year-only experience dates should remain a range in tenureBounds, not an invented exact year count.

preferences can add explicit role, location, work-mode, language, or contract constraints for scoring. It does not change which jobs are fetched.

Sources

KeySource
linkedinLinkedIn public job pages
remote_boardsRemote job boards
builtinBuilt In
justjoinitJust Join IT
nofluffjobsNo Fluff Jobs
hackernewsHacker News “Who is Hiring?”
ycombinator_wasY Combinator Work at a Startup
wttjWelcome to the Jungle
infojobsInfoJobs
tecnoempleoTecnoempleo

The default selects all ten listed sources. Results are merged fairly across sources and deduplicated before scoring, and every job needs its full description before it can be scored.

Search matches available job postings. Each keyword query matches the title or description when every word in that query matches; any query may match. Location and country filters use locations. These filters apply before the job data limit. titleExclude is checked afterward on the bounded fetched records. A strict remoteOnly search keeps only postings explicitly marked fully remote, excluding hybrid and unknown arrangements. Experience preferences affect scoring. RUN-SUMMARY records fetched jobs, applied filters and unavailable sources for each selected source.

How scoring works

  1. A deterministic pre-screen reads each posting and sorts it into a hard contradiction (dropped), uncertain evidence (held), or a plausible match (forwarded to AI). minRankToForward sets the forwarding floor and recoverHolds can send holds to AI as well.
  2. Each forwarded job gets one AI verdict: the raw fitScore, a short rationale, gaps, and a pass/fail with evidence for every hard gate.
  3. Any failed hard gate clamps the deliveryScore.
  4. Rows are ordered by delivery score first, then raw fit.

If the model call fails, the row is reported as ai_failed. It is never silently turned into a zero.

Shortlist or audit

resultMode decides what reaches the dataset and what is charged:

  • shortlist (default) returns only rows with evaluationStatus: "scored" and deliveryScore >= minDeliveryScore. The default threshold is 2 and equality counts. Everything else is neither returned nor charged.
  • audit keeps the complete evaluation trail: scored rows, static drops, holds, forward-cap holds, and ai_failed rows. Every retained row except ai_failed is charged. minDeliveryScore is recorded but does not filter.

Output

Dataset rows follow nomad-ai-job-fit-v1 (schema). Three keys matter for automation:

  • matchKey identifies one candidate plus one job. Use it as the upsert key.
  • evaluationKey changes with the exact candidate snapshot, job content, scoring contract, or evaluation time. Keep it as the receipt.
  • jobKey identifies the posting only. Keying a destination on it alone lets one candidate's result overwrite another's.

RUN-SUMMARY in the default key-value store follows nomad-ai-job-fit-run-summary-v4 (schema, real example). It records per-source outcomes, the result policy, warnings, candidate hashes (never résumé text), provider and model usage, billing receipts, and the terminal reason. Its counts block always includes staticDropped, staticHeld, aiScored, aiFailed, resultFilteredOut, and outputRows, so an empty shortlist is distinguishable from a run that did no work. Reconcile the summary with the run and its dataset before writing results anywhere.

Limits

LimitValue
Unique jobs evaluated per run200
AI verdict concurrency1–8
Source concurrency1–10
Résumé upload5 MB, 50 pages, 8,000 readable characters
Job description text and HTMLNo length limit; never truncated or rejected for size
Default run timeout15 minutes

Pricing

Result modejob-fit-result is charged whenPrice
shortlist (default)A scored row meets minDeliveryScore and is returned$0.02 per returned match ($20 per 1,000)
auditA complete evaluation row is retained; ai_failed is never charged$0.02 per retained non-failure row ($20 per 1,000)

One charged row is $0.02, 25 are $0.50, 100 are $2.00. There is no search, résumé, run-start, or model-key fee, and no paid child Actor runs: search reads available postings with verified complete descriptions, and never scrapes job websites during your run. Results depend on available coverage for each source (LinkedIn currently holds US software-engineering postings), so a search can return jobs from fewer sources than it names.

The model calls are paid by the Actor owner. That route is capped at $0.20/M prompt tokens and $1.20/M completion tokens, with at most two provider attempts per AI call and a fixed $0.25 circuit breaker per run. When the breaker trips, later AI-dependent evaluations become unbilled ai_failed rows in audit mode. Shortlist mode filters those failed rows out.

maxTotalChargeUsd on the run limits how many jobs can be evaluated before their scores are known. A low cap can therefore stop evaluation early even when some later jobs would have been filtered out and left uncharged.

Integrations and agents

The credential-free starters below select latest. The accompanying skill, guide, schemas, and examples describe that contract:

Maintained paths are API, MCP, n8n, Make, Zapier, Python, and the Agent Skill. No Airtable asset is claimed. An Actor run proves neither hosted client exposure nor a named-destination write; verify those layers separately.

The starters are inactive and contain no credentials. Local artifact validation checks the starter configuration. A successful Actor run proves the Actor path; it does not prove your n8n, Make, Zapier, or Google Sheets destination. Test a disposable destination before enabling a schedule.

Caveats

  • Public sites change, rate-limit, block, or omit fields. One source can be partial while the others still return usable rows.
  • A source teaser is not accepted as a full job description. Supplied and searched descriptions are never truncated or rejected for length, and returned job records carry the complete text and HTML. A posting whose AI prompt exceeds the owner-cost safety limit is returned as ai_failed and is not charged.
  • A source that returns zero rows after a verified request is a clean empty result. Unverified empties, all-source failure, or only invalid records fail the run rather than pretending the search was clean.
  • Search responses are cached for cacheTtlSeconds. Set it to 0 when a check must hit the source fresh.
  • Duplicate source identities with conflicting supplied content fail closed.
  • The Actor evaluates one candidate per run and does not learn from clicks or previous runs.
  • Scores are decision support, not facts about a person, and never a substitute for reading the posting.

Privacy and responsible use

  • Only public job pages are in scope. The Actor does not use cookies, borrowed accounts, login or paywall bypasses, or CAPTCHA evasion.
  • Résumés are processed in memory. They are not written as a separate dataset or copied into every fit prompt. The original Actor input or upload remains subject to Apify retention controls.
  • Extracted candidate evidence and fit prompts go through the configured OpenRouter/OpenAI route with provider data collection denied. No zero-data-retention claim is made.
  • File URLs are restricted to Apify upload hosts; arbitrary URL fetching is rejected.
  • Dataset and upstream-run inputs are read only. The Actor never applies to a job, changes source data, or makes an automated hiring decision.

Verify the source posting and keep a human responsible for application and hiring decisions.

Fast setup and source code

Public repository · Agent setup skill.

Use the Actor’s current input form for filters, or start with its documented API example. Select latest for ai-job-fit-scorer and record the immutable build ID and number returned by your run. Inspect the dataset and any run summary the Actor documents together; a successful status alone does not establish complete source coverage.

Restrictive filters or repeat-delivery suppression can produce zero results. Diagnostics and demo records are not source records. Optional AI or translation stays explicit where supported.

Observed output example

Selected fields from a real source record inspected on 2026-10-03; consult the output schema for the full contract. Values change with the source.

{
"schemaVersion": "nomad-ai-job-fit-v1",
"matchKey": "85fa5ac9acc99d4a8984212277adac384a841d92d96edf026a596bee17ceb615",
"evaluationKey": "24e88ceb74ce5aed864c51a76b9dd8337ef9140f8ee2bf257aaace3410f0b04b",
"jobKey": "wttj:f7509d9d-4946-411d-8dde-3903d7f74f82",
"candidateHash": "10435c5062dfa2ca106667416d98c01b9c9c9870a5363bb431eaf64744c41e81",
"candidateSnapshotHash": "aaa8f9a66057aca500a9e5d45b2d9cc73ec05a03518059812fd5f9f247c4df3b",
"evaluatedAt": "2026-10-03T08:06:54Z",
"source": "wttj",
"externalId": "f7509d9d-4946-411d-8dde-3903d7f74f82",
"url": "https://www.welcometothejungle.com/en/companies/galadrim/jobs/software-engineer-freelance-madrid_madrid",
"title": "Software Engineer - Freelance - Madrid",
"company": "Galadrim",
"location": "Madrid, Spain",
"postedAt": "2026-09-27",
"fitScore": 72,
"deliveryScore": 4,
"recommendation": "strong",
"evaluationStatus": "scored",
"why": "Strong Software Engineer match with eight years of backend/full-stack experience and React, TypeScript, Python, AWS, Docker, Kubernetes, and SQL. Spanish fluency and Madrid work logistics are unverified.",
"gapSummary": "The posting requires native Spanish and fluent English; the candidate only documents English at B2. Work arrangement, authorization, and freelance compatibility are not specified.",
"blockingGates": [],
"scoreAdjustedForGates": false
}

Current costs

Current Actor event rates, checked 2026-10-04. USD; use the Pricing tab for your plan and later changes.

EventFreeBronzeSilverGoldPlatinumDiamond
Retained job fit result (job-fit-result)$0.02$0.02$0.02$0.02$0.02$0.02

For example, 100 × job-fit-result totals $2 in Actor event charges on Free. This is an arithmetic example, before any waiver; actual charged events depend on the run and selected options. It is not a promise of output or a charge for simply setting an item cap.

Your total is the charged Actor events, plus platform usage only if this Actor’s Pricing tab bills it separately, plus any external provider you use with your own key. Do not add platform usage twice when it is included. Apify credits and plan discounts affect your invoice. Set a run cost limit in Console before starting.

Job Atlas

Explore the Job Atlas job-data and matching Actors. Use latest and retain the immutable build ID returned by each run.

Product guide | Source and client examples | Setup skill | Setup guide