# Hiring Signal (`wheresprada/hiring-signal`) Actor

- **URL**: https://apify.com/wheresprada/hiring-signal.md
- **Developed by:** [Mahruf Hossain](https://apify.com/wheresprada) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 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?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## 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.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## v0.3 fix (October 2026)

Fixes the false failure encountered with 193 live job listings: Apify's dataset metadata `itemCount` can lag behind newly written items for up to five seconds. The Actor now checks actual stored dataset items using paginated reads before deciding whether to commit a monitoring snapshot. Genuine incomplete dataset writes still fail safely, without advancing monitoring state.

## Hiring Signals — deployable Apify Actor v0.2

This Actor reads **published** job listings from official public Greenhouse and Lever APIs. It supports two modes:

- **scan** (default, stateless and suitable for public use): returns all current postings as `changeType=current`. Does not retain the user's watchlist or state.
- **monitor** (private scheduled use): compare successive successful scans using a user-generated private UUID v4; first run emits a baseline and later runs emit new, updated, and *confirmed removed* listings. Removal requires two successful scans, because job feeds can be temporarily incomplete. The snapshots live in a named Apify KV store owned by the Actor publisher.

### Prerequisites

- An [Apify account](https://console.apify.com/), Node.js and the [Apify CLI](https://docs.apify.com/cli/docs/installation).
- Python 3.12+ for local runs (optional; cloud builds install Python).
- No AI API key and no Greenhouse/Lever password needed.

### Local tests (offline, do not need API access)

```bash
python -m unittest discover -s tests -v
```

### Deploy from your computer

1. Unzip project into its own folder (keep `.actor/` and `src/` inside).
2. Open a terminal in that folder, run `npm install -g apify-cli`.
3. Run `apify login` (login using your own account; never share tokens in chats).
4. Run `apify push`. This creates/builds the Actor in your account.
5. In [Apify Console](https://console.apify.com/), open Development > My Actors > Hiring Signals; review the build log, then run it using `SAMPLE_INPUT_SCAN.json` with the example H1 (Lever) and IMC (Greenhouse) boards, then replace them with boards you want to monitor.
6. Inspect the output Dataset and `SUMMARY` record in the default key-value store.

`SAMPLE_INPUT_SCAN.json` includes real employer board slugs (H1 on Lever and IMC on Greenhouse) observed from public employer pages on October 9, 2026. Sources can change or block access; if a board fails, inspect the run log and confirm its public URL before changing the token.

For `monitor` mode, create a private watchlist UUID on your PC:

```bash
python -c "import uuid; print(uuid.uuid4())"
```

Use the generated value as `monitorId` in `SAMPLE_INPUT_MONITOR.json`; keep it unchanged on each run. **Do not share it publicly.** Save the complete input as an Apify Task, run once to establish baseline, then schedule that *task* daily. Don't run the same monitor concurrently. The first run outputs current jobs unless you set `includeBaseline=false`.

### Quality/safety

- Invalid board tokens and network failures leave prior state unchanged. Suspiciously empty or drastically smaller responses do not trigger removals in monitor mode.
- Removals are reported only after **two successful consecutive checks** of the board that both lack the same posting. They mean the posting disappeared, not proof it was filled.
- The Actor exports only information supplied by job boards, never invents salary/location fields.
- Dataset record completeness is checked by actually reading stored item pages (not the delayed dataset metadata counter). If an Apify run's maximum spending budget genuinely truncates billed dataset output, the Actor **does not advance** the monitoring snapshot; partial output can still be billed and reappear next run, so set spending limits appropriately.
- Production use of shared `monitor` mode by unrelated paying customers needs a fully tenant-isolated state design. Do **not** publish monitor mode as a customer-data service without that redesign. Public paid users should use `scan` mode until tenant isolation is in place.

### Monetization

Use Apify Console → Actor → Publishing → Monetization → Pay per event. Set only `apify-default-dataset-item` as a paid result event (start by testing $0.002 per scan result (and confirm you cover compute costs)), and disable or price `apify-actor-start` at $0 if you intend only per-row charges. The SDK automatically bills default-dataset items for this synthetic event. A 1,000-result scan at $0.002/item incurs $2 in gross user charges; calculate platform fees and run costs before publishing. Do not promise results or income.

The Actor is not publicly live until you finish Apify's publishing steps and account requirements. Payouts follow Apify's schedule, **not instant cash**.

Official docs: [Actor deployment](https://docs.apify.com/actors/development/deployment) · [Actor schedules](https://docs.apify.com/actors/running/schedules) · [Pay per event](https://docs.apify.com/actors/publishing/monetize/pay-per-event) · [Publishing](https://docs.apify.com/actors/publishing/publish) · [Greenhouse](https://docs.greenhouse.io/job-board.html) · [Lever](https://github.com/lever/postings-api)

# Actor input Schema

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

Scan exports all current jobs on each run. Monitor compares against a persistent snapshot using an individual UUID.

## `monitorId` (type: `string`):

A unique, stable UUID for this watchlist. Keep private and reuse on future runs; generate with Python uuid.uuid4(). A default shared identifier is unsafe.

## `boards` (type: `array`):

Each site is the Greenhouse board token or Lever site slug, not a full website URL.

## `includeBaseline` (type: `boolean`):

Monitor mode: emit current jobs on the first run. If disabled, first run only saves a baseline. Scan mode always emits current jobs.

## Actor input object example

```json
{
  "mode": "scan",
  "monitorId": "",
  "boards": [
    {
      "platform": "lever",
      "site": "h1",
      "company": "H1",
      "region": "global"
    },
    {
      "platform": "greenhouse",
      "site": "imc",
      "company": "IMC",
      "region": "global"
    }
  ],
  "includeBaseline": true
}
```

# Actor output Schema

## `jobChanges` (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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("wheresprada/hiring-signal").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 = {}

# Run the Actor and wait for it to finish
run = client.actor("wheresprada/hiring-signal").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 '{}' |
apify call wheresprada/hiring-signal --silent --output-dataset

```

## MCP server setup

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

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/dCdIVlMrlSYZv4kDy/builds/2SP3K6PadgBJInGhb/openapi.json
