# SmartRecruiters Jobs Scraper & API (`snapperwapper/smartrecruiters-jobs-scraper`) Actor

Extract structured SmartRecruiters listings with filters and change detection.

- **URL**: https://apify.com/snapperwapper/smartrecruiters-jobs-scraper.md
- **Developed by:** [snapperwapper](https://apify.com/snapperwapper) (community)
- **Categories:** Jobs, Lead generation, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per usage

This Actor is paid per platform usage. The Actor is free to use, and you only pay for the Apify platform usage, which gets cheaper the higher subscription plan you have.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-usage

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

## SmartRecruiters Jobs Scraper & API

Extract clean, structured job listings from public SmartRecruiters career boards through an Apify Actor and API. **SmartRecruiters Jobs Scraper & API** reads SmartRecruiters's public job-board API directly, so it is fast, deterministic, and needs no browser or proxy.

Use this Actor to monitor employers, build job-search products, enrich recruiting data, power alerts, research hiring trends, or feed open roles into your own database and automation stack. Give it one or many career-board URLs (or company slugs), apply optional filters, and receive a consistent dataset with full descriptions and application links.

### What this SmartRecruiters jobs scraper does

- Scrapes multiple SmartRecruiters job boards in one run.
- Uses only the platform's public job-board API—no browser, cookies, login, or proxy.
- Normalizes every listing to the same rich jobs schema used by this Actor family.
- Filters by keyword, location, and remote/hybrid status.
- Returns full HTML and plain-text descriptions when requested.
- Deduplicates jobs and respects a global maximum-result limit.
- Uses bounded concurrency, request timeouts, and exponential retries.
- Emits an `ERROR` row for a failed or invalid board without discarding successful boards.
- Optionally persists job fingerprints and labels listings `NEW`, `CHANGED`, or `CLOSED`.
- Strictly accepts only SmartRecruiters company slugs and canonical HTTPS board URLs; arbitrary URLs are never fetched.

### Use cases

- **Job alerts and newsletters:** run on a schedule and notify subscribers about newly published jobs.
- **Competitive hiring intelligence:** compare roles, locations, departments, and hiring velocity across companies.
- **Recruiting and sourcing:** collect fresh vacancies for candidate matching or outreach workflows.
- **Job aggregators:** import structured postings and direct application URLs into a search product.
- **Market research:** analyze remote work, skill demand, employment types, and geographic expansion.
- **Data pipelines:** export to Google Sheets, Airtable, Slack, webhooks, cloud storage, or a database.

### Input

The default run uses the known public example board `https://jobs.smartrecruiters.com/SmartRecruiters`.

```json
{
  "boardUrls": ["https://jobs.smartrecruiters.com/SmartRecruiters", "SmartRecruiters"],
  "keywords": ["engineer", "developer"],
  "locations": ["remote", "new york"],
  "remoteOnly": false,
  "maxResults": 500,
  "includeDescription": true,
  "detectChanges": true,
  "emitClosedJobs": true,
  "concurrency": 5,
  "requestTimeoutSecs": 30,
  "maxRequestRetries": 3
}
```

| Field | Type | Default | Description |
| --- | --- | --- | --- |
| `boardUrls` | string\[] | example board | One or more canonical SmartRecruiters board URLs or company slugs. Required. Duplicate boards are fetched once. |
| `keywords` | string\[] | `[]` | Case-insensitive OR filter over title, company, department, team, and description. |
| `locations` | string\[] | `[]` | Case-insensitive OR filter over normalized location fields. |
| `remoteOnly` | boolean | `false` | Keep roles marked remote or hybrid. |
| `maxResults` | integer | `1000` | Maximum job/change rows across all boards (1–100,000). Error rows remain visible. |
| `includeDescription` | boolean | `true` | Include complete `descriptionHtml` and `descriptionText`. Disable for smaller output. |
| `detectChanges` | boolean | `false` | Compare jobs with persistent `JOB_STATE` in the default key-value store. |
| `emitClosedJobs` | boolean | `true` | Emit missing jobs as `CLOSED` after a successful board refresh. |
| `concurrency` | integer | `5` | Maximum simultaneous board/detail requests (1–20). |
| `requestTimeoutSecs` | integer | `30` | Timeout per API request (1–120 seconds). |
| `maxRequestRetries` | integer | `3` | Retries for timeouts, network failures, HTTP 408/429, and 5xx responses (0–10). |

Filters are applied after normalization. Values within `keywords` and `locations` use OR logic; the different filter groups use AND logic. The Actor treats hybrid positions as eligible when `remoteOnly` is enabled because many ATS boards publish hybrid as a remote-capable workplace type.

### Output

Each dataset item has `recordType: "JOB"` or `recordType: "ERROR"`. A normalized job resembles:

```json
{
  "recordType": "JOB",
  "changeType": "NEW",
  "platform": "SmartRecruiters",
  "board": "SmartRecruiters",
  "id": "platform-job-id",
  "title": "Senior Software Engineer",
  "company": "Example Company",
  "location": "Remote",
  "locations": ["Remote"],
  "city": null,
  "region": null,
  "country": null,
  "workplaceType": "remote",
  "remote": true,
  "department": "Engineering",
  "team": "Platform",
  "employmentType": "Full-time",
  "experienceLevel": "Senior",
  "publishedAt": "2026-01-15T12:00:00.000Z",
  "updatedAt": "2026-01-20T12:00:00.000Z",
  "jobUrl": "https://example.invalid/job",
  "applyUrl": "https://example.invalid/job/apply",
  "descriptionHtml": "<p>Full job description…</p>",
  "descriptionText": "Full job description…",
  "salary": {
    "min": 120000,
    "max": 160000,
    "currency": "USD",
    "period": "year"
  },
  "scrapedAt": "2026-01-20T12:05:00.000Z"
}
```

#### Output fields

| Field | Description |
| --- | --- |
| `recordType` | `JOB` for a listing or `ERROR` for an isolated board failure. |
| `changeType` | `NEW`, `CHANGED`, or `CLOSED` when change detection applies; omitted for an unchanged current job. |
| `platform`, `board`, `id` | Source ATS, normalized company slug, and platform job identifier. |
| `title`, `company` | Job title and employer name. Some APIs expose only a board slug, which becomes the company fallback. |
| `location`, `locations` | Primary location and all exposed locations. |
| `city`, `region`, `country` | Structured geography when the upstream API provides it. |
| `workplaceType`, `remote` | Remote, hybrid, or on-site metadata and a normalized remote flag. |
| `department`, `team` | Organizational grouping when available. |
| `employmentType`, `experienceLevel` | Contract/employment and seniority information when available. |
| `publishedAt`, `updatedAt` | Normalized ISO timestamps exposed by the source. |
| `jobUrl`, `applyUrl` | Public posting and direct application links. |
| `descriptionHtml`, `descriptionText` | Full rich and plain descriptions when `includeDescription` is enabled. |
| `salary` | Structured min/max/currency/period or source-provided compensation text when published. |
| `scrapedAt` | UTC timestamp for this observation. |
| `boardInput`, `error` | Original input and readable failure message on `ERROR` records. |

Fields unavailable from a particular posting are omitted rather than guessed.

### Change detection: NEW, CHANGED, and CLOSED

Set `detectChanges` to `true` to compare the current successful board snapshots with the Actor's previous `JOB_STATE` record in its default key-value store:

- `NEW`: the job was not in the previous state.
- `CHANGED`: a normalized field changed since the previous run.
- no label: the job is still open and unchanged.
- `CLOSED`: the job was present before but is absent from a board that refreshed successfully.

State is preserved for any board that errors, so a temporary API failure cannot falsely close all of its jobs. For dependable monitoring, reuse the same Actor task or key-value store. A fresh run with a fresh default store has no earlier baseline and will label all current jobs `NEW`.

### Run the SmartRecruiters jobs API

Replace `YOUR_TOKEN` and `YOUR_USERNAME~ACTOR_NAME` with your Apify values.

#### curl

```bash
curl -X POST \
  "https://api.apify.com/v2/acts/YOUR_USERNAME~ACTOR_NAME/run-sync-get-dataset-items?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"boardUrls":["https://jobs.smartrecruiters.com/SmartRecruiters"],"keywords":["engineer"],"maxResults":100}'
```

#### JavaScript

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

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('YOUR_USERNAME/ACTOR_NAME').call({
    boardUrls: ['https://jobs.smartrecruiters.com/SmartRecruiters'],
    keywords: ['engineer'],
    includeDescription: true,
    maxResults: 100,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

#### Python

```python
import os
from apify_client import ApifyClient

client = ApifyClient(os.environ['APIFY_TOKEN'])
run = client.actor('YOUR_USERNAME/ACTOR_NAME').call(run_input={
    'boardUrls': ['https://jobs.smartrecruiters.com/SmartRecruiters'],
    'keywords': ['engineer'],
    'includeDescription': True,
    'maxResults': 100,
})
items = client.dataset(run['defaultDatasetId']).list_items().items
print(items)
```

### Schedules, webhooks, and integrations

Create an Apify **Task** with stable inputs, then add an hourly, daily, or weekly **Schedule**. Enable `detectChanges` for a monitoring workflow. Attach a run-succeeded **Webhook** to trigger your endpoint, Make, Zapier, n8n, or another Actor. Fetch the resulting dataset through the API, or use Apify integrations to export jobs to Google Sheets, Slack, Airtable, GitHub, cloud storage, and other destinations.

For alerts that should include only changed records, filter dataset items on `changeType` after the run. Keep the task's storage configuration stable so subsequent runs see the same change-detection state.

### Pricing

This Actor is designed for pay-per-event (PPE) pricing. During launch, the recommended event price is **$0.001 per job** (event name: `job`; equivalently `/bin/bash.001` per job in launch-pricing shorthand). The implementation charges that event only for `JOB` records when running on the Apify platform; local development runs are not charged. Apify platform compute and storage charges may also apply according to your plan. Error rows should not be charged as jobs. Review and adjust PPE pricing as usage patterns and API costs become clear.

### Why an API-based Actor?

Direct public job-board APIs return structured data with much less compute than browser automation. That normally means faster runs, fewer moving parts, no proxy cost, and predictable extraction. Bounded concurrency protects both the upstream service and the run; retry logic handles transient rate limits and server issues without hiding permanent errors.

### Limitations

- This Actor only reads publicly available SmartRecruiters boards and does not access private, draft, authenticated, or applicant data.
- It accepts no arbitrary endpoint or hostname. Custom career domains and redirects outside the canonical host may need their underlying company slug entered directly.
- Available fields depend on what the employer publishes through SmartRecruiters. Salary, seniority, geography, and update times may be absent.
- Keyword filtering with descriptions disabled cannot match text that exists only inside a description.
- A job is considered closed only after it disappears from a board that completed successfully; platform publishing delays can affect detection timing.
- Upstream API changes, outages, or rate limits can temporarily affect results. Each failed board appears as an explicit error row.
- Public job data can change; follow the source site's terms, robots policies where applicable, and relevant laws when storing or redistributing it.

### FAQ

#### Does this SmartRecruiters scraper require a proxy or browser?

No. It calls SmartRecruiters's public job-board API with regular HTTPS requests.

#### Can I scrape several companies at once?

Yes. Add any number of supported board URLs or slugs to `boardUrls`. Work is bounded by `concurrency`, and one failed company does not kill successful siblings.

#### Are full descriptions included?

Yes, by default. Set `includeDescription` to `false` to omit HTML and text descriptions and reduce payload size.

#### How are duplicates handled?

The Actor removes repeated inputs and deduplicates normalized jobs by platform, board, and job ID before writing output.

#### Why did every job show as NEW?

The current key-value store had no earlier snapshot. Run subsequent checks with the same task/store to get meaningful `CHANGED` and `CLOSED` events.

#### Can a board outage mark every job CLOSED?

No. Failed boards emit an `ERROR` item and keep their prior state. Closures are calculated only for boards fetched successfully.

#### Can I pass any API URL?

No. For SSRF safety the parser accepts only a simple company slug or a strict canonical HTTPS SmartRecruiters career-board URL. It constructs the public API endpoint internally.

#### How should I choose `maxResults`?

Set it high enough for the combined filtered boards. The cap applies globally after filtering and deduplication. Isolated error records are still retained for diagnostics.

#### Is this an official SmartRecruiters product?

No. It is an independent Apify Actor that consumes publicly exposed job-board data and is not endorsed by SmartRecruiters.

# Actor input Schema

## `boardUrls` (type: `array`):

Public career-board URLs or platform company slugs. Only this platform's canonical HTTPS URLs are accepted.

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

Keep jobs matching at least one case-insensitive keyword in title, company, department, team, or description.

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

Keep jobs matching at least one case-insensitive location term.

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

Keep remote and hybrid jobs only.

## `maxResults` (type: `integer`):

Maximum job and change rows; per-board errors are always retained.

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

Include full HTML and plain-text descriptions. Disable for smaller, faster output.

## `detectChanges` (type: `boolean`):

Compare with persistent key-value state and label NEW, CHANGED, and CLOSED jobs.

## `emitClosedJobs` (type: `boolean`):

When change detection is on, output jobs that disappeared from successful boards as CLOSED.

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

Maximum simultaneous board and detail requests.

## `requestTimeoutSecs` (type: `integer`):

Timeout in seconds for each API request.

## `maxRequestRetries` (type: `integer`):

Retries for timeouts, network errors, rate limits, and server errors.

## Actor input object example

```json
{
  "boardUrls": [
    "https://jobs.smartrecruiters.com/SmartRecruiters"
  ],
  "keywords": [],
  "locations": [],
  "remoteOnly": false,
  "maxResults": 1000,
  "includeDescription": true,
  "detectChanges": false,
  "emitClosedJobs": true,
  "concurrency": 5,
  "requestTimeoutSecs": 30,
  "maxRequestRetries": 3
}
```

# 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 = {
    "boardUrls": [
        "https://jobs.smartrecruiters.com/SmartRecruiters"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("snapperwapper/smartrecruiters-jobs-scraper").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 = { "boardUrls": ["https://jobs.smartrecruiters.com/SmartRecruiters"] }

# Run the Actor and wait for it to finish
run = client.actor("snapperwapper/smartrecruiters-jobs-scraper").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 '{
  "boardUrls": [
    "https://jobs.smartrecruiters.com/SmartRecruiters"
  ]
}' |
apify call snapperwapper/smartrecruiters-jobs-scraper --silent --output-dataset

```

## MCP server setup

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

```

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/xuYssckK5fKLU5nX0/builds/mOgdjN90lUEjZjYU8/openapi.json
