# New Job Alerts - Only Postings That Appeared Since Last Run (`starbright_overlap/new-jobs-feed`) Actor

Each run returns only the job postings that are new since your previous run, read live from each employer's applicant tracking system. Schedule it and pipe genuinely new openings into Slack, email or a database.

- **URL**: https://apify.com/starbright\_overlap/new-jobs-feed.md
- **Developed by:** [Sulle H](https://apify.com/starbright_overlap) (community)
- **Categories:** Jobs, Automation, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 new job postings

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/platform/actors/running/actors-in-store#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

## New Job Alerts — Only New Postings from 27,000+ Career Sites

Watch **27,000+ company career sites** and get back only the postings that appeared since your last run.

Every other job feed hands you the same thousands of rows every time and leaves you to diff them. This one remembers what it already showed you. Schedule it hourly or daily and each run returns a clean list of genuinely new openings — ready to pipe into Slack, an email digest, a spreadsheet or your own database.

Postings are read live from each employer's own applicant tracking system, so a row means the job is open on the company's careers page right now, with a direct apply link.

| Platform | Boards watched |
|---|---|
| Greenhouse | 5,578 |
| Personio | 5,172 |
| SmartRecruiters | 4,334 |
| Workday | 2,658 |
| Lever | 1,917 |
| Workable | 1,710 |
| Ashby | 1,596 |
| Breezy HR | 2,800 |
| Rippling | 1,298 |
| Pinpoint | 618 |
| **Total** | **27,681** |

No login, no cookies, no proxies, no API keys.

### How the feed remembers

The first run seeds the feed and returns the most recent matching postings. Every run after that returns only what is new.

State is kept per **feed name**, in your own account. Run several alerts side by side — `remote-react`, `berlin-sales`, `new-grad-ml` — and give each its own `feedId` so they don't consume each other's new postings.

A posting older than `lookbackDays` (7 by default) is never counted as new, even on a first run. Raise it if you run the feed less often than weekly.

### What you get

| Field | Description |
|---|---|
| `company` / `companySlug` | Company name and its board slug |
| `provider` | Which ATS the posting came from |
| `jobId` | Stable per-posting ID |
| `title` | Job title |
| `location` | Location string as published |
| `department` | Department or team |
| `employmentType` | Full-time, contract, etc., where the ATS exposes it |
| `remote` | Remote flag, where the ATS exposes it |
| `postedAt` / `updatedAt` | Publication and last-modified timestamps |
| `applyUrl` | Direct application link on the employer's own site |
| `salary` | Pay range as the employer wrote it. Only Breezy and Ashby publish this; `null` elsewhere. |
| `descriptionText` | Full description, when `includeDescription` is on |
| `scrapedAt` | When this row was read, ISO 8601 |
| `firstSeenAt` | When this feed first reported the posting |

Results come back newest first.

### Input

```json
{
  "keywords": ["engineer", "developer"],
  "excludeKeywords": ["senior", "staff"],
  "remoteOnly": true,
  "feedId": "remote-eng",
  "lookbackDays": 7,
  "maxBoards": 300
}
```

| Option | What it does |
|---|---|
| `keywords` | Keep titles containing any of these. Empty = every job. |
| `excludeKeywords` | Drop titles containing any of these. |
| `locations` | Keep postings whose location matches any entry. |
| `remoteOnly` | Keep only remote-flagged or remote-looking postings. |
| `feedId` | Names this feed's memory. One per alert you run. |
| `lookbackDays` | How far back a posting can be and still count as new. |
| `companies` / `providers` | Restrict to specific boards or ATS platforms. |
| `maxBoards` | How many boards to watch, largest employers first. |
| `maxJobs` | Cap on returned rows, so a run costs what you expect. |
| `includeDescription` | Off by default — alerts rarely need the full text. |

### Suggested setup

1. Run it once with your filters to seed the feed.
2. Add a **Schedule** — daily is a good default, hourly for competitive roles.
3. Connect the dataset to Slack, email, Zapier, Make or a webhook via Apify Integrations.

Each scheduled run then delivers only what is new.

#### A note on Workday

Workday is where most large enterprises post, and it is the biggest single source here — but its
public listing endpoint carries less than the others. Workday rows have the company, title,
location, requisition id and apply link, and **no department, employment type or description**;
those live behind a per-posting request the price of a result cannot cover. Workday also reports
posting age as text ("Posted Today", "Posted 30+ Days Ago"), so `postedAt` is exact when the age
is exact and `null` when it is not — a missing date rather than an invented one. If you filter on
posting date, Workday rows without a date are kept rather than dropped.

### How the memory actually works

Worth understanding before you build a workflow on it, because the details decide what you get.

**Where state lives.** Each feed keeps a small record in a named key-value store **inside your own
Apify account**. Nothing about your feed is stored on our side, and two customers watching the same
employers never affect each other.

**What gets remembered.** Only postings that were actually delivered to you, plus earlier
deliveries still inside the lookback window. A posting trimmed by `maxJobs` is *not* marked as
seen — it goes out on the next run instead. That is deliberate: the backlog drains rather than
disappearing.

**Why the window matters.** `lookbackDays` bounds both what counts as new and how big the memory
gets. Without it the seen-set would grow towards the registry's million-plus postings and outgrow
the 9 MB record limit. Postings age out of the memory on their own.

**Postings with no date.** Workday reports age as prose, so many of its rows have no timestamp.
Those are judged purely by the seen-set: reported once, then suppressed. They cannot be filtered by
`lookbackDays` because their age is genuinely unknown.

**Several feeds at once.** `feedId` names the memory. Run `remote-react`, `berlin-sales` and
`new-grad-ml` side by side and each keeps its own position. Reusing one `feedId` across different
filters is the one thing to avoid — the runs will consume each other's new postings.

### Which fields each platform gives you

| Field | Workday | SmartRecruiters | Greenhouse | Workable | Lever | Ashby | Breezy |
|---|---|---|---|---|---|---|---|
| title, company, location | yes | yes | yes | yes | yes | yes | yes |
| applyUrl | yes | yes | yes | yes | yes | yes | yes |
| postedAt | partial | yes | yes | yes | yes | yes | yes |
| department | no | yes | yes | yes | yes | yes | yes |
| employmentType | yes | yes | no | yes | yes | yes | yes |
| descriptionText | no | no | yes | yes | yes | yes | no |
| salary | no | no | no | no | no | yes | yes |

Measured from real runs. `includeDescription` is off by default here because an alert is usually
title, company and link — and because Workday and SmartRecruiters rows carry no description
whatever the setting.

### Recipes

**A morning digest.** Schedule daily, `lookbackDays: 2`, your keywords, and connect the dataset to
email or Slack. Each run delivers only what opened since yesterday.

**Hiring-intent alerts for sales.** `keywords: ["sales", "account executive"]`, `lookbackDays: 3`.
A company opening sales headcount is a company with budget, and you hear about it the day it posts.

**A watchlist of employers.** Put slugs in `companies` and raise `lookbackDays` to 14. Useful for
tracking competitors, acquisition targets, or companies you want to work for.

**Feeding a job board without duplicates.** Point it at your niche, run hourly, and append straight
to your database. `provider:companySlug:jobId` is stable, so you can key on it safely.

### Tuning

| Symptom | What to change |
|---|---|
| Too many results per run | Lower `maxJobs`, or narrow `keywords` |
| One employer dominates | Lower `maxJobsPerCompany` (defaults to 25) |
| Missing roles you expected | Raise `maxBoards`, or widen `lookbackDays` |
| Nothing new most runs | Run less often, or widen the filters — an idle feed is working correctly |
| Want depth on a few firms | Set `companies` and raise `maxJobsPerCompany` |

### Cost

Pay per delivered posting. A run that finds nothing new costs nothing, which makes frequent
scheduling cheap: the price tracks what actually changed rather than how often you look.

### If you only need one ATS

This Actor is built for breadth — it does not ask you which applicant tracking system a company
uses, because the registry already knows. When you only care about one platform, a narrower Actor
is cheaper to reason about and its input schema has fewer knobs:

| Actor | Coverage |
|---|---|
| [Workday Jobs Scraper](https://apify.com/starbright_overlap/workday-jobs-scraper) | 2,658 Workday boards — paste a careers URL |
| [SmartRecruiters Jobs Scraper](https://apify.com/starbright_overlap/smartrecruiters-jobs-scraper) | 4,334 SmartRecruiters boards |
| [Breezy HR Jobs Scraper](https://apify.com/starbright_overlap/breezy-hr-jobs-scraper) | 2,800 Breezy boards, with pay ranges |
| [Workable Jobs Scraper](https://apify.com/starbright_overlap/workable-jobs-scraper) | 1,710 Workable boards, with full descriptions |
| [Greenhouse Jobs Scraper](https://apify.com/starbright_overlap/greenhouse-jobs-scraper) | Any Greenhouse board by slug or URL, with full descriptions |

All of them emit the same row shape as this Actor, so datasets from any combination can be unioned
without a mapping layer.

### Use it as an API

Most people who rely on this Actor never open the Apify console after the first run — they call it
from their own code and read the rows straight back. One request in, job rows out, no polling and no
dataset id to chase:

```bash
curl -X POST "https://api.apify.com/v2/acts/starbright_overlap~new-jobs-feed/run-sync-get-dataset-items?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"feedId": "my-feed", "keywords": ["engineer"], "maxJobs": 200}'
```

```python
import requests

rows = requests.post(
    "https://api.apify.com/v2/acts/starbright_overlap~new-jobs-feed/run-sync-get-dataset-items",
    params={"token": "YOUR_TOKEN"},
    json={"feedId": "my-feed", "keywords": ["engineer"], "maxJobs": 200},
    timeout=300,
).json()

for r in rows:
    print(r["company"], "—", r["title"], "—", r["applyUrl"])
```

```javascript
const rows = await (await fetch(
  'https://api.apify.com/v2/acts/starbright_overlap~new-jobs-feed/run-sync-get-dataset-items?token=YOUR_TOKEN',
  { method: 'POST', headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({"feedId": "my-feed", "keywords": ["engineer"], "maxJobs": 200}) },
)).json();
```

Your token is at **Settings → Integrations** in the Apify console. Keep `maxJobs` set to what you
actually need — it is the cap that decides what the call costs.

#### Running it on a schedule instead

If you want the rows to arrive without asking, add a **Schedule** from the Actor page (Actions →
Schedule) and point a webhook at your endpoint. A daily schedule plus the `postedWithinDays` filter
is the usual setup for a job board or an alerting pipeline.

### Pricing

Pay per result — you are charged per new posting delivered. A run that finds nothing new costs nothing.

### Notes

- Boards are scanned largest-first, so a capped run watches the most active employers.
- Dead or rate-limited boards are skipped without aborting the run; details land in a `FAILED_BOARDS` record.
- The board registry is re-validated every month, so dead companies drop out on their own.

### Freshness, measured rather than promised

Some employers leave requisitions open for years — across the registry, 15% of Greenhouse postings and about half of the largest SmartRecruiters boards are over a year old. That matters more for an alert feed than for a search: a posting that has sat open since 2017 is not news.

`lookbackDays` (7 by default) is the guard. A posting older than that is never treated as new, even on a first run. Raise it if you run the feed less than weekly; lower it to 1 or 2 for a tight daily alert.

Postings with **no publication date** are judged by the seen-set instead, so they are reported once and then suppressed. That is most Workday rows.

### A note on Workday

Workday is where most large enterprises post, and it is the biggest single source here — Accenture, Walmart, CVS Health, Lowe's. Its public listing endpoint carries less than the others: Workday rows have company, title, location, requisition id and apply link, and **no department, employment type or description**. Workday also reports posting age as text ("Posted Today", "Posted 30+ Days Ago"), so `postedAt` is exact when the age is exact and `null` when it is not.

### Frequently asked

**How does it know what is new?**
It keeps a small record in your own account listing the postings it has already delivered, keyed by feed name. Each run returns what is not in that record. Nothing is stored on our side.

**What happens on the very first run?**
It seeds the feed and returns the most recent matching postings, so the first run is useful rather than empty. Every run after that returns only what appeared since.

**Can I run several alerts at once?**
Yes — give each its own `feedId` (`remote-react`, `berlin-sales`, `new-grad-ml`). Feeds have separate memories and do not consume each other's new postings.

**What if nothing new appeared?**
The run returns nothing and costs nothing, since billing is per delivered posting.

**I capped `maxJobs` and there were more new postings than that. Are they lost?**
No. Only what was actually delivered is remembered, so the remainder goes out on the next run. The backlog drains rather than disappearing.

**Which ATS platforms are covered?**
Workday, Greenhouse, SmartRecruiters, Workable, Lever, Ashby, Breezy, Rippling and Pinpoint HR — 27,681 company boards in total.

**Can I send new jobs straight to Slack or a sheet?**
Yes. Add an Apify Schedule to run it, then use the platform's integrations (Slack, Zapier, Make, webhooks, Google Sheets) on the dataset each run produces.

# Actor input Schema

## `keywords` (type: `array`):

Keep only postings whose title contains at least one of these. Case-insensitive. Leave empty for every job.

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

Drop postings whose title contains any of these, e.g. `senior`, `intern`.

## `locations` (type: `array`):

Keep only postings whose location contains one of these, e.g. `london`, `germany`.

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

Keep only postings flagged remote by the ATS or with a remote-looking location.

## `feedId` (type: `string`):

Each feed name remembers its own position. Use a different name per alert you run — e.g. `remote-react` and `berlin-sales` — so they don't consume each other's new postings.

## `lookbackDays` (type: `integer`):

A posting older than this is never treated as new, even on a first run. Raise it if you run the feed less than weekly.

## `companies` (type: `array`):

Board slugs, e.g. `databricks`. Leave empty to watch the whole registry of 17,000+ companies.

## `providers` (type: `array`):

Any of: greenhouse, personio, smartrecruiters, breezy, workday, lever, workable, ashby, rippling, pinpoint. Leave empty for all.

## `maxBoards` (type: `integer`):

Boards are scanned largest-first, so a capped run still watches the most active employers. Raise for full coverage of all 17,000+ boards.

## `maxJobs` (type: `integer`):

Hard cap on returned rows, so a run costs what you expect. Defaults to 300 — enough for a daily alert without burning free-tier credit. Raise it for a wider sweep.

## `includeDescription` (type: `boolean`):

Off by default — alerts are usually title, company and link. Turning it on makes runs slower and heavier.

## `concurrency` (type: `integer`):

Higher is faster but more likely to hit ATS rate limits.

## `maxJobsPerCompany` (type: `integer`):

Keeps one huge employer from filling the whole alert. Boards are scanned largest-first, so without this the biggest few use up the result budget and you stop hearing about everyone else.

## Actor input object example

```json
{
  "keywords": [
    "engineer"
  ],
  "remoteOnly": false,
  "feedId": "default",
  "lookbackDays": 7,
  "maxBoards": 300,
  "maxJobs": 300,
  "includeDescription": false,
  "concurrency": 8,
  "maxJobsPerCompany": 25
}
```

# Actor output Schema

## `newJobs` (type: `string`):

Postings that appeared since this feed's previous run, newest first.

## `failedBoards` (type: `string`):

Written only when a board was unreachable or rate-limited. The run itself is unaffected.

# 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 = {
    "keywords": [
        "engineer"
    ],
    "feedId": "default",
    "lookbackDays": 7,
    "maxBoards": 300,
    "maxJobs": 300,
    "maxJobsPerCompany": 25
};

// Run the Actor and wait for it to finish
const run = await client.actor("starbright_overlap/new-jobs-feed").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 = {
    "keywords": ["engineer"],
    "feedId": "default",
    "lookbackDays": 7,
    "maxBoards": 300,
    "maxJobs": 300,
    "maxJobsPerCompany": 25,
}

# Run the Actor and wait for it to finish
run = client.actor("starbright_overlap/new-jobs-feed").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 '{
  "keywords": [
    "engineer"
  ],
  "feedId": "default",
  "lookbackDays": 7,
  "maxBoards": 300,
  "maxJobs": 300,
  "maxJobsPerCompany": 25
}' |
apify call starbright_overlap/new-jobs-feed --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,starbright_overlap/new-jobs-feed"
        }
    }
}

```

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/uKBMYUZYlmdgzgkqo/builds/SD5WLCPG5T0F5Kpd5/openapi.json
