# Job Posting Verifier - Four ATS Link Checks (`clearsignaltools/job-posting-verifier`) Actor

Check selected Greenhouse, Lever, Ashby and SmartRecruiters job links against employer APIs. Get source-backed statuses and an optional cloud review in your saved task. Repeat without local CSV processing; no automatic removals.

- **URL**: https://apify.com/clearsignaltools/job-posting-verifier.md
- **Developed by:** [ClearSignal Tools](https://apify.com/clearsignaltools) (community)
- **Categories:** Jobs, Automation
- **Stats:** 3 total users, 2 monthly users, 100.0% runs succeeded, 1 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.00 / 1,000 resolved posting checks

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

## Job Posting Verifier

#### Greenhouse / Lever / Ashby / SmartRecruiters

**By ClearSignal Tools · Evidence you can inspect.**

[![Job Posting Verifier Actor status](https://apify.com/actor-badge?actor=clearsignaltools/job-posting-verifier)](https://apify.com/clearsignaltools/job-posting-verifier)

Keep saved job links up to date. Paste Greenhouse, Lever, Ashby or SmartRecruiters posting URLs and get a timestamped answer from the employer's public job-board API.

**$5 per 1,000 resolved checks · Unresolved checks carry no result fee · No third-party API key**

Built for job-board operators refreshing listings, recruiting researchers checking saved roles, and automation builders adding source evidence to their pipelines.

Unlike checking HTTP status alone, this Actor compares the posting ID with the employer's job-board API. A branded error page that returns HTTP 200 is not treated as an active posting.

#### What you get

- One reviewable status, reason and observation time per input URL.
- Provider, employer-board and posting identifiers for exact joins.
- Source API URL and board count after a successful board read.
- JSON, CSV and Excel dataset exports, plus a machine-readable run summary.
- API and scheduling support through Apify without another service account.

### Quick start

Paste public Greenhouse, Lever, Ashby or SmartRecruiters posting URLs into **Job posting URLs** and run. Export results as JSON, CSV or Excel from the dataset. The example URL is illustrative and can change status over time.

```json
{"jobUrls":["https://job-boards.greenhouse.io/webflow/jobs/7951430"]}
```

Start with the public [one-link quick-start example](https://apify.com/clearsignaltools/job-posting-verifier/examples/job-link-check-quick-start). Its task is capped at $0.006, 60 seconds and 128 MB. It is an owner-created demonstration, not a customer run or endorsement.

### Repeat a selected-list review in the cloud

For a niche job-board editor maintaining an existing shortlist, enable **Create cloud
review report**, save the input as your own Apify task, and run it. Open **Cloud review**
in the run's Output tab, or read the `REVIEW` JSON record in that run's storage. No CSV
download, local Python helper, ClearSignal operator or additional API key is needed.

```json
{"jobUrls":["https://job-boards.greenhouse.io/webflow/jobs/7951430"],"createReview":true}
```

The report keeps `listed` records marked `retain_for_now`. `not_listed`, `unlisted`,
`unknown`, `unsupported` and inputs not checked before a cap all require review. A failed
fetch never becomes a removal. This is **each run's current snapshot**, not automatic
change detection or a promise that a role is hiring. No data is removed or written to your
job board. Each run uses its own storage; no shared customer history is created.

Review mode accepts **at most 100 unique selected URLs** across the existing 20-board
limit; remove duplicate URLs, including tracking-query variants. The standard dataset
mode retains its 500-input limit. Start with a smaller selected list, not a whole-board
daily refresh. The four supported providers are Greenhouse, Lever, Ashby and SmartRecruiters.

Repeat by pressing **Run** on the same saved task. To automate, choose your task in
[Apify Schedules](https://docs.apify.com/actors/running/schedules), set a weekly cadence
and timezone, and deliberately enable it only after the first useful output. Set the
task's maximum charge to your intended per-run bill, timeout to 60 seconds and memory
to 128 MB. A schedule is **not a monthly spending cap**. Disable it when the list is no
longer useful; don't retry failed runs automatically. Nothing here activates a schedule.

At current prices, 50 fully resolved links cost **$0.25005 per run** at 128 MB and
**$1.00020 for four runs**; 100 cost $0.50005/run or $2.00020 for four. Five weekly runs
in a calendar month cost more. Every repeated resolved check is billed again, even if
unchanged. Review artifacts add no separate event fee. Customer post-run storage/API
usage follows Apify's plan rules. Unknown/unsupported results have no result fee; the
start fee remains. A full 10,000-link daily refresh would be about $1,500/month before
starts and is not the intended use case.

For developers, use an existing task with its saved cap (do not expose your token):

```bash
curl -X POST -H "Authorization: Bearer $APIFY_TOKEN" \
  "https://api.apify.com/v2/actor-tasks/$TASK_ID/runs?maxTotalChargeUsd=0.251&memory=128&timeout=60"
```

Use the returned run ID to check completion, then its `defaultKeyValueStoreId` to read
`/v2/key-value-stores/STORE_ID/records/REVIEW`. Verify `SUCCEEDED` and `summary.unprocessed`
before using the report. An older successful run is not the latest observation if the
next scheduled attempt failed. Keep run IDs; do not infer closed jobs from missing output.

**Alternatives:** direct provider APIs are free apart from your hosting/integration effort.
For Greenhouse-only lists, Winstonvale's verifier advertises a lower per-check price.
Jobs Feed API offers only-new/changed/removed feed events and can be a better fit for full
feed synchronization. ClearSignal's claim is a ready-to-read, four-provider selected-list
review in your cloud task, not uniquely available scheduling or the cheapest bulk feed.
See [Winstonvale](https://apify.com/winstonvale/source-backed-greenhouse-job-verifier) and
[Jobs Feed API](https://apify.com/sergeyfaraday/jobs-feed-api) for their current coverage and terms.

### From a spreadsheet or existing job feed

Use the free [CSV preparation and reconciliation helper](https://github.com/interestingcodelol/job-link-csv-helper/releases/latest)
to deduplicate employer URLs, prepare bounded batches and join exported results back to
your original rows. Its local fictional demonstration makes no network requests or paid
runs. Live Actor checks remain optional and use the pricing below.

Export or copy only the employer apply-URL column. In the Apify input form, paste one URL per line into **Job posting URLs**. Before running:

1. Keep at most 500 supported URLs from at most 20 employer boards.
2. Remove duplicate posting URLs; duplicate inputs are separate paid checks.
3. Set a maximum charge. At the base price, 100 resolved checks cost $0.50 plus the start fee; a full 500 costs $2.50 plus the start fee.
4. Export the resulting dataset and join it to your records by canonical `inputUrl`, never by output position. Query strings and fragments are removed.

A spending cap can return a partial dataset, so a source row with no matching result means **not checked**. It must not be treated as a closed job. Keep `unknown`, `unlisted` and `not_listed` distinct and review them before changing your feed.

### Interpret the evidence

#### SmartRecruiters: check the posting behind a malformed link

Some imported records contain an API-only `/postings/` segment in a public job URL,
such as `https://jobs.smartrecruiters.com/ScalableGmbH/postings/744000147966144`.
Runtime 0.2.3 can recover the company and numeric posting ID from that explicit shape,
then check the employer's complete public API inventory. It does not treat the broken
page's HTTP status as proof that the posting expired.

SmartRecruiters rows include `canonicalJobUrl`, `canonicalUrlBasis`, and
`inputUrlCorrection`. The canonical URL is derived from the provider's URL format;
page liveness is not separately checked. Keep joining results on `inputUrl`, which
preserves the supplied path while dropping tracking parameters. A canonical URL is
not an instruction to change your database automatically.

Public job URLs with a numeric ID and optional title suffix are also supported.
Empty boards, changing totals, duplicate IDs, wrong-company rows, and incomplete
pagination return `unknown`, with no result fee. Boards over 2,000 postings are
currently outside the completeness limit. This cannot recover arbitrary redirects,
LinkedIn short links, or postings behind a login.

| Status | Meaning |
| --- | --- |
| `listed` | Posting ID was present in the current employer API response. |
| `not_listed` | Posting ID was absent from a successfully fetched, nonempty board. |
| `unlisted` | Provider returned the posting with an explicit unlisted flag. |
| `unknown` | Provider failed, returned an empty or malformed response, or a completeness limit was reached. |
| `unsupported` | URL does not match a supported public ATS posting format. |

**A listed posting does not prove that an employer is actively hiring. Absence does not prove the role is closed or filled. This is not a ghost-job detector or employment verification service.**

Each result includes the input URL (without query parameters), observation time, status and reason. Supported results include provider, board and posting ID; successful board reads also include source API URL and posting count. Found postings include title and location when supplied by the provider.

### Example results

Live validation on September 7, 2026 returned the following. These are test observations, not customer endorsements; statuses can change.

| Provider | Public test board | Existing posting | Synthetic missing ID |
| --- | --- | --- | --- |
| Greenhouse | Webflow | `listed` | `not_listed` |
| Lever | Warner Music Group | `listed` | `not_listed` |
| Ashby | Ashby | `listed` | `not_listed` |

A cloud test also returned `unsupported` and HTTP-404 `unknown` rows free of result fees, then stopped at its configured budget after one resolved check.

```json
{"status":"listed","provider":"greenhouse","board":"webflow","postingId":"7951430","boardPostingCount":29,"sourceUrl":"https://boards-api.greenhouse.io/v1/boards/webflow/jobs"}
```

Abbreviated example; full rows also include the input URL, observation timestamp and explanation.

### Pricing

$0.005 per resolved posting check (`listed`, `not_listed`, or `unlisted`): **$5 per 1,000 resolved checks**. `unknown` and `unsupported` results have no result fee. Apify's Actor Start event costs $0.00005 per GB of allocated memory, minimum one event; default memory is 128 MB. Duplicate inputs are separate checks and are charged separately if resolved.

Set a maximum charge for the run (minimum budget $0.006). The Actor stops when another resolved check would exceed that budget; remaining inputs are not processed. A `SUMMARY` record reports requested and completed counts, statuses and whether the spending limit stopped processing.

### Supported inputs and limits

- Greenhouse: `job-boards.greenhouse.io/BOARD/jobs/ID` and `boards.greenhouse.io/BOARD/jobs/ID`.
- Lever: `jobs.lever.co/BOARD/UUID`.
- Ashby: `jobs.ashbyhq.com/BOARD/UUID`.
- SmartRecruiters: `jobs.smartrecruiters.com/COMPANY/ID` with optional `-title`, plus the explicitly recognized erroneous `COMPANY/postings/ID` shape. Available in runtime 0.2.3 and later.
- HTTPS only. Common `/apply` or `/application` suffixes are accepted where applicable.
- 1–500 input URLs and at most 20 distinct boards per run.
- Custom employer domains, LinkedIn, Indeed, Workday and login-only boards are unsupported.
- One board snapshot is reused within a run. Lever pagination is capped at 2,000 postings; reaching the cap produces `unknown` rather than an absence verdict. Responses over 8 MB are rejected.
- Regional Greenhouse/Lever URL variants are recognized but have not yet been live-validated; treat this support as experimental.
- Runtime 0.2.2 adds a shared 120-request/32 MB response budget and checks a 45-second
  work deadline between operations. A provider limit produces `unknown`, not an absence
  verdict; remaining inputs are not checked. `SUMMARY.resources` records requests,
  response bytes and the limit reason; `stoppedAtWorkLimit` and `unprocessed` identify partial work.
  An in-flight read can finish after the deadline; retain a 60-second platform timeout.
  These bounds apply to runtime 0.2.2; older pinned builds retain their original behavior.
- Upstream timeouts, restrictions and changes can produce `unknown`. No proxy or third-party API key is required.

### Automation and support

Run through the Apify API or schedule repeated runs in Apify Console. Download the default dataset or connect it to your workflow. Compare results across runs in your own system; this Actor does not maintain a historical database or send alerts itself.

For a reproducible issue, use this Actor's Issues tab and include a public example URL and run ID. Do not post credentials or private applicant information.

### FAQ

#### Does `listed` mean the employer is actively hiring?

No. It means the posting ID appeared in the employer's current public job-board API response. It does not verify hiring intent, vacancy availability or application review.

#### Does `not_listed` mean the role was filled?

No. It is evidence that the ID was absent from one successful, nonempty board response. Review the timestamp and source evidence before changing your records.

#### Why did a URL return `unknown`?

The provider timed out, rejected the request, returned malformed or empty data, or reached a completeness guard. `unknown` has no result fee and should be retried or reviewed, not converted into a closed-job verdict.

#### Can I start from CSV or Excel?

Yes. Paste the employer apply-URL column into **Job posting URLs**, then export the resulting dataset as JSON, CSV or Excel. Deduplicate first and join results back by canonical `inputUrl`.

#### Does the Actor edit or close job postings?

No. It only reads public provider responses and writes evidence to the run's default dataset plus a `SUMMARY` record. It does not submit applications, edit provider data or maintain a separate history.

#### What happens if my spending cap stops the run?

The dataset can be partial. Any source row without a matching result is **not checked**; never treat it as `not_listed`. The `SUMMARY` record reports whether the cap stopped processing.

# Actor input Schema

## `jobUrls` (type: `array`):

Paste 1–500 full HTTPS job-posting URLs from Greenhouse, Lever, Ashby or SmartRecruiters, one per line. Maximum 20 employer boards. Duplicate supported URLs are separate paid checks.

## `createReview` (type: `boolean`):

Optional HTML and JSON current-cycle review in this run. Maximum 100 unique selected URLs. Failed/missing checks stay in review; no automatic removals, history comparison or schedule.

## Actor input object example

```json
{
  "jobUrls": [
    "https://job-boards.greenhouse.io/webflow/jobs/7951430"
  ],
  "createReview": false
}
```

# Actor output Schema

## `checks` (type: `string`):

Per-URL status with source evidence. Listed means present in the public API, not proof of active hiring.

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

Requested and completed counts and spending-limit stop indicator.

## `review` (type: `string`):

Current-cycle manual review for selected lists. Enable createReview; not a change-over-time or removal report.

## `reviewJson` (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 = {
    "jobUrls": [
        "https://job-boards.greenhouse.io/webflow/jobs/7951430"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("clearsignaltools/job-posting-verifier").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 = { "jobUrls": ["https://job-boards.greenhouse.io/webflow/jobs/7951430"] }

# Run the Actor and wait for it to finish
run = client.actor("clearsignaltools/job-posting-verifier").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 '{
  "jobUrls": [
    "https://job-boards.greenhouse.io/webflow/jobs/7951430"
  ]
}' |
apify call clearsignaltools/job-posting-verifier --silent --output-dataset

```

## MCP server setup

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

```

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/JcJO2VJyJmlbkLHfO/builds/iY1PGkYUOzZNBpIkA/openapi.json
