# Job Radar — Deduped, Ghost-Filtered Job Alerts (`inovaflow/job-radar`) Actor

Fresh job openings from Google Jobs, Indeed and employer career pages in one run — duplicates merged across boards, likely ghost/stale postings hidden, every job ranked by an explainable match score. Email digest + shareable dashboard. No auto-apply: you click.

- **URL**: https://apify.com/inovaflow/job-radar.md
- **Developed by:** [inovaflow](https://apify.com/inovaflow) (community)
- **Categories:** Jobs, Automation
- **Stats:** 2 total users, 1 monthly users, 66.7% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.10 / actor start

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

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

**Fresh, real job openings — duplicates merged, stale and ghost-flagged postings pushed down or hidden, every job ranked for you.**

Job boards have two dirty secrets. First, the same opening is syndicated everywhere — one job has been found copied across **200+ boards and feeds**, so half of what a multi-board search returns is the same role wearing different URLs. Second, a lot of what's posted is never going to hire anyone: **18–22% of postings are "ghost jobs"** (Greenhouse, 2025), **4 in 10 hiring managers admit posting a fake one** in the past year (ResumeBuilder), and hires-per-posting have **halved since 2019** (Revelio Labs). Scrapers happily hand you all of it, duplicated and stale.

Job Radar is the finished product instead of the raw feed. Type what you're looking for and where, add your email if you want the digest — and every run brings back one clean, ranked list: **duplicates merged across boards, every job scored 0–100 for how well it matches you and checked against a ghost-job checklist**, with the apply link that goes to the *employer's own site* whenever one exists. Run it on a **Schedule** and it becomes a radar: after the first run you only hear about what's *new*.

A note on the ghost filter, since the number above is about the market and not about your results: the score is deliberately conservative. Only the clearest cases are hidden outright, and on a **fresh daily digest that is often none** — a job posted today isn't a ghost yet. What the score does on every run is **rank suspicious postings down** and **show you the reasons** on the card ("no salary listed", "not found on the employer's careers site", "only reachable through a job-aggregator repost"). It earns its keep on wider searches, where older and recycled listings actually turn up. We'd rather show you a stale posting with its reasons attached than quietly bury a real job to make a statistic look better.

**No auto-apply.** Job Radar finds and ranks; you read and click. Your applications stay yours.

> **Priced lean** — pay only when you run, no subscription. Missing a board, a country, a filter? Open an issue on the **Issues** tab — we read every one. 🙏

### Where it searches

| Source | What it covers | Powered by | Default |
|---|---|---|---|
| Companies you follow | Greenhouse / Lever / Ashby career pages — direct from the employer, **free** | built in | ✅ prefilled |
| Google for Jobs | 20+ boards in one sweep (the spine), each with a real apply link | [johnvc/Google-Jobs-Scraper](https://apify.com/johnvc/google-jobs-scraper) | off — opt in |
| Indeed | the biggest single board, very cheap | [valig/indeed-jobs-scraper](https://apify.com/valig/indeed-jobs-scraper) | off — opt in |
| Glassdoor | salary-rich listings | [valig/glassdoor-jobs-scraper](https://apify.com/valig/glassdoor-jobs-scraper) | off |
| LinkedIn | public listings only — no login, no cookies | [cheap\_scraper/linkedin-job-scraper](https://apify.com/cheap_scraper/linkedin-job-scraper) | off |

**By default Job Radar runs the free career-page fetcher only** — zero setup, no scraper credit, results in seconds. The board scrapers (Google for Jobs, Indeed, Glassdoor, LinkedIn) run as **pay-as-you-go Store Actors on your own Apify account** (cents per run — see [Costs](#costs)); tick the ones you want. The career-page fetcher is ours and costs nothing: add `greenhouse:stripe`, `lever:palantir`, `ashby:ramp` — or just the company slug and we probe all three.

### The quality layer (what you're actually paying for)

- **One row per real job.** We normalize company, title, seniority and location, then merge duplicates across boards — the canonical record keeps the best apply link (employer ATS first), the earliest true posting date, and lists every other board it appeared on. Being on 3+ boards is itself a legitimacy signal, and we show it.
- **Ghost & stale postings are scored, shown, and only then hidden.** Every job gets a 0–100 *ghost score* from signals like true age (median time-to-fill is ~44 days — a posting alive past 90 is a red flag), repost churn, "always hiring" evergreen language, missing salary where the law requires one, one company blanketing 10+ cities with the same title, and aggregator-only listings that don't exist on the employer's site. The score always pushes a suspicious posting down the ranking and prints its reasons on the card. Only a job scoring **70+** is hidden outright — a high bar, so on a fresh daily digest the "we hid N" counter is often **0 ghosts** and the hidden count is mostly off-target titles. That's deliberate: a false "ghost" verdict costs you a real job.
- **Repost-proof age.** Boards let companies repost to reset the "posted 2 days ago" clock. We keep our own *first seen* date across runs — their clock resets, ours doesn't.
- **Exact matches, not keyword soup.** A search is a *phrase*, not a bag of words, and it is matched against the **title** only. A one- or two-word search matches **whole titles only**: `react developer` returns jobs titled *React Developer*, and deliberately not *Sr React Developer*, *Front-End React Developer* or *React Developer Intern* — at that length a substring is a fragment of half the market. From **three words up**, a title that *contains* your phrase counts too, because by then the phrase is the role: `senior react developer` also catches *Senior React Developer, Payments*. Want the variants? Give each one its own line — or, in a thin market where only one board answers, switch **Title match strictness** to **Broad** to also keep titles that carry *all* your words in any order (so `frontend developer` catches *Medior Frontend Developer*). Everything off-target is counted in the "we hid N" panel, never silently dropped.
- **An explainable match score.** Each job is scored 0–100 against your searches, must-haves, salary floor and seniority — and shows *which of your searches it matched*, not a black-box number.
- **Only what's new.** The first run is your baseline; after that, each run flags what's new, what changed (salary added, reposted), and what disappeared — perfect on a daily or weekly Schedule.

### What you get

- **🔗 A shareable dashboard page** — every run publishes a clean, branded page to your own Apify storage: every search it ran with its match count, the ranked list with apply buttons, filter chips (new · remote · salary listed · direct employer) plus a filter per search, sorting, and the hidden-postings counter with one-click reveal. One link, no login, dark & light mode, works on your phone.
- **📧 An email digest** — type an address and each run mails you the new matches first (subject like *"Job Radar: 12 new matches for react developer — 3 hot, 14 hidden as stale"*), plus what changed and what closed. Remove the address to stop.
- **A clean dataset** — one row per unique job (title, company, location, salary, apply link + type, posted date, match score, which of your searches it matched, ghost score with reasons, which boards carried it) for your own spreadsheets and automations.

### Set it up in 2 minutes

1. Enter the **job titles** you want — one per line, matched against the title (`Senior React Developer`, then `React Engineer` if you want both) — and a **location** (or flip **Remote only**). Matching is **Strict** by default; switch **Title match strictness** to **Broad** for thin markets.
2. (Optional) Add must-have / exclude keywords, a salary floor, seniority — and your **email**.
3. **Companies you follow** comes prefilled with a few examples for free direct career-page coverage — edit the list to track your own.
4. Hit **Start**. Then use the **Schedule** tab to make it daily — that's when "new since last run" starts working for you.

### Costs

One flat fee, only when you run — no subscription, never per job.

- **$0.10 per GB of run memory** at start — at the default 1 GB that's **$0.10 per run**, roughly **$3/month as a daily digest**. Dedup, ghost filtering, exact-match ranking, the dashboard and the email are all included.
- The optional board scrapers bill their own pay-as-you-go fees directly to **your own Apify account**, and we never mark them up. The default run uses the **free career pages only**, so it adds **no scraper credit** — just the flat fee. Google for Jobs and Indeed are both very cheap (fractions of a cent per listing), so enabling them adds only **a few cents** of scraper credit per single-search run. **Max matching jobs per source** is a *post-filter* target — the number of matching jobs kept per source; under the hood we fetch up to 3× that many raw rows (capped at 300) and whittle them down, so lowering it still caps what the scrapers bill you. Each extra search line and each optional source (Glassdoor, LinkedIn — LinkedIn bills a 150-result minimum when on) adds proportionally.

### Ask your AI assistant (MCP)

Works as an agent tool out of the box: connect [mcp.apify.com](https://mcp.apify.com) to Claude, Cursor, or any MCP client and just ask — *"find me senior React jobs in Berlin"* or *"anything new for my job search today?"* — and it runs this Actor with the right inputs.

### Notes

Reads **publicly visible listings** only and logs into nothing — LinkedIn coverage is public-listings-only, off by default, and skipped gracefully if unavailable. The board scrapers run on your own Apify account (your token — see the input's advanced field); your job list, history and dashboard live in your own Apify storage. We host nothing, and we never apply on your behalf.

# Actor input Schema

## `queries` (type: `array`):

Each line is ONE search, matched against the job TITLE. In the default Strict mode a one- or two-word search matches the WHOLE title only — `React Developer` returns jobs titled React Developer, not `Sr React Developer` or `React Developer Intern`; from three words up, a title that contains your phrase also counts — `Senior React Developer` also catches `Senior React Developer, Payments`. Switch Title match strictness to Broad to also catch titles that carry your words in any order (e.g. `Medior Frontend Developer`). Want more roles? Give each its own line (e.g. `React Engineer`, `Frontend Developer`).

## `location` (type: `string`):

City, region or country to search in, e.g. `Berlin`, `Prague`, `New York`. Leave empty and turn on Remote only for a location-free search.

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

Only keep jobs that are remote (fully remote or remote-first).

## `seniority` (type: `string`):

Preferred seniority. Jobs at exactly this level get a ranking boost; clear mismatches are ranked down (never silently dropped).

## `mustHaveKeywords` (type: `array`):

Jobs are dropped unless the title or description contains ALL of these, e.g. `typescript`. Leave empty to skip this filter.

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

Jobs whose title or description contains ANY of these are dropped, e.g. `clearance`, `PHP`. Leave empty to skip this filter.

## `minSalary` (type: `integer`):

Drop jobs whose stated salary tops out below this yearly amount (in the currency the posting uses). Jobs with no stated salary are kept — they lose ranking points instead.

## `useGoogleJobs` (type: `boolean`):

Search Google for Jobs — one scrape covers LinkedIn, Glassdoor, ZipRecruiter, Monster and 20+ other boards. Very cheap — a fraction of a cent per listing of your Apify credit. Off by default because it runs on — and bills — your own Apify account; tick it to add broad market coverage.

## `useIndeed` (type: `boolean`):

Search Indeed directly — the cheapest high-volume source (~$0.10 per 1,000 jobs of your Apify credit). Off by default (runs on your own Apify account) — tick it to enable.

## `followCompanies` (type: `array`):

Employer career boards fetched directly — the freshest postings and true direct-apply links, at zero cost. Entries like `greenhouse:stripe`, `lever:palantir`, `ashby:ramp`, or just a company slug (e.g. `stripe`) to probe all three systems. Prefilled with a few examples — the free, zero-config default source.

## `useGlassdoor` (type: `boolean`):

Also search Glassdoor (~$0.40 per 1,000 jobs of your Apify credit) — adds salary bands and a numeric posting age.

## `useLinkedIn` (type: `boolean`):

Also search public LinkedIn job listings via a third-party scraper (~$0.70 per 1,000 jobs of your Apify credit; the scraper enforces a 150-result minimum per run). No LinkedIn login is ever used. If LinkedIn blocks the scrape, the run continues without it.

## `maxJobsPerSource` (type: `integer`):

How many MATCHING jobs to keep from each source — counted AFTER filtering (title match → dedup → ghost removal), not raw scraped rows. We over-fetch a few times this many raw rows (bounded, so it still caps what the scrapers bill you) and keep the best matches up to this number.

## `matchMode` (type: `string`):

How strictly a job TITLE must match your search. Strict (default) keeps only titles that ARE your search — or, for searches of 3+ words, contain it verbatim — the highest-precision option. Broad also keeps titles that contain your search at any length, or contain every word of it in any order (e.g. "Frontend Developer" also matches "Medior Frontend Developer") — better recall in thin markets where only one board answers.

## `maxAgeDays` (type: `integer`):

Hard cutoff on posting age. Our age is repost-proof on repeat runs: we remember when WE first saw a job, so reposting doesn't reset its clock. Default 60 days — above the ~44-day median time-to-fill, so still-open employer roles show; older-than-this is hidden and truly stale/evergreen postings are down-ranked by the ghost score on top.

## `includeGhostJobs` (type: `boolean`):

By default, postings that score as likely stale or ghost (old, endlessly reposted, evergreen "always hiring" ads…) are hidden and only counted. Turn this on to see them anyway, each with the reasons it was flagged.

## `hideStaffingAgencies` (type: `boolean`):

Drop postings from recruiting/staffing agencies rather than the hiring company itself.

## `newOnly` (type: `boolean`):

When on, repeat runs of the same search highlight only jobs that appeared since the previous run — ideal on a daily or weekly schedule. The very first run always reports everything.

## `outputEmail` (type: `string`):

Type an email address and every run emails you the digest — new matches ranked by fit, with direct apply links and the count of postings we hid as stale. Sent from job-radar@apify.inovaflow.app (no-reply). Leave blank to skip email.

## `apifyMcpConnector` (type: `string`):

An Apify MCP connector whose account runs and pays for the job-board scrapers — you authorize once, with no token to paste. Create it in the Apify Console under Settings → Integrations → MCP Connectors with the server URL https://mcp.apify.com, then pick it here. Takes precedence over the API token below if both are set. Optional — leave blank to run on the account executing this Actor.

## `apifyToken` (type: `string`):

By default the source scrapers run on — and bill — the Apify account running this Actor. Paste a token here to run them on a different account instead (Apify Console → Settings → API & Integrations). Treat it like a password. Leave blank in most cases.

## Actor input object example

```json
{
  "queries": [
    "Senior Software Engineer",
    "Senior Frontend Engineer"
  ],
  "location": "Berlin, Germany",
  "remoteOnly": false,
  "seniority": "any",
  "useGoogleJobs": false,
  "useIndeed": false,
  "followCompanies": [
    "greenhouse:stripe",
    "greenhouse:databricks",
    "greenhouse:gitlab",
    "greenhouse:brex",
    "ashby:ramp",
    "ashby:notion"
  ],
  "useGlassdoor": false,
  "useLinkedIn": false,
  "maxJobsPerSource": 100,
  "matchMode": "strict",
  "maxAgeDays": 60,
  "includeGhostJobs": false,
  "hideStaffingAgencies": false,
  "newOnly": true,
  "outputEmail": "me@example.com",
  "apifyToken": "apify_api_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}
```

# Actor output Schema

## `dashboard` (type: `string`):

Branded interactive dashboard page — ranked job cards with direct apply links, match-reason chips, freshness badges and the hidden-postings counter.

## `jobs` (type: `string`):

One row per unique job — deduped across sources, with match score, ghost score, posting age, salary and every board it appeared on.

## `summary` (type: `string`):

Machine-readable run verdict: job counts, new/hidden split, per-source stats and the dashboard URL.

# 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 = {
    "queries": [
        "Senior Software Engineer",
        "Senior Frontend Engineer"
    ],
    "location": "United States",
    "followCompanies": [
        "greenhouse:stripe",
        "greenhouse:databricks",
        "greenhouse:gitlab",
        "greenhouse:brex",
        "ashby:ramp",
        "ashby:notion"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("inovaflow/job-radar").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 = {
    "queries": [
        "Senior Software Engineer",
        "Senior Frontend Engineer",
    ],
    "location": "United States",
    "followCompanies": [
        "greenhouse:stripe",
        "greenhouse:databricks",
        "greenhouse:gitlab",
        "greenhouse:brex",
        "ashby:ramp",
        "ashby:notion",
    ],
}

# Run the Actor and wait for it to finish
run = client.actor("inovaflow/job-radar").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 '{
  "queries": [
    "Senior Software Engineer",
    "Senior Frontend Engineer"
  ],
  "location": "United States",
  "followCompanies": [
    "greenhouse:stripe",
    "greenhouse:databricks",
    "greenhouse:gitlab",
    "greenhouse:brex",
    "ashby:ramp",
    "ashby:notion"
  ]
}' |
apify call inovaflow/job-radar --silent --output-dataset

```

## MCP server setup

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

```

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/1Rci2fucbtafw1haY/builds/eYateYK6XHObxd8FJ/openapi.json
