# AI Job Search & Fit Scorer — 10 Sources + AI Matching (`job-atlas/ai-job-fit-scorer`) Actor

Search 10 public developer-job sources or score your own job list against a résumé or profile. Get a ranked shortlist with a 0–100 fit score, a 0–5 delivery score after hard-requirement checks, evidence, skill gaps, and links to the postings. No model key needed.

- **URL**: https://apify.com/job-atlas/ai-job-fit-scorer.md
- **Developed by:** [Job Atlas](https://apify.com/job-atlas) (community)
- **Categories:** Jobs, AI, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$20.00 / 1,000 retained job fit results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

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

Give it one résumé or a short candidate profile. It searches up to 10 public
developer-job sources, removes duplicates, reads each full posting, 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:

```json
{
  "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](https://github.com/Exdenta/nomad-agent-job-scrapers/blob/main/docs/examples/ai-job-fit-scorer/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**:

```bash
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/job-atlas~ai-job-fit-scorer/runs?build=latest&waitForFinish=120&maxItems=1&maxTotalChargeUsd=0.02"
```

Build `0.1.22` produced the historical example above. Maintained callers select `latest` and record each returned run’s resolved immutable build ID and numeric build number. 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](https://github.com/Exdenta/nomad-agent-job-scrapers/blob/main/docs/examples/ai-job-fit-scorer/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`, ISO codes for Welcome to the Jungle in
`search.countryCodes`, 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.

```json
{
  "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](https://github.com/Exdenta/nomad-agent-job-scrapers/blob/main/docs/examples/ai-job-fit-scorer/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.

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

### Sources

| Key | Source |
|---|---|
| `linkedin` | LinkedIn public job pages |
| `remote_boards` | Remote job boards |
| `builtin` | Built In |
| `justjoinit` | Just Join IT |
| `nofluffjobs` | No Fluff Jobs |
| `hackernews` | Hacker News “Who is Hiring?” |
| `ycombinator_was` | Y Combinator Work at a Startup |
| `wttj` | Welcome to the Jungle |
| `infojobs` | InfoJobs |
| `tecnoempleo` | Tecnoempleo |

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.

Filter support differs by source. Location is native on LinkedIn, Just Join IT,
InfoJobs, and Tecnoempleo. InfoJobs accepts recognized Spanish province names or
its own province IDs (for example, Madrid maps to `33`); unrecognized locations
leave its nationwide search unfiltered. Built In and No Fluff Jobs use developer categories
instead of keyword search. A strict `remoteOnly` search keeps only jobs that
are explicitly fully remote, which excludes hybrid and unknown arrangements and
therefore most LinkedIn rows. `RUN-SUMMARY` records, per source, what was
fetched and which filters applied.

### 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](https://github.com/Exdenta/nomad-agent-job-scrapers/blob/main/integrations/shared/nomad-ai-job-fit-v1.schema.json)).
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](https://github.com/Exdenta/nomad-agent-job-scrapers/blob/main/integrations/shared/nomad-ai-job-fit-run-summary-v4.schema.json),
[real example](https://github.com/Exdenta/nomad-agent-job-scrapers/blob/main/docs/examples/ai-job-fit-scorer/run-summary.example.json)).
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

| Limit | Value |
|---|---|
| Unique jobs evaluated per run | 200 |
| AI verdict concurrency | 1–8 |
| Source concurrency | 1–10 |
| Résumé upload | 5 MB, 50 pages, 8,000 readable characters |
| Job description evidence | 20,000 characters |
| Default run timeout | 15 minutes |

### Pricing

| Result mode | `job-fit-result` is charged when | Price |
|---|---|---:|
| `shortlist` (default) | A scored row meets `minDeliveryScore` and is returned | **$0.02 per returned match ($20 per 1,000)** |
| `audit` | A 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: all
collectors run inside this Actor.

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` and verify each resolved immutable run. The accompanying
skill, guide, schemas, and examples describe that contract:

- [Integration guide](https://github.com/Exdenta/nomad-agent-job-scrapers/blob/main/docs/ai-job-fit-scorer.md)
  with the exact-run consumption contract
- [Agent skill](https://github.com/Exdenta/nomad-agent-job-scrapers/tree/main/.agents/skills/ai-job-fit-scorer)
  for Codex and Claude Code, with validators for rows and run summaries
- [Hosted Apify MCP descriptor](https://github.com/Exdenta/nomad-agent-job-scrapers/tree/main/integrations/mcp)
- [REST run-and-fetch client](https://github.com/Exdenta/nomad-agent-job-scrapers/tree/main/integrations/api)
- [n8n → Google Sheets workflow](https://github.com/Exdenta/nomad-agent-job-scrapers/tree/main/integrations/n8n)
- [Make → Google Sheets blueprint](https://github.com/Exdenta/nomad-agent-job-scrapers/tree/main/integrations/make)
- [Zapier editor specification](https://github.com/Exdenta/nomad-agent-job-scrapers/tree/main/integrations/zapier)
- [Example inputs and real outputs](https://github.com/Exdenta/nomad-agent-job-scrapers/tree/main/docs/examples/ai-job-fit-scorer)

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 jobs over
  the description limit fail validation; searched descriptions may be
  truncated and are marked when they are.
- 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.

### Job Atlas

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

- [Linkedin](https://apify.com/job-atlas/linkedin-enrich-translate-normalize-scraper)
- [Euraxess](https://apify.com/job-atlas/euraxess-enrich-translate-normalize-scraper)
- [YC](https://apify.com/job-atlas/ycombinator-enrich-translate-normalize-scraper)
- [Scorer](https://apify.com/job-atlas/ai-job-fit-scorer)

[Website and integration guides](https://nomadagent.dev/) | [Source and client examples](https://github.com/Exdenta/nomad-agent-job-scrapers)

# Actor input Schema

## `mode` (type: `string`):

Choose Search + score to find current jobs, or Score supplied jobs to evaluate normalized jobs you already have.

## `search` (type: `object`):

Used only in Search + score. The fresh form searches all ten public sources for software engineer roles posted in the last seven days around Madrid, skips intern and junior titles, and keeps at most three jobs per source. Edit sources, terms, and filters for your market. Free-text preferences never narrow the search: set the city or country in Location, ISO codes for Welcome to the Jungle in Country codes, and remote-only in Fully remote only. All collectors run inside this Actor—there are no paid child Actor runs or stacked child fees.

## `jobs` (type: `array`):

JSON array of complete nomad-agent-job-v1 records. Every item needs top-level schemaVersion, identity, data, custom, llm, and raw; identity needs source plus externalId or url, and raw.description contains the posting text. Copy the working shape from the public example https://github.com/Exdenta/nomad-agent-job-scrapers/blob/main/docs/examples/ai-job-fit-scorer/inline-input.json. Use this, sourceDatasetId, or sourceActorRunId—never more than one. Hard limits: 200 records and 20,000 plain-text description characters per job; oversized evidence is rejected, never truncated.

## `sourceDatasetId` (type: `string`):

Paste the immutable Apify dataset ID—not the dataset name or URL. Each dataset item must be one complete nomad-agent-job-v1 record. The dataset is read only and never modified. Use only one supplied-job source.

## `sourceActorRunId` (type: `string`):

Paste the exact upstream run ID—not the Actor ID, build ID, or run URL. The run must be terminal SUCCEEDED with exit code 0; this Actor reads that run's default dataset as nomad-agent-job-v1 records. Use only one supplied-job source.

## `expectedSourceBuild` (type: `string`):

Optional guard for sourceActorRunId. Enter the upstream run's exact immutable build ID or build number; the run fails instead of reading jobs when it does not match.

## `maxItems` (type: `integer`):

Maximum unique jobs to evaluate (1–200). In search mode this caps the merged, deduplicated result set; in score-jobs mode it caps the supplied source.

## `candidateProfile` (type: `object`):

Structured facts about the candidate. Needs at least one of primaryRole, targetTerms, skills, or freeText. Add seniority, years, language, location, and contract constraints only when the candidate actually stated them; leave unknowns empty. An example profile is prefilled so a first run works out of the box; replace every field with your own evidence before acting on the results. Use exactly one of this, Résumé, or Résumé text.

## `resume` (type: `string`):

Text-based PDF or UTF-8 text, up to 5 MB, 50 PDF pages, and 8,000 readable characters. Read in memory and sent once for profile extraction; never written as a separate dataset or repeated in each fit prompt.

## `resumeText` (type: `string`):

Plain-text alternative for API clients, up to 8,000 readable characters. Use exactly one of candidateProfile, resume, or resumeText.

## `preferences` (type: `string`):

Up to 4,000 characters of explicit role, location, work-mode, language, contract, or other constraints. Unknowns are never guessed. Used for scoring only: it never narrows the search, so a location written here does not change which jobs are fetched. Use search.location, search.countryCodes, and search.remoteOnly for that.

## `resultMode` (type: `string`):

Shortlist (default) returns and charges only successfully scored rows whose deliveryScore meets minDeliveryScore. Audit returns the complete evaluation dataset, including static\_drop, static\_hold, forward\_cap\_hold, and ai\_failed rows; every Audit row except ai\_failed is charged.

## `minDeliveryScore` (type: `integer`):

Inclusive 0–5 threshold used only in Shortlist mode (default 2). deliveryScore already includes hard-gate reductions, so it is safer for delivery than the raw fitScore. Audit mode records this value but does not filter by it.

## `minRankToForward` (type: `integer`):

Jobs whose deterministic pre-screen rank is below this 0–100 value are held before any AI call (default 30). Hard contradictions are always dropped regardless of rank.

## `maxAiItems` (type: `integer`):

Cap on AI verdict calls after the static screen (0–200). Remaining eligible jobs are returned as forward\_cap\_hold rather than given a fabricated score.

## `recoverHolds` (type: `boolean`):

Also send recoverable static holds to AI. Off by default so uncertain evidence stays visibly held instead of being scored on guesses.

## `aiConcurrency` (type: `integer`):

Concurrent fit verdict calls (1–8). Lower this for smaller or latency-sensitive batches.

## `sourceApifyToken` (type: `string`):

For sourceActorRunId when the run is outside this Actor’s limited access: supply your own Apify token with read access to that run and its dataset. Prefer sourceDatasetId for automatic access to just the selected dataset. This token is used only to read your selected Apify source.

## Actor input object example

```json
{
  "mode": "search",
  "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": 3,
    "cacheTtlSeconds": 1800,
    "concurrency": 3,
    "sourceTimeoutSecs": 120
  },
  "sourceDatasetId": "a1B2c3D4e5F6g7H8I",
  "sourceActorRunId": "a1B2c3D4e5F6g7H8I",
  "expectedSourceBuild": "0.1.12",
  "maxItems": 3,
  "candidateProfile": {
    "primaryRole": "Software Engineer",
    "targetTerms": [
      "Software Engineer",
      "Backend Engineer",
      "Full Stack Engineer",
      "Platform Engineer"
    ],
    "skills": [
      "Python",
      "TypeScript",
      "JavaScript",
      "React",
      "Node.js",
      "SQL",
      "PostgreSQL",
      "Docker",
      "Kubernetes",
      "AWS",
      "REST APIs",
      "Git",
      "CI/CD"
    ],
    "seniorityLevels": [
      "mid",
      "senior"
    ],
    "yearsExperience": 8,
    "workableLanguages": [
      {
        "language": "English",
        "level": "professional",
        "evidence": "Example profile: professional working proficiency"
      }
    ],
    "workableLanguagesComplete": false,
    "acceptedWorkArrangements": [
      "remote",
      "hybrid",
      "onsite"
    ],
    "remoteLocations": [
      "Worldwide"
    ],
    "hybridLocations": [
      "Worldwide"
    ],
    "onsiteLocations": [
      "Worldwide"
    ],
    "workArrangementPreferencesComplete": false,
    "acceptedContractTypesComplete": false,
    "freeText": "Software engineer with eight years of experience building and operating backend and full-stack web services in Python, TypeScript and SQL, deployed on AWS with Docker and Kubernetes."
  },
  "resultMode": "shortlist",
  "minDeliveryScore": 2,
  "minRankToForward": 30,
  "recoverHolds": false,
  "aiConcurrency": 2
}
```

# Actor output Schema

## `dataset` (type: `string`):

No description

## `runSummary` (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 = {
    "mode": "search",
    "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": 3,
        "cacheTtlSeconds": 1800,
        "concurrency": 3,
        "sourceTimeoutSecs": 120
    },
    "maxItems": 3,
    "candidateProfile": {
        "primaryRole": "Software Engineer",
        "targetTerms": [
            "Software Engineer",
            "Backend Engineer",
            "Full Stack Engineer",
            "Platform Engineer"
        ],
        "skills": [
            "Python",
            "TypeScript",
            "JavaScript",
            "React",
            "Node.js",
            "SQL",
            "PostgreSQL",
            "Docker",
            "Kubernetes",
            "AWS",
            "REST APIs",
            "Git",
            "CI/CD"
        ],
        "seniorityLevels": [
            "mid",
            "senior"
        ],
        "yearsExperience": 8,
        "workableLanguages": [
            {
                "language": "English",
                "level": "professional",
                "evidence": "Example profile: professional working proficiency"
            }
        ],
        "workableLanguagesComplete": false,
        "acceptedWorkArrangements": [
            "remote",
            "hybrid",
            "onsite"
        ],
        "remoteLocations": [
            "Worldwide"
        ],
        "hybridLocations": [
            "Worldwide"
        ],
        "onsiteLocations": [
            "Worldwide"
        ],
        "workArrangementPreferencesComplete": false,
        "acceptedContractTypesComplete": false,
        "freeText": "Software engineer with eight years of experience building and operating backend and full-stack web services in Python, TypeScript and SQL, deployed on AWS with Docker and Kubernetes."
    },
    "resultMode": "shortlist",
    "minDeliveryScore": 2,
    "aiConcurrency": 2
};

// Run the Actor and wait for it to finish
const run = await client.actor("job-atlas/ai-job-fit-scorer").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 = {
    "mode": "search",
    "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": 3,
        "cacheTtlSeconds": 1800,
        "concurrency": 3,
        "sourceTimeoutSecs": 120,
    },
    "maxItems": 3,
    "candidateProfile": {
        "primaryRole": "Software Engineer",
        "targetTerms": [
            "Software Engineer",
            "Backend Engineer",
            "Full Stack Engineer",
            "Platform Engineer",
        ],
        "skills": [
            "Python",
            "TypeScript",
            "JavaScript",
            "React",
            "Node.js",
            "SQL",
            "PostgreSQL",
            "Docker",
            "Kubernetes",
            "AWS",
            "REST APIs",
            "Git",
            "CI/CD",
        ],
        "seniorityLevels": [
            "mid",
            "senior",
        ],
        "yearsExperience": 8,
        "workableLanguages": [{
                "language": "English",
                "level": "professional",
                "evidence": "Example profile: professional working proficiency",
            }],
        "workableLanguagesComplete": False,
        "acceptedWorkArrangements": [
            "remote",
            "hybrid",
            "onsite",
        ],
        "remoteLocations": ["Worldwide"],
        "hybridLocations": ["Worldwide"],
        "onsiteLocations": ["Worldwide"],
        "workArrangementPreferencesComplete": False,
        "acceptedContractTypesComplete": False,
        "freeText": "Software engineer with eight years of experience building and operating backend and full-stack web services in Python, TypeScript and SQL, deployed on AWS with Docker and Kubernetes.",
    },
    "resultMode": "shortlist",
    "minDeliveryScore": 2,
    "aiConcurrency": 2,
}

# Run the Actor and wait for it to finish
run = client.actor("job-atlas/ai-job-fit-scorer").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 '{
  "mode": "search",
  "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": 3,
    "cacheTtlSeconds": 1800,
    "concurrency": 3,
    "sourceTimeoutSecs": 120
  },
  "maxItems": 3,
  "candidateProfile": {
    "primaryRole": "Software Engineer",
    "targetTerms": [
      "Software Engineer",
      "Backend Engineer",
      "Full Stack Engineer",
      "Platform Engineer"
    ],
    "skills": [
      "Python",
      "TypeScript",
      "JavaScript",
      "React",
      "Node.js",
      "SQL",
      "PostgreSQL",
      "Docker",
      "Kubernetes",
      "AWS",
      "REST APIs",
      "Git",
      "CI/CD"
    ],
    "seniorityLevels": [
      "mid",
      "senior"
    ],
    "yearsExperience": 8,
    "workableLanguages": [
      {
        "language": "English",
        "level": "professional",
        "evidence": "Example profile: professional working proficiency"
      }
    ],
    "workableLanguagesComplete": false,
    "acceptedWorkArrangements": [
      "remote",
      "hybrid",
      "onsite"
    ],
    "remoteLocations": [
      "Worldwide"
    ],
    "hybridLocations": [
      "Worldwide"
    ],
    "onsiteLocations": [
      "Worldwide"
    ],
    "workArrangementPreferencesComplete": false,
    "acceptedContractTypesComplete": false,
    "freeText": "Software engineer with eight years of experience building and operating backend and full-stack web services in Python, TypeScript and SQL, deployed on AWS with Docker and Kubernetes."
  },
  "resultMode": "shortlist",
  "minDeliveryScore": 2,
  "aiConcurrency": 2
}' |
apify call job-atlas/ai-job-fit-scorer --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,job-atlas/ai-job-fit-scorer"
        }
    }
}

```

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/OZ919PaAyAbifOdcL/builds/4BuKqr1fxICJxDC8v/openapi.json
