# Hiring Signals Tracker – ATS Job Postings (`parviz_a/my-actor`) Actor

Track new job postings from companies on Greenhouse, Lever, and Ashby — normalized into one feed, with built-in change detection.

- **URL**: https://apify.com/parviz\_a/my-actor.md
- **Developed by:** [Parviz Abbasov](https://apify.com/parviz_a) (community)
- **Categories:**
- **Stats:** 2 total users, 1 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $20.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?

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

## Hiring Signals Tracker – ATS Job Postings (Greenhouse, Lever, Ashby)

Track open roles at any company that hires through **Greenhouse, Lever, or Ashby** — pulled straight from each company's own official job-board API, normalized into one clean schema, and optionally reduced to just the jobs that are **new since your last run**.

Hiring activity is one of the strongest public buying signals in B2B sales, and one of the most useful sourcing signals in recruiting. This Actor turns "is this company hiring, and for what?" into a structured, automatable dataset instead of a manual careers-page check.

### Features

- **Three ATS platforms, one schema.** Greenhouse, Lever, and Ashby jobs all come back with the same fields, so you can compare companies side by side.
- **Official, public JSON APIs only.** No browser automation, no CAPTCHA-solving, no scraping of rendered HTML — this reads the same structured feeds those companies use to power their own careers pages.
- **Monitoring mode.** Turn on "only new since last run" and schedule the Actor daily or weekly. You'll only get (and only pay for) newly-posted jobs — a true hiring-signal alert feed.
- **Keyword and location filters.** Narrow results to the roles and geographies you care about.
- **Optional webhook.** Push a summary of new jobs to Slack, Zapier, or Make after every run.
- **AI-agent / MCP ready.** Clean, flat JSON output that's easy for an LLM agent to reason over directly.

### Use cases

- **Sales & RevOps:** flag target accounts that are hiring in a department relevant to your product (e.g. "hiring for Data Engineering" as a signal for a data-tooling vendor).
- **Recruiting & staffing agencies:** track open requisitions across client or prospect companies without checking each careers page by hand.
- **Competitive intelligence:** watch which teams a competitor is investing in.
- **Job seekers & career changers:** monitor a shortlist of target employers for new openings.
- **AI agents:** give an agent a live, structured view of a company's hiring activity.

### How it works

1. You give the Actor a list of companies, each with the ATS platform it uses and its "board token" (the slug in its careers-page URL — e.g. for `jobs.lever.co/acme`, the token is `acme`).
2. The Actor calls that company's public ATS job-board API directly.
3. Jobs are normalized into a common schema, optionally filtered by keyword/location, and (in monitoring mode) compared against what was seen on the previous run.
4. Results are written to the dataset; new jobs can also trigger a webhook.

Not sure which ATS a company uses? Set `atsType` to `"auto"` and the Actor will try Greenhouse, then Lever, then Ashby with the token you provide.

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `companies` | array | 3 example companies | Companies to track: `name`, `atsType` (`greenhouse`/`lever`/`ashby`/`auto`), `boardToken`. |
| `keywords` | array of strings | `[]` | Only return jobs whose title contains one of these words. |
| `locations` | array of strings | `[]` | Only return jobs whose location contains one of these strings. |
| `maxJobsPerCompany` | integer | `200` | Cap on matching jobs returned per company per run. |
| `onlyNewSinceLastRun` | boolean | `false` | Monitoring mode: only return/charge for newly-seen jobs. |
| `includeDescriptionSnippet` | boolean | `true` | Include a short (~300 character) plain-text description snippet. |
| `webhookNotificationUrl` | string | `""` | POST a JSON summary of new jobs here after each run. |

The default input runs successfully out of the box with no changes needed.

### Output

Each dataset item looks like this:

```json
{
  "company": "Ramp",
  "atsSource": "ashby",
  "jobId": "8f1e2c3a-...",
  "title": "Senior Backend Engineer, Payments",
  "department": "Engineering",
  "location": "New York City",
  "remote": false,
  "seniorityGuess": "Senior",
  "postedAt": "2026-08-12T00:00:00.000Z",
  "url": "https://jobs.ashbyhq.com/ramp/8f1e2c3a-...",
  "salaryText": "$180,000 - $230,000",
  "isNew": true,
  "scrapedAt": "2026-08-29T10:15:00.000Z",
  "descriptionSnippet": "Ramp is looking for a Senior Backend Engineer to join our Payments team…"
}
```

`seniorityGuess` is a heuristic inferred from the job title text (Intern, Junior/Associate, Manager, Senior, Principal/Staff, Director, VP, Executive, or Unspecified) — it is not sourced from the ATS and should be treated as a helpful hint, not ground truth.

Any company the Actor could not reach (bad token, ATS outage, etc.) is logged to a separate `ERRORS` dataset rather than mixed into your priced results — open it from the run's **Storage** tab if a company came back empty.

### Pricing

This Actor uses Apify's **pay-per-event** model:

- **Actor start** — a small flat fee automatically charged once per run.
- **Per job record returned** — charged for each row written to your results (in monitoring mode, only for genuinely new jobs).

You only pay for data you actually receive. See the Actor's **Pricing** tab in Apify Console for current per-event rates.

### FAQ

**Do I need API keys for Greenhouse/Lever/Ashby?**
No. This Actor only reads each platform's public job-board feed — the same one that powers each company's own public careers page.

**What if a company changes its ATS or board token?**
The next run for that company will fail gracefully and log an entry to the `ERRORS` dataset instead of breaking the whole run. Update the `boardToken` in your input once you have the new one.

**Can I track a company that uses an ATS other than these three?**
Not yet in this version. Greenhouse, Lever, and Ashby cover a large share of tech and venture-backed companies; support for additional platforms may be added in a future version.

**How "new" is "new" in monitoring mode?**
A job is new the first time this Actor sees that exact job ID for that company. Run it on a schedule (e.g. daily) for a continuous hiring-signal feed.

### Limitations

- Covers Greenhouse, Lever, and Ashby only.
- `remote` and `seniorityGuess` are best-effort text heuristics, not authoritative fields from the ATS.
- Full job descriptions are not stored — only a short snippet, by design.
- If a company disables its public job-board feed, that company's jobs will not be retrievable by this Actor or by any other tool using the same public API.

### Troubleshooting

| Symptom | Likely cause | Fix |
|---|---|---|
| A company returns 0 jobs | Wrong `boardToken`, or the company currently has no open roles | Verify the token from that company's careers-page URL |
| A company appears in the `ERRORS` dataset | Wrong `atsType`, invalid token, or ATS API outage | Re-check the ATS platform; try `atsType: "auto"` |
| Monitoring mode returns nothing on the second run | Expected if no new jobs were posted since the last run | This is normal — it means no new hiring signal |

### API usage examples

**cURL**

```bash
curl "https://api.apify.com/v2/acts/YOUR_USERNAME~hiring-signals-tracker/run-sync-get-dataset-items?token=YOUR_API_TOKEN" \
  -X POST -H "Content-Type: application/json" \
  -d '{"companies":[{"name":"Ramp","atsType":"ashby","boardToken":"ramp"}]}'
```

**Python**

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_API_TOKEN")
run = client.actor("YOUR_USERNAME/hiring-signals-tracker").call(run_input={
    "companies": [{"name": "Ramp", "atsType": "ashby", "boardToken": "ramp"}],
    "onlyNewSinceLastRun": True,
})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["company"], "-", item["title"])
```

**JavaScript**

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: 'YOUR_API_TOKEN' });
const run = await client.actor('YOUR_USERNAME/hiring-signals-tracker').call({
  companies: [{ name: 'Ramp', atsType: 'ashby', boardToken: 'ramp' }],
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

# Actor input Schema

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

List of companies to check. Each entry needs: name (any label), atsType ("greenhouse", "lever", "ashby", or "auto"), and boardToken (the slug from that company's careers page URL, e.g. jobs.lever.co/<boardToken>). Use "auto" if you are not sure which ATS a company uses – the Actor will try Greenhouse, then Lever, then Ashby with the token you give it.

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

Only return jobs whose title contains at least one of these words (case-insensitive). Leave empty to return all open jobs.

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

Only return jobs whose location text contains at least one of these strings (case-insensitive), e.g. "Remote", "New York", "United Kingdom". Leave empty to return all locations.

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

Safety cap on how many matching jobs to return per company, per run.

## `onlyNewSinceLastRun` (type: `boolean`):

When ON, the Actor remembers (per Actor, tied to your Apify account) which jobs it has already seen and only returns – and charges for – newly-posted jobs. Turn this ON when you schedule the Actor to run daily or weekly, to track hiring signals over time. Turn it OFF to get a full snapshot of every current opening (useful for the first run, or for a one-off audit).

## `includeDescriptionSnippet` (type: `boolean`):

Include a short (~300 character) plain-text snippet of the job description in the output. Full job descriptions are never stored.

## `webhookNotificationUrl` (type: `string`):

If set, after each run the Actor sends one POST request with a JSON summary of newly found jobs to this URL (e.g. a Zapier, Make.com, or Slack incoming-webhook URL). Leave blank to disable.

## Actor input object example

```json
{
  "companies": [
    {
      "name": "Greenhouse",
      "atsType": "greenhouse",
      "boardToken": "greenhouse"
    },
    {
      "name": "Ramp",
      "atsType": "ashby",
      "boardToken": "ramp"
    },
    {
      "name": "Wealthsimple",
      "atsType": "lever",
      "boardToken": "wealthsimple"
    }
  ],
  "keywords": [],
  "locations": [],
  "maxJobsPerCompany": 200,
  "onlyNewSinceLastRun": false,
  "includeDescriptionSnippet": true,
  "webhookNotificationUrl": ""
}
```

# Actor output Schema

## `results` (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("parviz_a/my-actor").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("parviz_a/my-actor").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 parviz_a/my-actor --silent --output-dataset

```

## MCP server setup

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

```

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/JdOWmNz37na1RRpPz/builds/ucjPfsuHLiAIPHVC1/openapi.json
