# SmartRecruiters Jobs Lookup — Open Postings API (`accountable_eel/smartrecruiters-jobs-lookup`) Actor

Look up any company's live, open SmartRecruiters job postings by company identifier or careers-page URL — title, department, city, remote flag, and apply link per role. Calls SmartRecruiters' own public postings API directly, no key required.

- **URL**: https://apify.com/accountable\_eel/smartrecruiters-jobs-lookup.md
- **Developed by:** [Adrian Voss](https://apify.com/accountable_eel) (community)
- **Categories:** Jobs, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 1 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.00 / 1,000 successful lookups

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/platform/actors/running/actors-in-store#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

## SmartRecruiters Jobs Lookup

Look up any company's live, open job postings on [SmartRecruiters](https://www.smartrecruiters.com)
by company identifier or careers-page URL. This calls SmartRecruiters' own public postings
API (`api.smartrecruiters.com`) directly — no API key, no scraping of the rendered careers
page.

### Features

- **Company → full postings list.** Every currently open job posting on the fetched page for
  the company, in one call.
- **Per-job detail.** Title, department, city, country, a combined `location` string, remote
  flag, posted date, and a direct apply URL for each posting.
- **Total count reported.** `totalReported` reflects SmartRecruiters' own whole-board total
  (`totalFound`), separate from `totalJobCount` (this page, before filters) — see
  [Filtering the roles you get back](#filtering-the-roles-you-get-back).
- **Real job filters.** Narrow to the roles you actually want by title keyword, excluded
  title keyword, location, remote-only, and posted-after date — see
  [Filtering the roles you get back](#filtering-the-roles-you-get-back).
- **New-jobs-only mode.** Put the actor on a schedule and get only the postings that
  appeared since your last run — see [Watching for new jobs](#watching-for-new-jobs).
- **URL or bare ID input.** Accepts either a careers-page URL
  (`careers.smartrecruiters.com/<Id>`) or the bare company identifier itself.
- **Pay only for hits.** Companies with no open postings, or that aren't on SmartRecruiters
  at all, cost nothing — see [Pricing](#pricing).

### Input

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

One company per line — the SmartRecruiters company identifier (case-sensitive) or the full careers.smartrecruiters.com/<Id> URL. Accepted formats: SGS, careers.smartrecruiters.com/PublicStorage.

```json
{
  "items": ["smartrecruiters", "careers.smartrecruiters.com/Ubisoft"],
  "maxConcurrency": 5,
  "proxyConfiguration": { "useApifyProxy": true }
}
```

`items` is a list of SmartRecruiters company identifiers or full careers-page URLs — if you
pass a URL, the identifier is extracted from it automatically (e.g.
`careers.smartrecruiters.com/Ubisoft` → `Ubisoft`); the identifier is case-sensitive and
must match exactly what appears in the company's own careers URL. One dataset row is
returned per item; rows with `"found": false` are never charged. `maxConcurrency` (default
5\) caps parallel requests. `proxyConfiguration` routes requests through Apify Proxy.

### Output

| query | found | status | company | totalReported | jobCount | totalJobCount | newJobs | firstRun | jobs | id | title | department | city | country | location | remote | releasedDate | postedAt | applyUrl | scrapedAt |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| SGS | true | OK | SGS | 4322 | 100 | \<open postings on this page (before filters)> | <new since last run> | \<first run of this watchlist?> | \<all jobs (full list)> | 744000145034539 | 【約聘】行政人員-非破壞檢測部(高雄) | Laboratory & Testing | 高雄市 | tw | \<location (city, country)> | false | 2026-08-23T13:06:08.495Z | <first posted> | https://jobs.smartrecruiters.com/SGS/744000145034539 | 2026-08-23T23:25:14.205Z |

```json
{
  "query": "smartrecruiters",
  "found": true,
  "data": {
    "company": "smartrecruiters",
    "totalReported": 12,
    "jobCount": 12,
    "jobs": [
      {
        "id": "743000012345678",
        "title": "Senior Backend Engineer",
        "department": "Engineering",
        "city": "Warsaw",
        "country": "pl",
        "remote": false,
        "releasedDate": "2026-07-15T09:00:00Z",
        "applyUrl": "https://jobs.smartrecruiters.com/smartrecruiters/743000012345678"
      }
    ]
  },
  "scrapedAt": "2026-08-20T14:03:11.000Z"
}
```

A row is only marked `found: true` — and only then billed — once the company identifier
resolves to at least one open posting on SmartRecruiters. A company identifier that doesn't
exist on SmartRecruiters, or a company with zero currently open postings, comes back as
`found: false` and is never charged.

**Two counts, two different things.** SmartRecruiters' postings API returns `totalFound` (the
company's whole board — SGS reports 4,322) alongside a single page of postings (the API's
default limit, 100). This actor has never paginated past that first page. `totalReported`
carries the whole-board `totalFound` number; `totalJobCount` is the page-scoped count — how many
postings were actually on the fetched page, before any filters ran. `jobCount` is what's left
after filtering. On a company with more postings than fit on one page, `totalReported` will be
much bigger than `totalJobCount` — that's expected, not a bug.

### Filtering the roles you get back

All of these are optional and live in the **🎯 Job filters** section of the Input tab. Leave them
empty and you get every open role on the fetched page, exactly as before.

| Input | What it does |
| --- | --- |
| `titleKeywords` | Keep only roles whose **title** contains at least one of these. Case-insensitive, partial words count — `"engineer"` matches *Engineering Manager*. Any-match: `["engineer","designer"]` keeps both kinds. |
| `excludeTitleKeywords` | Drop any role whose title contains one of these — `["intern","contract"]`. Applied after `titleKeywords`, so a role matching both is dropped. |
| `locations` | Keep only roles whose combined location (`city, country`) contains one of these — `["Berlin","de"]`. Case-insensitive, partial matches count, any-match. |
| `remoteOnly` | Keep only roles that read as remote. Matches SmartRecruiters' own `location.remote` flag, plus *remote*, *anywhere*, *work from home*, *wfh* and *distributed* in the location, and the word *remote* in the title. |
| `postedAfter` | Keeps only roles SmartRecruiters released on or after this date — `"2026-06-01"`, or a full ISO timestamp. Inclusive of the date you give. |

Different filters combine with **AND** (title *and* location *and* remote), values inside one
filter combine with **OR**. Filtering happens before rows are built, so it applies identically
whether you're getting one row per job or one row per company. Filters run only on the fetched
page — they narrow what's already in hand, they can't reach postings 101 and beyond on a
board bigger than one page.

```json
{
  "companies": ["SGS", "Ubisoft"],
  "titleKeywords": ["engineer", "technician"],
  "excludeTitleKeywords": ["intern"],
  "locations": ["Germany", "Remote"],
  "postedAfter": "2026-08-01"
}
```

A company whose roles all get filtered out still gets one `found: true` row with
`jobCount: 0` — you asked a question and got a real answer, so it's charged like any other
company. A company with **no SmartRecruiters postings at all** is still a `found: false` miss and
still free.

### Watching for new jobs

Turn on **`onlyNewSinceLastRun`** and put the actor on a schedule to get a hiring alert instead of
a full dump:

- The **first run** returns everything that matches your filters (on the fetched page) and
  remembers which postings it showed you.
- **Every run after that** returns only postings it hasn't shown you before. `newJobs` tells you
  how many, and `firstRun` tells you whether this was the baseline run.
- A company with **nothing new still gets a row** — `found: true`, `jobCount: 0`, `newJobs: 0` —
  so a quiet week is visibly quiet, not an empty dataset you can't tell from a broken run. Every
  company checked is charged once either way.

`newJobs` and `firstRun` are on every row of every run so the output columns never move — they're
simply empty when you haven't turned this on.

The seen-list is kept in a key-value store named `smartrecruiters-jobs-lookup-state` in your own
Apify account, one entry per company. A few things worth knowing:

- **Your filters are part of the identity of a watchlist.** Two schedules with different
  `titleKeywords` keep separate seen-lists and won't shadow each other. Editing a filter starts a
  fresh watchlist, so the next run re-baselines and returns the full matching set once.
- **The company identifier's case matters here, unlike on Greenhouse.** `SGS` and `sgs` would
  keep separate watchlists — always paste the identifier exactly as SmartRecruiters uses it.
- **A role that gets closed and re-posted later counts as new again.** The seen-list only ever
  holds postings currently on the fetched page, so it can't grow without bound.
- Don't combine this with "Only keep rows containing" (`includeKeywords`) — that filter runs on
  the finished row and can drop the `0 new jobs` heartbeat row, which is the one row you want to
  keep seeing.

### Use cases

- Track hiring velocity at target companies by re-running the lookup on a schedule and
  diffing `jobCount` over time.
- Build a sourcing pipeline that pulls fresh open roles — with department, location, and
  apply URL — for a watchlist of employers.
- Feed a recruiting or sales-intelligence dashboard with real-time headcount-growth signals
  (rising job counts by department or country).
- Research which companies in a given industry are actively hiring, and where (by city or
  remote status).
- Verify whether a company still uses SmartRecruiters as its applicant tracking system
  before building an integration around it.

### Pricing

$4 per 1,000 companies, plus a $0.005 start fee. Misses (`found:false`) are never charged.

### Use it from Clay, n8n, Make, or an AI agent

This actor runs synchronously over plain HTTP — call it directly from a script, a workflow tool, or an AI agent, no Apify Console needed once you have an API token.

```bash
curl "https://api.apify.com/v2/acts/accountable_eel~smartrecruiters-jobs-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>" \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{"companies":["SGS"]}'
```

**n8n.** Add an HTTP Request node: Method `POST`, URL `https://api.apify.com/v2/acts/accountable_eel~smartrecruiters-jobs-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>`, Body Content Type `JSON`, JSON Body `{"companies":["SGS"]}` (swap in an expression from an earlier node for a real value).

**Clay.** Add an "HTTP API" column: Method `POST`, URL `https://api.apify.com/v2/acts/accountable_eel~smartrecruiters-jobs-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>`, Body `{"companies":["{{company}}"]}`, mapping the row's company into the `companies` array.

**MCP.** In Claude, Cursor, or any MCP client with the Apify MCP server, ask for "SmartRecruiters Jobs Lookup | Apify" — the agent will find and run this actor.

### FAQ

**What counts as "not found"?** Either the company identifier doesn't exist on
SmartRecruiters, or it exists but currently has zero open postings — both come back as
`found: false` and aren't charged.

**Is the company identifier case-sensitive?** Yes — it must match exactly what
SmartRecruiters uses in the company's own `careers.smartrecruiters.com/<Id>` URL. Passing
the full careers URL avoids guessing the exact casing, since the identifier is extracted
from it automatically.

**Does `totalReported` always match `jobCount`?** No, and they're not meant to. `totalReported`
is SmartRecruiters' own whole-board count (`totalFound` — e.g. 4,322 for a large company like
SGS); `totalJobCount` is how many postings were on the single fetched page (capped at the API's
page limit, e.g. 100) before any filters ran; `jobCount` is what's left in this row after
filtering (and, in delta mode, after removing what you've already seen). On a board bigger than
one page, `totalReported` will be much bigger than `totalJobCount` — that's expected.

**Why was I charged for a company that returned 0 jobs?**
Because its page had open postings — they just didn't match your filters, or weren't new since
your last run. `totalJobCount` shows how many were actually on the fetched page before filtering.
A company with no postings on SmartRecruiters at all comes back `found: false` and is never
charged.

**Does the "new since last run" state cost anything or leak between users?**
It lives in a named key-value store (`smartrecruiters-jobs-lookup-state`) inside your own Apify
account, so nobody else can see it and its storage cost is negligible (a list of posting IDs per
company). Delete that store to reset every watchlist.

**Does this only cover companies using SmartRecruiters?** Yes — it's specific to
SmartRecruiters' own ATS platform. A company using a different applicant tracking system
(Greenhouse, Lever, Workday, etc.) will not resolve here.

**Do closed or filled positions show up?** No — the API returns currently open postings
only; filled or withdrawn roles drop out of the response.

**Does it require authentication?** No — this hits SmartRecruiters' public postings API,
the same data source their own careers pages are built from, with no login or API key.

# Actor input Schema

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

One company per line — the SmartRecruiters company identifier (case-sensitive) or the full careers.smartrecruiters.com/<Id> URL. Accepted formats: SGS, careers.smartrecruiters.com/PublicStorage. You're only charged for the ones we actually find — a miss costs nothing.

## `testRun` (type: `boolean`):

Turn this on to test your input on a small sample before running the full list. Turn it off to process everything.

## `onlyFound` (type: `boolean`):

Only keep rows where something was actually found. Misses are always free, whether or not you show them here.

## `includeKeywords` (type: `array`):

Optional. Only keep results that mention at least one of these words (e.g. a job title, a city, a product name). Leave empty to keep everything.

## `excludeKeywords` (type: `array`):

Optional. Drop any result that mentions one of these words. Leave empty to skip nothing.

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

Optional. Stop the run once this many results have been found — useful for a quick, cheap sample. Leave blank for no limit.

## `titleKeywords` (type: `array`):

Optional. Keep only roles whose title contains at least one of these words — e.g. "engineer", "account executive", "designer". Case doesn't matter, and partial words work ("engineer" matches "Engineering Manager"). Leave empty to keep every role.

## `excludeTitleKeywords` (type: `array`):

Optional. Drop any role whose title contains one of these words — e.g. "intern", "senior", "contract". Applied after the include list above, so a role matching both is dropped.

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

Optional. Keep only roles whose location mentions one of these — e.g. "Berlin", "New York", "United Kingdom". Case doesn't matter and partial matches work. Leave empty to keep every location.

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

Keep only roles whose location or title says remote (also matches "anywhere", "work from home", "distributed"). Companies label remote work inconsistently, so treat this as a strong filter, not a guarantee.

## `postedAfter` (type: `string`):

Optional. A date like 2026-06-01 (or a full timestamp). Keeps only roles SmartRecruiters released on or after this date. Leave empty for no date limit.

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

Turn this on for a scheduled watchlist: the first run returns everything that matches and remembers it, and every run after that returns only postings it hasn't shown you before. A company with nothing new still gets a row (with 0 jobs), so a quiet schedule never looks like a broken one. Changing the filters above starts a fresh watchlist.

## `columns` (type: `array`):

Choose which pieces of information to include in each result row. All are included by default.

## `expandRows` (type: `boolean`):

When on, each job posting found gets its own row instead of being grouped under its company. You're still only charged once per company, no matter how many rows it produces.

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

Parallel requests. Keep conservative — this target has no browser fallback, so getting blocked costs more than slow-and-steady.

## `proxyConfiguration` (type: `object`):

Apify Proxy config. Residential recommended for anti-bot-sensitive targets.

## Actor input object example

```json
{
  "companies": [
    "SGS"
  ],
  "testRun": false,
  "onlyFound": false,
  "includeKeywords": [],
  "excludeKeywords": [],
  "titleKeywords": [],
  "excludeTitleKeywords": [],
  "locations": [],
  "remoteOnly": false,
  "postedAfter": "",
  "onlyNewSinceLastRun": false,
  "columns": [
    "company",
    "totalReported",
    "jobCount",
    "totalJobCount",
    "newJobs",
    "firstRun",
    "jobs",
    "id",
    "title",
    "department",
    "city",
    "country",
    "location",
    "remote",
    "releasedDate",
    "postedAt",
    "applyUrl"
  ],
  "expandRows": true,
  "maxConcurrency": 5,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# 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": [
        "SGS"
    ],
    "includeKeywords": [],
    "excludeKeywords": [],
    "titleKeywords": [],
    "excludeTitleKeywords": [],
    "locations": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("accountable_eel/smartrecruiters-jobs-lookup").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": ["SGS"],
    "includeKeywords": [],
    "excludeKeywords": [],
    "titleKeywords": [],
    "excludeTitleKeywords": [],
    "locations": [],
}

# Run the Actor and wait for it to finish
run = client.actor("accountable_eel/smartrecruiters-jobs-lookup").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": [
    "SGS"
  ],
  "includeKeywords": [],
  "excludeKeywords": [],
  "titleKeywords": [],
  "excludeTitleKeywords": [],
  "locations": []
}' |
apify call accountable_eel/smartrecruiters-jobs-lookup --silent --output-dataset

```

## MCP server setup

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

```

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/5XHFoHIeJrmAlPKmy/builds/MGY7xDV3tTGpGbtSA/openapi.json
