Recruitee Jobs by Company: Careers Site Offers API avatar

Recruitee Jobs by Company: Careers Site Offers API

Pricing

from $1.05 / 1,000 job row returneds

Go to Apify Store
Recruitee Jobs by Company: Careers Site Offers API

Recruitee Jobs by Company: Careers Site Offers API

Published offers of any company careers site on Recruitee, from its keyless offers endpoint: title, department, city, country, work model, employment type, experience level, weekly hours, tags, publish date and apply URL. A summary mode counts open jobs per department, city, country or tag.

Pricing

from $1.05 / 1,000 job row returneds

Rating

0.0

(0)

Developer

Samat Makatov

Samat Makatov

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

14 days ago

Last modified

Share

Read the open jobs of a company straight from the careers site it runs on Recruitee: title, department, city, country, work model, employment type, experience level, weekly hours, tags, publish timestamp, offer page and apply link — one row per published offer. You name the careers sites (a slug such as fastned, or any URL of that site); the actor reads each board once through the site's own keyless offers endpoint. No API key, no login, no proxy, no browser. A summary mode counts the open jobs per department, city, country, work model or tag, so you can see the shape of a board before you export it.

This is a per-company reader, not a job search: it answers "what does this employer have open right now", which is what recruiters, market researchers, remote job boards and hiring-signal pipelines ask about a watchlist of companies.

Use cases

  • Watch one employer: export every open offer of a careers site with department, location and apply link, and rerun it on a schedule to see the board change.
  • Feed a remote or hybrid job board: the source carries three separate flags, so workModel: remote gives you the postings the employer really flagged remote, and hybrid gives you the middle group most exports throw away.
  • Country and market research: filter a list of careers sites to ["NL"], ["DE"], ["GB"] and count how many jobs each employer has open where you work.
  • Talent mapping on a watchlist: scan 20–200 companies for one job family (titleKeywords: ["engineer"]) and drop the permanent "Open Application" posting with excludeTitleKeywords.
  • Hiring signal for a lead list: postedWithinDays: 7 plus onlyNew on a daily schedule writes only the openings that appeared since the last run — the classic "this company started hiring" trigger.
  • Job text for an LLM screener or a search index: includeDescription: true adds the description and requirements as published HTML and as plain text, in the same request and at no extra call.

Input

