# SmartRecruiters Scraper \[From $0.8💰] | Job Data (`ahmed_jasarevic/jobs-smartrecruiters-scraper`) Actor

Scrape all open job postings from any SmartRecruiters company board (jobs.smartrecruiters.com) via the public Posting API. Title, department, location, remote/hybrid, employment type, date, apply URL, full description and salary. Multi-company runs.

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

## Pricing

from $0.70 / 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.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## SmartRecruiters Scraper — All Open Jobs From jobs.smartrecruiters.com

Scrape **every open job posting from any SmartRecruiters company board** (`jobs.smartrecruiters.com`) — many boards in one run — through SmartRecruiters' own **public, keyless Posting REST API**: no browser, no proxy, no CAPTCHA, no anti-bot bypass. A measured 1,000-posting run over Bosch Group (**4,825 live postings**) and Sportradar finished in **7.1–8.2 seconds** with **42 structured fields** per row.

***

### Use Cases: Hiring Signals, Remote Job Feeds And Recruiting Intelligence

- **Hiring signals data for lead generation** — a fresh posting means approved budget and an open pain; watch a prospect list's boards while roles are live.
- **Hiring intent data API for sales teams** — pipe rows into your CRM so "company X opened 12 roles" becomes a trigger, not a manual check.
- **Competitor hiring intelligence** — see which teams your rivals staff, where and at what seniority, from their own ATS.
- **Remote jobs data feed** — `isRemote` and `isHybrid` come from the ATS itself, so remote filtering is a `WHERE` clause, not a guess from a location string.
- **A SmartRecruiters alternative to licensed job-data vendors** — Lightcast, Textkernel and JobsPipe sell normalised feeds; public ATS APIs give you the raw material.

***

### How It Works: The Public SmartRecruiters Posting API, No Browser Required

```
List (paged)  GET https://api.smartrecruiters.com/v1/companies/{identifier}/postings?limit=100&offset={n}&q={query}
Detail        GET https://api.smartrecruiters.com/v1/companies/{identifier}/postings/{postingId}
```