FieldTypeDefaultMeaning
companiesstring[]requiredCareers-site slugs (fastned) or any URL of the site (https://fastned.recruitee.com/o/<offer>). See Finding the slug. One entry = one request. An entry that is not a slug (a custom domain, two words) becomes a found: false row; the other entries still run.
modestringjobsjobs = one row per offer, summary = one row per group with a count. See Modes.
groupBystringdepartmentGroup of the summary: department, city, country, workModel, employmentType, category, experienceLevel, tag. Used in summary mode only.
titleKeywordsstring[]emptyKeep an offer when its title contains one of these words (case-insensitive).
excludeTitleKeywordsstring[]emptyDrop an offer whose title contains one of these words. Applied after titleKeywords.
departmentsstring[]emptyKeep offers whose department contains one of these. Free text each company types itself — see Reference.
tagsstring[]emptyKeep offers carrying one of these tags, matched in full (case-insensitive).
locationContainsstring[]emptyKeep offers whose city, state or country contains one of these, matched against every location of the offer.
countryCodesstring[]emptyKeep offers with a location in these ISO 3166-1 alpha-2 countries, e.g. ["NL","GB"].
workModelstringanyany, remote, hybrid, onsite or unspecified. See Work model.
employmentTypesstring[]emptySubstring match over the employment-type code, e.g. ["fulltime"]. See Codes.
experienceLevelsstring[]emptySubstring match over the experience code, e.g. ["entry_level","student"].
categoriesstring[]emptySubstring match over the job-family code, e.g. ["information_technology"].
postedWithinDaysintegeremptyKeep offers published in the last N days (1–3650), counted in whole UTC days.
onlyNewbooleanfalseWrite only offers not delivered before (one memory per saved task). See Only new offers.
includeDescriptionbooleanfalseAdd descriptionHtml, descriptionText, requirementsHtml, requirementsText to every row.
sortBystringpublishedDescpublishedDesc, publishedAsc, titleAsc, departmentAsc or siteOrder.
maxItemsinteger50Stop after this many rows in total (1–5000).
maxItemsPerCompanyintegeremptyCap the rows of each careers site before the global limit (1–2000).
fieldsstring[]allKeep only these output fields, in this order. companySlug and found are always kept. Names are checked before the run: another letter case or a near miss (titel) is corrected, an unknown name is left out — both said in the status message; when no name is a column of the mode, the run stops with the list of valid names. Asking for descriptionText or another text field switches includeDescription on.

Filters combine with AND across fields and with OR inside one field: titleKeywords: ["engineer","scientist"] plus countryCodes: ["NL"] means "engineer or scientist, in the Netherlands".

Reference

Finding the slug

The slug is the label in front of .recruitee.com on the careers site: https://fastned.recruitee.com/ → fastned. Paste the whole URL of the site, of a single offer (…/o/<offer>) or of a localized board (…/l/en/vacatures) and the slug is read out of it.

Some employers put the careers site on their own domain (jobs.company.com), which does not contain the slug. Open any offer there and press the apply button: the application page runs on <slug>.recruitee.com, and that is the value you need. The actor never guesses a host for a foreign domain: such an entry comes back as a found: false row with errorCode: invalid-slug, and the other entries of the list still run.

Modes

  • jobs — one row per published offer of every careers site, ordered by sortBy across all of them.
  • summary — the same request, but one row per group with the number of open jobs: companySlug, companyName, groupBy, value, jobCount. An offer open in three cities counts once per city, an offer with two tags counts once per tag, and offers with the field empty are collected under (unspecified), so the groups add up to the board. Summary rows cost the same as job rows but there are far fewer of them.

Work model

The source keeps three separate flags per offer instead of one boolean, and this actor exposes them as isRemote, isHybrid, isOnSite plus a single label in workModel:

workModelMeaning
remotethe employer flagged the offer remote
hybridflagged hybrid (and not remote)
onsiteflagged on-site only
unspecifiedthe employer set no flag at all — invisible to a plain remote/on-site split

An offer carrying more than one flag (it happens) is labelled by the widest one: remote before hybrid before on-site.

Codes of the platform

employmentType, experienceLevel, category and educationLevel are snake_case codes, and the platform documents no closed list — the set differs per careers site. That is why those filters are substring matches: ["fulltime"] keeps fulltime, fulltime_permanent and fulltime_fixed_term. Values seen on live careers sites on 2026-09-28 (examples, not a complete list):

  • employmentType: fulltime, fulltime_permanent, fulltime_fixed_term, parttime_permanent, parttime_fixed_term, contract, temporary, internship
  • experienceLevel: student_school, student_college, entry_level, mid_level, experienced, manager, senior_manager, executive, senior_executive
  • category: information_technology, internet, engineering, technical, sales, marketing_pr, finance, customer_service, recruitment_hr, administrative, management, logistics, procurement, construction, manufacturing, automotive, energy, design, consulting, architectural_services, legal_services, education, healthcare, hospitality, security, government_nonprofit, other
  • educationLevel: high_school_coursework, high_school, college_coursework, associate_degree, bachelor_degree, master_degree, doctorate, certification, professional

To read the vocabulary of your own companies, run mode: summary with groupBy: employmentType, category or experienceLevel once — the group values are exactly the codes you can filter on afterwards. Departments and tags work the same way: they are free text, so groupBy: department prints the names before you filter on them.

If a filter value matches nothing on a board, a close misspelling is corrected to the spelling that board uses and said in the status message and in SUMMARY.corrections (e.g. departments: "Netwrok Development" read as "Network Development"); a value that resembles nothing stays as typed, is reported in the log and in SUMMARY.filtersWithoutMatch, and simply keeps no offer. The search is never widened behind your back. These are free-text fields; the choice fields (mode, groupBy, workModel, sortBy) take only the listed values, and Apify refuses any other value before the run starts.

Only new offers

With onlyNew: true the actor remembers careers site, offer id and publish timestamp of every offer it delivered, in a named key-value store of your account, and writes only offers that were not delivered before. The first run writes everything it finds; later runs write the openings that appeared since. A posting taken offline and published again counts as new.

  • One memory per saved task (recruitee-jobs-state-<taskId>): two schedules with different filters on the same careers site do not hide offers from each other. Runs started without a task (Console, API) share recruitee-jobs-state. A task's first onlyNew run starts from that shared memory, so nothing delivered by earlier runs comes again.
  • Only delivered rows are remembered. Offers cut by maxItems or by your maximum cost per run come back in the next run.
  • found:false rows (unknown slug, empty board, an entry that is not a slug) are delivered once; a careers site that does not answer gets its row on every run.
  • Runs started at the same moment read the same memory — schedule them one after another.

Examples

Everything one employer has open

{ "companies": ["fastned"], "mode": "jobs", "maxItems": 25 }

Remote offers across a watchlist

{ "companies": ["deephealth", "nmbrs", "greenflux"], "workModel": "remote", "maxItems": 25 }

Engineering roles in the Netherlands, no open applications

{
"companies": ["fastned", "greenflux", "nmbrs", "afsenergy"],
"titleKeywords": ["engineer", "developer"],
"excludeTitleKeywords": ["open application"],
"countryCodes": ["NL"],
"maxItems": 30
}

Hiring signal: what appeared in the last week, once per posting

{
"companies": ["deephealth", "rosebelgoldmines", "fastned", "afsenergy"],
"postedWithinDays": 7,
"onlyNew": true,
"maxItems": 100
}

How a board is organised (summary)

{ "companies": ["deephealth", "fastned", "nmbrs", "fortrade"], "mode": "summary", "groupBy": "department", "maxItems": 30 }

Posting texts for an LLM screener, trimmed to four fields

{
"companies": ["rosebelgoldmines", "afsenergy"],
"includeDescription": true,
"fields": ["companyName", "title", "applyUrl", "descriptionText"],
"maxItems": 10
}

Output

One row per published offer (jobs mode). A real row from a cloud run of the prefilled example (descriptions off, so no posting text):

{
"companySlug": "fastned",
"companyName": "Fastned",
"offerId": 2760203,
"title": "Expansion manager France / Développeur.se Immobilier - Grand Est",
"department": "Network Development",
"workModel": "hybrid",
"isRemote": false,
"isHybrid": true,
"isOnSite": false,
"city": "Lyon",
"state": "Auvergne-Rhône-Alpes",
"country": "France",
"countryCode": "FR",
"countryCodes": [
"FR"
],
"cities": [
"Lyon",
"Strasbourg"
],
"countries": [
"France (FR)"
],
"locationNames": [
"Lyon, Auvergne-Rhône-Alpes, France",
"Strasbourg, Grand-Est, France"
],
"locationCount": 2,
"employmentType": "fulltime_permanent",
"experienceLevel": "mid_level",
"educationLevel": "associate_degree",
"category": "sales",
"minHoursPerWeek": 40,
"maxHoursPerWeek": 40,
"salaryMin": null,
"salaryMax": null,
"salaryCurrency": null,
"salaryPeriod": null,
"tags": [],
"status": "published",
"publishedAt": "2026-09-25T14:04:48.000Z",
"createdAt": "2026-09-25T14:02:08.000Z",
"updatedAt": "2026-09-28T13:13:49.000Z",
"closeAt": null,
"daysSincePublished": 3,
"highlight": null,
"slug": "expansion-manager-france-developpeurse-immobilier-grand-est",
"guid": "w86r9",
"url": "https://fastned.recruitee.com/o/expansion-manager-france-developpeurse-immobilier-grand-est",
"applyUrl": "https://fastned.recruitee.com/o/expansion-manager-france-developpeurse-immobilier-grand-est/c/new",
"careersSiteUrl": "https://fastned.recruitee.com/",
"found": true,
"note": null,
"errorCode": null,
"fetchedAt": "2026-09-28T20:19:02.945Z"
}
FieldTypeMeaning
companySlugstringCareers site the row came from
companyNamestringCompany name as the careers site publishes it
offerIdnumberId of the offer on the platform
titlestringJob title
departmentstring|nullTeam name the employer typed
workModelstringremote, hybrid, onsite or unspecified
isRemote, isHybrid, isOnSitebooleanThe three flags of the source, unmerged
city, state, country, countryCodestring|nullPrimary location of the offer
countryCodes, cities, countries, locationNamesstring[]Every location of the offer (multi-city postings)
locationCountnumberHow many locations the offer lists
employmentType, experienceLevel, educationLevel, categorystring|nullThe platform codes — see Codes
minHoursPerWeek, maxHoursPerWeeknumber|nullWeekly hours when the employer filled them
salaryMin, salaryMax, salaryCurrency, salaryPeriodnumber/string|nullSalary — published by few employers, usually null
tagsstring[]Free labels of the employer (often empty)
statusstringAlways published: nothing else is served
publishedAt, createdAt, updatedAt, closeAtstring|nullUTC ISO-8601 timestamps
daysSincePublishednumber|nullWhole UTC days since publishedAt
highlightstring|nullShort teaser, rarely filled
slug, guidstringIdentifiers in the offer URL
url, applyUrl, careersSiteUrlstringOffer page, application form (…/c/new), careers site
foundbooleanfalse on a found:false row (see below)
notestring|nullWhy a found:false row exists, in plain words
errorCodestring|nullOn a found:false row: site-not-found, site-empty, site-unreachable or invalid-slug
fetchedAtstringTime of the request, UTC ISO-8601
descriptionHtml, descriptionText, requirementsHtml, requirementsTextstring|nullOnly with includeDescription: true

Always filled: companySlug, companyName, offerId, title, url, applyUrl, workModel, status, publishedAt, daysSincePublished, locationCount, found, fetchedAt. Usually filled: department, city, country, countryCode, countryCodes, locationNames, slug, guid, employmentType, createdAt, updatedAt. Often empty because the source is empty: state, experienceLevel, educationLevel, category, minHoursPerWeek, maxHoursPerWeek, closeAt, salary*, highlight, tags, requirements*.

A summary row is short: companySlug, companyName, groupBy, value, jobCount, url, found, note, errorCode, fetchedAt.

found:false rows. A careers site that gave no offers yields one row with found: false, a note in plain words and an errorCode:

errorCodeMeaning
site-not-foundthe platform answered 404 for the slug — nobody publishes under it
site-emptythe careers site exists and publishes nothing right now
site-unreachableno usable answer after retries (timeout, 5xx, a block)
invalid-slugthe entry names no careers site (a custom domain, a page of the platform, two words); it is never requested

A watchlist of 200 companies never dies because of one bad entry, and an empty run never looks like a silent success. These rows are billed like any row and keep their slot inside maxItems; the site-empty row appears only when the run would otherwise write nothing. When no careers site answered at all, the run fails and writes (bills) no row.

The application mailbox the source ships with every offer (…@…recruitee.com) is deliberately not mapped — the actor reads company postings, not contact data.

Dataset views: Job offers (overview, with found and errorCode), Conditions and requirements (type, experience, education, hours, salary, closing date, apply link), Open jobs per group (summary mode). Download as JSON, CSV, Excel, XML or HTML, or read the dataset through the API.

Each run also writes a SUMMARY record into the default key-value store (linked in the run output): the careers sites requested, found and not read (and why), offers per site, rejected entries, what the filters cut, corrections of the input and of filter values, filters without a match, rows delivered and what stopped the run (stoppedBy: spending limit, timeout), the onlyNew store, the request count and the run status line.

Use it from code / agents

curl -X POST "https://api.apify.com/v2/acts/yadroo~recruitee-jobs/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"companies":["fastned","deephealth"],"workModel":"remote","maxItems":25}'
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('yadroo/recruitee-jobs').call({ companies: ['fastned'], maxItems: 25 });
const { items } = await client.dataset(run.defaultDatasetId).listItems();
from apify_client import ApifyClient
client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("yadroo/recruitee-jobs").call(run_input={"companies": ["fastned"], "maxItems": 25})
items = client.dataset(run["defaultDatasetId"]).list_items().items

Dataset items can also be pulled directly: GET https://api.apify.com/v2/datasets/<datasetId>/items?clean=true&format=csv. For agents with a small context window, set fields (e.g. ["companyName","title","city","applyUrl"]) — the rows shrink to what the model needs, and includeDescription stays off.

MCP: add https://mcp.apify.com to Claude / Cursor / any MCP client and call the yadroo/recruitee-jobs tool with the same JSON input.

Pricing

Pay per event: $0.001 per run start + $0.0015 per dataset row (offer row, summary row or found:false row). Store discounts: Bronze −10 %, Silver −20 %, Gold and above −30 % on the row price; the start event is the same on every plan; platform usage is included. There is no per-company fee and no extra charge for descriptions, filters or the summary mode — every careers site costs one request either way.

RunRowsCost (free plan)
One careers site, whole board25$0.001 + 25 × $0.0015 = $0.0385
The prefilled example (three careers sites, default maxItems 50)50$0.001 + 50 × $0.0015 = $0.076
Watchlist of 20 companies, ~6 offers each120$0.001 + 120 × $0.0015 = $0.181
The same 20 companies as a department summary~60$0.001 + 60 × $0.0015 = $0.091
Daily watch with onlyNew, 3 new postings3$0.001 + 3 × $0.0015 = $0.0055
Daily watch with onlyNew, nothing new0$0.001

The start event is charged on every run, including a run that finds nothing. maxItems is a hard stop, so a run never costs more than 0.001 + maxItems × 0.0015. Your maximum cost per run is respected to the row: the run stops at the last row it pays for and ends with "Stopped at your spending limit: N rows delivered"; with onlyNew only those rows are remembered, so the rest comes in the next run. A run in which no careers site answered fails and writes (bills) no row. A typical run takes 1–5 seconds.

Limits & FAQ

Why does my company return nothing? Only offers with status published are served. Drafts, internal postings and closed jobs are invisible, and an employer between hiring rounds answers with an empty board — you get a found: false row saying so, not an error.

I get found: false with a 404 note. That slug is not served. Open https://<slug>.recruitee.com/ in a browser: if the page is missing too, the employer publishes under a different slug, which is often not the company name.

Can I search all companies at once? No. The endpoint is per careers site; there is no cross-employer search on this platform. You bring the slugs — that is also why the data is live on every run and never an index that lags behind.

Where do I find the slug when the careers site runs on the company's own domain? Press apply on any offer: the application page runs on <slug>.recruitee.com. See Finding the slug.

Why is the salary empty? Most employers on this platform do not publish salary. The fields exist (salaryMin, salaryMax, salaryCurrency, salaryPeriod) and are filled when the board has them, but do not plan a salary study around them.

Why are the codes not documented? The platform never published a closed list for employment type, category, experience and education, and the values differ per careers site. Use mode: summary to read what your companies actually use; the filters are substring matches so partial codes work.

How fresh is the data? Every run reads the live board, so rows are as fresh as the careers site itself — there is no cache and no index in between. fetchedAt records the moment of the request; all timestamps are UTC ISO-8601.

Rate limits and blocks. The source documents no rate limit. The actor reads at most four careers sites in parallel, one request each, and backs off on 429/5xx while honouring Retry-After. No proxy, no browser, no login. A careers site that keeps failing gets a site-unreachable row with the reason; after three careers sites in a row without any answer the platform is taken as down and the rest of the list is not tried in that run (named in the status). A run fails only when no careers site answered at all — and then it writes nothing.

Spending limit and timeout. Four careers sites are read at a time, about ten per second, so a watchlist of a few hundred companies fits well inside the default 15-minute timeout. Near the timeout the actor stops starting careers sites, saves the rows it has read and ends SUCCEEDED with "Stopped before the run timeout: N rows saved"; the sites not read are named in the status and in SUMMARY.companiesNotRead. A restarted or migrated run never writes (or bills) a row twice.

How big can a board be? One request returns the whole published board — boards of 70+ offers came back complete. maxItems and maxItemsPerCompany cut rows after the fact, so a single big employer cannot fill a watchlist dataset.

What is not included? Application mailboxes and application questions (personal-data hygiene), cover images and social-sharing fields, translations of an offer into other languages, and anything behind the apply form. Closed and draft offers do not exist for this endpoint.


Made by Yadroo. Sibling actors: greenhouse-jobs · workable-jobs · ashby-job-postings · hh-kz-vacancies · domain-intel