The list endpoint discovers postings (`totalFound` says how many exist); the detail endpoint returns the complete job ad (`jobAd.sections`, `applyUrl`, `referralUrl`, `releasedDate`, every employer `customField`). Both are read with `CheerioCrawler` as `additionalMimeTypes: ['application/json']` parsing `context.body` — JSON in, JSON out, nothing rendered. Rows are normalised, salary is parsed, writes are batched (50 rows or 1.5 s). Why this beats the career-page HTML: [below](#smartrecruiters-api-vs-career-page-html-why-this-actor-never-renders-a-browser).

***

### Input Parameters

| Field | Type | Required | Default | Notes |
| --- | --- | --- | --- | --- |
| `companies` | `array` of `string` | **Yes** | — (prefilled `BoschGroup`, `Sportradar`) | Identifiers — the path segment of `jobs.smartrecruiters.com/{identifier}`. Full board URLs are accepted and normalised. Many boards per run. |
| `searchQuery` | `string` | No | empty | Full-text filter passed to the API's `q` (e.g. `engineer`). Empty = every open posting. |
| `maxItems` | `integer` | No | `200` | Max postings **across all companies combined**. Range 1–5000. |
| `includeDescriptions` | `boolean` | No | `true` | `true` fetches each posting's detail endpoint for full description text (~1 request per posting); `false` uses only the list endpoint, much faster (~1 request per 100 postings), no description text. |
| `maxConcurrency` | `integer` | No | `32` | In-flight API requests. Range 1–64, capped 64; faster up to the API's ~200 req/s, then levels off. |
| `proxy` | `object` | No | `{ "useApifyProxy": false }` | Standard Apify proxy picker, nothing hard-coded — none needed, the API answers with no anti-bot challenge. |

**Finding a company identifier:** it is the path segment of the board URL — `jobs.smartrecruiters.com/**BoschGroup**` → `BoschGroup` — and is **case-sensitive**. An unknown identifier is not an error: the API returns `totalFound: 0`, the board is skipped (logged as `0 open postings`), the run continues.

***

### Output Fields

One dataset row per posting — 42 fields, exported as JSON, CSV, Excel, XML, JSONL or HTML.

- **Identity & organisation** — `companyIdentifier`, `companyName`, `postingId`, `uuid`, `refNumber` (requisition reference, e.g. `REF297662Y`), `title`, `department`, `team`, `function`, `industry`, `experienceLevel` (seniority).
- **Location** — `city`, `region`, `country` (lowercase ISO as published), `postalCode`, `fullLocation`, `isRemote`, `isHybrid`, `additionalLocations[]` (multi-site roles: `{city, country, region, fullLocation, remote}`).
- **Employment & timing** — `typeOfEmployment`, `employmentType` (Full-time, Part-time, Contract…), `contractType` (Permanent / Fixed-term / Contractor), `postedAt` (ISO-8601), `active`, `language`, `isEvergreen`.
- **URLs** — `applyUrl` (direct apply), `postingUrl` (public ad), `referralUrl`.
- **Salary** — `salaryText` (raw snippet, `null` when pay is not published), `salaryMin`, `salaryMax`, `salaryCurrency`, `salaryPeriod`.
- **Description text** — `descriptionText` / `descriptionHtml`, `qualificationsText` / `qualificationsHtml`, `additionalInformationText`, `companyDescriptionText`, from `jobAd.sections`.
- **Everything else** — `customFields` (all employer-defined fields: legal entity, brands, worker type, tariff area…), `scrapedAt` (ISO-8601 row timestamp).

Text fields require `includeDescriptions: true` (the default); everything else comes from the list endpoint either way.

***

### Example Input: Three Boards, Filtered

```json
{
    "companies": ["BoschGroup", "Sportradar", "SiemensHealthineers"],
    "searchQuery": "engineer",
    "maxItems": 300,
    "includeDescriptions": true
}
```

Full board URLs work too: `{ "companies": ["https://jobs.smartrecruiters.com/BoschGroup"] }`

### Example Output: One Normalized Row Per Posting

```json
{
    "companyIdentifier": "BoschGroup",
    "companyName": "Bosch",
    "postingId": "10742915",
    "refNumber": "REF297662Y",
    "title": "Software Engineer, Embedded Systems",
    "department": "Mobility Solutions",
    "experienceLevel": "Mid-Senior Level",
    "city": "Stuttgart",
    "country": "de",
    "fullLocation": "Stuttgart, Germany",
    "isRemote": false,
    "isHybrid": true,
    "employmentType": "Full-time",
    "contractType": "Permanent",
    "postedAt": "2026-09-14T08:12:03.000Z",
    "applyUrl": "https://jobs.smartrecruiters.com/BoschGroup/10742915/application",
    "postingUrl": "https://jobs.smartrecruiters.com/BoschGroup/10742915",
    "salaryText": "70,000 - 85,000 EUR per annum",
    "salaryMin": 70000,
    "salaryMax": 85000,
    "salaryCurrency": "EUR",
    "descriptionText": "You will develop embedded software for our vehicle domain controllers…",
    "customFields": { "Global Salary Level": "EG12", "Worker Type": "Employee" },
    "scrapedAt": "2026-10-05T09:41:22.000Z"
}
```

All 42 fields are documented above; this is the shape of one row.

***

### SmartRecruiters API Vs Career-Page HTML: Why This Actor Never Renders A Browser

**Surface used: SmartRecruiters' public Posting REST API (JSON).** The recon followed the cheapest-surface-first rule:

1. **Embedded JSON in the HTML** (`__NEXT_DATA__`, `application/ld+json`): `jobs.smartrecruiters.com/BoschGroup` returns 200 but is a **pure SPA shell** — the only script-level blob in the 78 KB of HTML is Google Tag Manager's `dataLayer` (9 occurrences), with **zero job data**, no `ld+json`, no `__NEXT_DATA__`.
2. **Third-party search backend** (Algolia / Elasticsearch / Meilisearch / TypeSense): none — SmartRecruiters has no such index; its boards run on its own backend.
3. **Internal / mobile JSON API**: ✅ **the vendor's own officially documented developer API** — HTTP **200**, clean JSON, **no API key, no cookies, no Cloudflare challenge, no session state.**

It is the *same* source the career portal renders from, so nothing is lost: **no browser** means `CheerioCrawler` on JSON rather than `PlaywrightCrawler` — roughly **an order of magnitude cheaper** on Apify compute units; the detail endpoint still returns the complete structured ad (`jobAd.sections`: `jobDescription`, `qualifications`, `additionalInformation`, `companyDescription`, plus `applyUrl`, `referralUrl`, `releasedDate` and the whole `customField` array); and `totalFound` + `offset` give deterministic pagination for free.

`server: cloudflare` and `cf-ray` **are** present on the board HTML, but the API host answers the same IP with a plain `200` — no anti-bot bypass, residential proxy or paid anti-bot vendor required.

**Paging:** the list endpoint silently **clamps `limit` to 100** (ask for `limit=500`, get 100), so the Actor pages at 100 with `offset` until `totalFound` is exhausted — and because `totalFound` arrives with page 1, **all remaining pages are scheduled at once** rather than chained. `maxRequestsPerCrawl` is derived from the input, so a run can never run away.

**Re-verifying the contract** — documented at **developer.smartrecruiters.com**:

```bash
curl -s "https://api.smartrecruiters.com/v1/companies/BoschGroup/postings?limit=2" | jq '.totalFound, .content[0].name'
curl -s "https://api.smartrecruiters.com/v1/companies/BoschGroup/postings/<postingId>" | jq '.jobAd.sections | keys'
```

All endpoints live in **`src/routes.ts`** (`listUrl` / `detailUrl` / `makeListRequest` / `makeDetailRequest`), so an API change is a one-file fix; mapping is `buildRecord()` in **`src/main.ts`**, salary parsing in **`src/salary.ts`**.

***

### Salary Data Scraping: Extract Pay From SmartRecruiters Job Postings

SmartRecruiters has **no standard salary field**, so the Actor looks where pay actually appears:

1. **`customField` entries** whose label matches `/salary|compensation|remuneration|pay|wage|stipend/i` (e.g. Bosch's `Global Salary Level`, or a `Salary Range` band).
2. **Free text** in `additionalInformation` / `jobDescription` / `qualifications` with a currency marker, a `k` magnitude suffix or an explicit range — so `"5 years of experience"` is never mistaken for pay.

Currency detection covers symbols (`$ £ € ¥ ₹ ₽`) and ISO codes; parsing handles `US` (`120,000`) and `EU` (`50.000`) separators plus `70-85k` shorthand, landing in `salaryText`, `salaryMin`, `salaryMax`, `salaryCurrency`, `salaryPeriod`. **When the employer does not publish pay, `salaryText` is `null`** — the honest answer, not a parsing failure: filter on it to isolate pay-transparent employers, or keep the nulls as "disclosure unknown".

### Scrape SmartRecruiters Jobs 6x Faster: Parallel Runtime, Cost And Free Tier

- **`CheerioCrawler`, 256 MB** default memory, no browser; `maxRequestRetries: 3`, 60 s handler timeout, `maxRequestsPerCrawl` computed from the input.
- \~**1 request per posting** with `includeDescriptions: true`; ~**1 request per 100 postings** with `false`.
- `maxConcurrency: 32` (configurable, capped 64). **No paid anti-bot API, no residential proxy** — the proxy input is optional and off by default.

Throughput is bounded by API latency, and the Actor hides it: **all list pages are enqueued up front** (page 1's `totalFound` is known immediately, so every remaining `offset 100, 200, …` page is pushed in one batch instead of chained), **detail fetches run in a parallel pump** capped at `maxConcurrency` with backoff on 429 / 5xx / 408 (the request queue is used only for list pages — a storage round trip per request is expensive), and **dataset writes are batched** to `Dataset.pushData` in groups of 50 rows or every 1.5 s, with the final flush awaited before exit so no row is lost to shutdown.

Measured on the same machine, same boards (BoschGroup + Sportradar, 1000 postings, descriptions on):

| Version | Wall time | Rows |
| --- | --- | --- |
| Serial (concurrency 4, one page at a time, one row push per posting) | **48.2 s** | 998 |
| Parallel (concurrency 32, all pages up front, detail pump, batched writes) | **7.1–8.2 s** | 1000 |

Roughly **6× faster**, every requested row delivered; beyond ~32 requests speed levels off because the API caps out around 200 req/s.

**Price:** pay-per-event — **`result` $0.0008 on the free plan, $0.0007 on GOLD+, per row**, plus **`Actor Start` $0.00005** per run. The gate checks `APIFY_USER_IS_PAYING`: **🆓 free users** get a capped preview of the **first 10 postings** (all companies combined); **⭐️ paid users** get the full `maxItems` (up to 5000). The cap is enforced by never enqueueing further detail requests — a free run is hard-bounded at 10 postings rather than killed mid-flight.

***

### SmartRecruiters API Python: Query The Posting API Or Drive This Actor

Raw API — no key, no session, but you rebuild pagination and normalisation yourself:

```python
import requests
BASE, identifier = "https://api.smartrecruiters.com/v1/companies", "BoschGroup"  # case-sensitive
jobs, offset = [], 0
while True:
    page = requests.get(f"{BASE}/{identifier}/postings", timeout=30,
                        params={"limit": 100, "offset": offset}).json()  # limit clamps to 100
    jobs.extend(page["content"]); offset += 100
    if offset >= page["totalFound"] or not page["content"]:
        break
detail = requests.get(f"{BASE}/{identifier}/postings/{page['content'][0]['id']}", timeout=30).json()
print(len(jobs), detail["jobAd"]["sections"].keys())
```

Or run this Actor and get the 42 normalised fields, multi-company handling, the salary parser and the free-tier rules already applied:

```python
import requests
items = requests.post(
    "https://api.apify.com/v2/acts/ahmed_jasarevic~jobs-smartrecruiters-scraper/run-sync-get-dataset-items",
    params={"token": "YOUR_TOKEN", "waitsecs": 60},
    json={"companies": ["BoschGroup", "Sportradar"], "maxItems": 500}, timeout=90).json()
print(len(items), items[0]["title"], items[0]["salaryText"])
```

***

### SmartRecruiters Job Postings Data For Talent Acquisition Analytics And Labour Market Intelligence

- **`postedAt` (ISO-8601)** — a real publish date from the ATS, not a re-scrape date; diff two runs on it to see what is genuinely new.
- **`department` / `function` / `experienceLevel` / `team`** — headcount mix by organisational unit and seniority, per company or across a watchlist.
- **`isRemote` / `isHybrid` / `fullLocation` / `additionalLocations[]`** — location strategy from the employer's own flags; **`customFields`** keeps employer-specific structure (legal entity, brands, worker type) intact; **`scrapedAt`** orders interleaved snapshots.

`companies` accepts many boards per run, so the same input scales from one client's board to a 50-company watchlist — Bosch Group alone publishes **4,825 live postings** on a single identifier. Schedule it (daily for monitoring, weekly for benchmarking) and let the dataset accumulate; there is no built-in delta mode, so diff downstream on `postedAt`.

***

### Build A Job Postings Database With A Job Market Data API - Labor Market Intelligence

- **Public ATS APIs** — SmartRecruiters, Greenhouse, Lever, Ashby, Workable, Personio and Workday all publish keyless job endpoints: fresh, direct-from-source postings, at compute cost plus $0.0008 per row here.
- **Multi-ATS Store Actors** — several schemas normalised into one dataset: `softyways/greenhouse-lever-ashby-workday-job-scraper` $1.50/1k, `glidepath/ats-jobs-scraper` (10 ATS) $1.75/1k.
- **Licensed vendors** — Lightcast, Textkernel and JobsPipe sell cross-platform coverage, dedup and history at enterprise licensing prices; JobsPipe's claim that its public feed is plan-tier dependent is a self-published, unverified vendor claim.

The public-API route is the cheap one — the same material paid aggregators ingest, minus their dedup layer. What you give up is cross-ATS coverage: you must know which ATS each target runs, which is why the Related Actors list pairs this scraper with multi-ATS and Indeed/LinkedIn sources instead of pretending one feed is the whole market.

### Greenhouse, Lever, Ashby And Workday Job API Comparison

**Which ATS platforms expose public job APIs?** SmartRecruiters, Greenhouse, Lever, Ashby, Workable, Personio and Workday — all keyless. **SmartRecruiters vs Greenhouse vs Lever:** none needs a key, but the shapes differ — SmartRecruiters paginates at 100 with `totalFound` and returns full `jobAd.sections` on the detail call; Greenhouse needs a second call for the description; Lever does not. SmartRecruiters' quirk is the silent `limit` clamp to 100, which this Actor handles for you.

**What other SmartRecruiters Store Actors charge** (verified Store listings): `memo23/smartrecruiters-scraper` $0.0009/result + $0.025 start (24 users, 5.0★); `jobo.world/smartrecruiters-jobs-scraper-api` $0.0031/job (50 users / 10 monthly); `schnellscrapers/smartrecruiters-jobs-scraper` $1 per 1,000; `blooming_gator/smartrecruiters-jobs-api` $5 per 1,000 with only 53.6% of runs succeeded; `parseforge` $0.04116/result — against this Actor's **$0.0008/row (free plan), $0.0007/row (GOLD+) + $0.00005 start**.

Need **more than SmartRecruiters in one run**? `softyways/greenhouse-lever-ashby-workday-job-scraper` covers 7 ATS at $1.50/1k; `glidepath/ats-jobs-scraper` covers 10 ATS at $1.75/1k.

***

### Scrape Indeed And LinkedIn Jobs For A Multi-Source Job Data Pipeline

SmartRecruiters boards cover the employer's *own* careers site; aggregators combine them with the big boards, where the authoritative Store Actors are `valig/indeed-jobs-scraper` (32,875 users, 4.73★) and `borderline/indeed-scraper` (26,346 users, 4.63★, $0.005/job) on Indeed; `curious_coder/linkedin-jobs-scraper` (168,213 users, 4.59★, $0.001/result) and `bebity/linkedin-jobs-scraper` (42,299 users, 4.31★) on LinkedIn. ATS feeds are fresher and employer-authoritative but company-by-company; the aggregators give breadth across companies you never listed — use this Actor for depth on named targets, them for discovery.

***

### Integrations & Automation: Apify API, Webhooks, Zapier, Make & Schedules

- **Apify REST API** — `POST https://api.apify.com/v2/acts/ahmed_jasarevic~jobs-smartrecruiters-scraper/runs?token=YOUR_TOKEN` with the input JSON as the body, then read the dataset; the Python and JavaScript SDKs (`apify-client-python` / `apify-client-js`) make the same call.
- **Webhooks** — fire on `actor.run.SUCCEEDED` to push each finished dataset into your database, CRM or Slack.
- **Zapier and Make** — every Apify Actor is callable as a Zap or scenario; map dataset items to rows or sheets.
- **Schedules** — **daily** for hiring-intent monitoring and competitor hiring intelligence; **weekly** for talent acquisition analytics and compensation benchmarking; hourly only if you power live job alerts.
- **Console** — paste the [Example Input](#example-input-three-boards-filtered) JSON and hit Run; rows appear under Storage → Dataset, exportable as JSON, CSV, Excel, XML, JSONL or HTML.

***

### Related Actors: SmartRecruiters, ATS And Job Data Scrapers

- [jobs-lever-scraper](https://apify.com/ahmed_jasarevic/jobs-lever-scraper) — the sibling ATS feed: every Lever board in one HTTP request per company, no per-job fee.
- [smartrecruiters-scraper](https://apify.com/memo23/smartrecruiters-scraper) — the closest SmartRecruiters-only rival (5.0★); claims Bosch 4,600+, OpenAI 700+ and `globalId` dedupe.
- [smartrecruiters-jobs-scraper-api](https://apify.com/jobo.world/smartrecruiters-jobs-scraper-api) — SmartRecruiters-only at $0.0031/job, marketed as "zero ghost jobs, verified daily".
- [greenhouse-lever-ashby-workday-job-scraper](https://apify.com/softyways/greenhouse-lever-ashby-workday-job-scraper) — 7 ATS platforms including SmartRecruiters in one schema, at $1.50/1k.
- [indeed-job-scraper](https://apify.com/ahmed_jasarevic/indeed-job-scraper) — the aggregator side of the same job-market workflow: salary ranges, company data and skills across many countries.

### FAQ

#### Is there an official SmartRecruiters API for job postings?

Yes. `api.smartrecruiters.com/v1/companies/{identifier}/postings` is publicly documented at `developers.smartrecruiters.com` and needs no API key, no OAuth token, no session and no cookies. The per-posting detail endpoint adds the full `jobAd.sections` text. This Actor is a thin paginated and normalised wrapper around those two endpoints.

#### How do I get all open jobs from jobs.smartrecruiters.com for many companies?

Put every board identifier into `companies` (`BoschGroup`, `Sportradar`, ... — the part after `jobs.smartrecruiters.com/`, case-sensitive; full board URLs are also accepted). The Actor pages the list endpoint at 100 postings per call, schedules every remaining page up front from `totalFound`, and fetches detail for each posting when `includeDescriptions` is on. One run handles a single client board or a 50-company watchlist.

#### What is the best way to scrape job postings without a browser?

Use the ATS's own public API rather than the career-page HTML. The SmartRecruiters career page is a JavaScript shell that ships only a GTM `dataLayer` — no job data, no `ld+json`, no `__NEXT_DATA__` — so HTML scraping needs a full browser renderer and a proxy. The JSON API answers plainly with HTTP 200, which is what this Actor runs on (CheerioCrawler parsing `context.body`), roughly an order of magnitude cheaper than PlaywrightCrawler and with no anti-bot bypass involved.

#### SmartRecruiters vs Greenhouse vs Lever: which has the best public jobs API?

All three expose keyless endpoints. SmartRecruiters paginates at 100 with a `totalFound` counter (and silently clamps `limit` to 100 — this Actor handles that) and returns complete `jobAd.sections` on the detail call. Greenhouse needs a second call to get description text. Lever does not paginate the same way. If you are comparing Store Actors instead of raw endpoints: `softyways/greenhouse-lever-ashby-workday-job-scraper` covers 7 ATS platforms in one schema at $1.50/1k.

#### Can I get salary data from job postings programmatically?

Partly. SmartRecruiters has no standard salary field, so pay appears either in `customFields` whose label matches `salary`, `compensation`, `remuneration`, `pay`, `wage` or `stipend`, or in free text carrying a currency marker. This Actor parses both, handling currency symbols, ISO codes, US `120,000` and EU `50.000` separators and the `70-85k` shorthand, and returns `salaryText` / `salaryMin` / `salaryMax` / `salaryCurrency` / `salaryPeriod`. When the employer does not publish pay, `salaryText` is `null` — an honest answer rather than a failure.

#### How can I build a job postings database with a job market data API?

Three routes: public ATS APIs (SmartRecruiters, Greenhouse, Lever, Ashby, Workable, Personio, Workday — all keyless), multi-ATS Store Actors that normalise several schemas into one dataset, or licensed vendors such as Lightcast, Textkernel and JobsPipe. The public-API route is the cheap one — the same material aggregators ingest — at compute cost plus $0.0008 per row here; what you give up is cross-ATS coverage, because you must know which ATS each target runs.

#### Which ATS platforms expose public job APIs?

SmartRecruiters, Greenhouse, Lever, Ashby, Workable, Personio and Workday all publish keyless job endpoints. Store Actors exist for the multi-ATS case (`softyways/greenhouse-lever-ashby-workday-job-scraper` for 7, `glidepath/ats-jobs-scraper` for 10) and for the aggregators (`valig/indeed-jobs-scraper`, `curious_coder/linkedin-jobs-scraper`).

#### Is it legal to scrape public job listings?

Job postings are published by the employers themselves for the public to read, and this Actor needs no login, no CAPTCHA solving and no personal contact data. SmartRecruiters' own Customer Terms restrict *customers* from running automated scripts against the SmartRecruiters Applications; the public Posting API is separately documented for programmatic use. You remain responsible for your own compliance with the source's Terms of Service, robots policy and applicable data-protection law.

### SEO Keywords

smartrecruiters api, smartrecruiters scraper, scrape smartrecruiters jobs, smartrecruiters api python, jobs.smartrecruiters.com, smartrecruiters job postings data, smartrecruiters jobs scraper apify, job postings database, hiring signals data, hiring intent data api, job market data API, remote jobs data feed, salary data scraping, recruiting intelligence, talent acquisition analytics, labour market intelligence, labor market intelligence, ats data extraction, job aggregator data source, best job scraping api, greenhouse lever ashby workday job api, scrape indeed linkedin jobs

### For AI Agents & LLM Apps

**Purpose:** returns every open job posting from one or more `jobs.smartrecruiters.com` boards as 42 normalised dataset rows, read from the public keyless SmartRecruiters Posting API.

**Minimal input** — the smallest run that works:

```json
{ "companies": ["BoschGroup"] }
```

**Filtered variant** (the only other fields an agent normally touches):

```json
{ "companies": ["BoschGroup", "Sportradar"], "searchQuery": "engineer", "maxItems": 500, "includeDescriptions": false }
```

**Output fields:** `companyIdentifier`, `companyName`, `postingId`, `uuid`, `refNumber`, `title`, `department`, `team`, `function`, `industry`, `experienceLevel`, `city`, `region`, `country`, `postalCode`, `fullLocation`, `isRemote`, `isHybrid`, `additionalLocations`, `typeOfEmployment`, `employmentType`, `contractType`, `postedAt`, `active`, `language`, `isEvergreen`, `applyUrl`, `postingUrl`, `referralUrl`, `salaryText`, `salaryMin`, `salaryMax`, `salaryCurrency`, `salaryPeriod`, `descriptionText`, `descriptionHtml`, `qualificationsText`, `qualificationsHtml`, `additionalInformationText`, `companyDescriptionText`, `customFields`, `scrapedAt`.

**Behaviors an agent should know:**

- `companies` is the only required field. Identifiers are **case-sensitive**; an unknown identifier returns `totalFound: 0` and is **skipped without an error**, so a typo silently yields zero rows — pass the exact slug from the board URL.
- Full board URLs (`https://jobs.smartrecruiters.com/BoschGroup`) are accepted and normalised to the identifier automatically.
- Never paginate yourself: the list endpoint **silently clamps `limit` to 100**. The Actor reads `totalFound` from page 1 and schedules all remaining pages up front.
- `includeDescriptions` defaults to `true` and costs roughly **one extra request per posting**; set it to `false` to use only the list endpoint (~1 request per 100 postings, much faster, no description text).
- `maxItems` defaults to `200`, caps at `5000`, and counts **across all companies combined**, not per board.
- On a non-paying plan the run is hard-bounded at the **first 10 postings** across all companies; paying plans get the full `maxItems`.
- `searchQuery` maps straight to the API's `q` parameter; leave it unset for every open posting.
- `maxConcurrency` defaults to `32` (capped at `64`); the API tops out around 200 requests/s.
- No browser and no proxy are required — the default `proxy` (`useApifyProxy: false`) is correct; leave it alone.
- Salary is parsed by the Actor; `salaryText: null` means the employer does not publish pay.

**Billing:** pay-per-event — `result` at **$0.0008 per row** on the free plan ($0.0007 on GOLD+), plus `Actor Start` at **$0.00005** per run. No other billed events.

### Legal & Compliance Disclaimer

- This Actor is an independent tool. It is not affiliated with, endorsed by or sponsored by SmartRecruiters SE or jobs.smartrecruiters.com.
- It reads the publicly documented, keyless SmartRecruiters Posting API (`api.smartrecruiters.com`) — no login, no CAPTCHA solving, no session hijacking and no browser fingerprint spoofing. It does not touch applicant accounts or application data.
- The output contains publicly published job postings and employer-authored descriptions. It contains no applicant personal data and no personal contact details (no emails, no phone numbers).
- SmartRecruiters' Customer Terms restrict customers from using automated agents or scripts to strip or mine data from the SmartRecruiters Applications; the public Posting API is documented separately for programmatic access. Users are responsible for their own compliance with the source platform's Terms of Service, its robots policy and applicable data-protection law (for example GDPR where you process data about individuals in the EU).

# Actor input Schema

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

SmartRecruiters company identifiers (the part after jobs.smartrecruiters.com/), e.g. BoschGroup or Sportradar. Full board URLs such as https://jobs.smartrecruiters.com/BoschGroup are also accepted and normalised automatically. Many boards can be scraped in one run.

## `searchQuery` (type: `string`):

Optional full-text filter passed straight to the SmartRecruiters Posting API `q` parameter. Leave empty to return every open posting on the board.

## `maxItems` (type: `integer`):

Maximum number of job postings to scrape across all companies combined.

## `includeDescriptions` (type: `boolean`):

When true (default) every posting is fetched from the detail endpoint so full description / requirements text is included. Set to false to only use the cheaper list endpoint (much faster, but no description text).

## `maxConcurrency` (type: `integer`):

How many API requests the scraper keeps in flight at once. Higher is faster (up to the API limit) but applies more load to the SmartRecruiters API. 32 is a good default; the value is capped at 64.

## `proxy` (type: `object`):

Select proxies to be used by your crawler.

## Actor input object example

```json
{
  "companies": [
    "BoschGroup",
    "Sportradar"
  ],
  "maxItems": 200,
  "includeDescriptions": true,
  "maxConcurrency": 32,
  "proxy": {
    "useApifyProxy": false
  }
}
```

# 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 = {
    "companies": [
        "BoschGroup",
        "Sportradar"
    ],
    "maxItems": 200,
    "includeDescriptions": true,
    "maxConcurrency": 32,
    "proxy": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("ahmed_jasarevic/jobs-smartrecruiters-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 = {
    "companies": [
        "BoschGroup",
        "Sportradar",
    ],
    "maxItems": 200,
    "includeDescriptions": True,
    "maxConcurrency": 32,
    "proxy": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("ahmed_jasarevic/jobs-smartrecruiters-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 '{
  "companies": [
    "BoschGroup",
    "Sportradar"
  ],
  "maxItems": 200,
  "includeDescriptions": true,
  "maxConcurrency": 32,
  "proxy": {
    "useApifyProxy": false
  }
}' |
apify call ahmed_jasarevic/jobs-smartrecruiters-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,ahmed_jasarevic/jobs-smartrecruiters-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/kmcgDdQEiSWCWbMkB/builds/iJbiPMyNgdcxHZgNu/openapi.json
