Recruitee Jobs by Company: Careers Site Offers API
Pricing
from $1.05 / 1,000 job row returneds
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
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
3 days ago
Last modified
Categories
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: remotegives you the postings the employer really flagged remote, andhybridgives 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 withexcludeTitleKeywords. - Hiring signal for a lead list:
postedWithinDays: 7plusonlyNewon 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: trueadds the description and requirements as published HTML and as plain text, in the same request and at no extra call.
Input
| Field | Type | Default | Meaning |
|---|---|---|---|
companies | string[] | required | Careers-site slugs (fastned) or any URL of the site (https://fastned.recruitee.com/o/<offer>). See Finding the slug. One entry = one request. |
mode | string | jobs | jobs = one row per offer, summary = one row per group with a count. See Modes. |
groupBy | string | department | Group of the summary: department, city, country, workModel, employmentType, category, experienceLevel, tag. Used in summary mode only. |
titleKeywords | string[] | empty | Keep an offer when its title contains one of these words (case-insensitive). |
excludeTitleKeywords | string[] | empty | Drop an offer whose title contains one of these words. Applied after titleKeywords. |
departments | string[] | empty | Keep offers whose department contains one of these. Free text each company types itself — see Reference. |
tags | string[] | empty | Keep offers carrying one of these tags, matched in full (case-insensitive). |
locationContains | string[] | empty | Keep offers whose city, state or country contains one of these, matched against every location of the offer. |
countryCodes | string[] | empty | Keep offers with a location in these ISO 3166-1 alpha-2 countries, e.g. ["NL","GB"]. |
workModel | string | any | any, remote, hybrid, onsite or unspecified. See Work model. |
employmentTypes | string[] | empty | Substring match over the employment-type code, e.g. ["fulltime"]. See Codes. |
experienceLevels | string[] | empty | Substring match over the experience code, e.g. ["entry_level","student"]. |
categories | string[] | empty | Substring match over the job-family code, e.g. ["information_technology"]. |
postedWithinDays | integer | empty | Keep offers published in the last N days (1–3650), counted in whole UTC days. |
onlyNew | boolean | false | Write only offers this actor has not delivered before. See Only new offers. |
includeDescription | boolean | false | Add descriptionHtml, descriptionText, requirementsHtml, requirementsText to every row. |
sortBy | string | publishedDesc | publishedDesc, publishedAsc, titleAsc, departmentAsc or siteOrder. |
maxItems | integer | 50 | Stop after this many rows in total (1–5000). |
maxItemsPerCompany | integer | empty | Cap the rows of each careers site before the global limit (1–2000). |
fields | string[] | all | Keep only these output fields, in this order. |
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 refuses a foreign domain instead of guessing a host.
Modes
jobs— one row per published offer of every careers site, ordered bysortByacross 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:
workModel | Meaning |
|---|---|
remote | the employer flagged the offer remote |
hybrid | flagged hybrid (and not remote) |
onsite | flagged on-site only |
unspecified | the 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 logged, 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.
Only new offers
With onlyNew: true the actor remembers careers site, offer id and publish timestamp in its own key-value store (recruitee-jobs-state) and writes only offers that were not there before. The first run writes everything it finds; later runs write the openings that appeared since. Offers cut by maxItems are not remembered, so they come back next time. A posting taken offline and published again counts as new.
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,"fetchedAt": "2026-09-28T20:19:02.945Z"}
| Field | Type | Meaning |
|---|---|---|
companySlug | string | Careers site the row came from |
companyName | string | Company name as the careers site publishes it |
offerId | number | Id of the offer on the platform |
title | string | Job title |
department | string|null | Team name the employer typed |
workModel | string | remote, hybrid, onsite or unspecified |
isRemote, isHybrid, isOnSite | boolean | The three flags of the source, unmerged |
city, state, country, countryCode | string|null | Primary location of the offer |
countryCodes, cities, countries, locationNames | string[] | Every location of the offer (multi-city postings) |
locationCount | number | How many locations the offer lists |
employmentType, experienceLevel, educationLevel, category | string|null | The platform codes — see Codes |
minHoursPerWeek, maxHoursPerWeek | number|null | Weekly hours when the employer filled them |
salaryMin, salaryMax, salaryCurrency, salaryPeriod | number/string|null | Salary — published by few employers, usually null |
tags | string[] | Free labels of the employer (often empty) |
status | string | Always published: nothing else is served |
publishedAt, createdAt, updatedAt, closeAt | string|null | UTC ISO-8601 timestamps |
daysSincePublished | number|null | Whole UTC days since publishedAt |
highlight | string|null | Short teaser, rarely filled |
slug, guid | string | Identifiers in the offer URL |
url, applyUrl, careersSiteUrl | string | Offer page, application form (…/c/new), careers site |
found | boolean | false on a marker row (see below) |
note | string|null | Why a marker row exists |
fetchedAt | string | Time of the request, UTC ISO-8601 |
descriptionHtml, descriptionText, requirementsHtml, requirementsText | string|null | Only 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, fetchedAt.
Marker rows. A slug the platform does not serve (HTTP 404) and a careers site that currently publishes nothing both yield one row with found: false and a note saying which of the two it is — a watchlist of 200 companies never dies because one slug has a typo, and an empty run never looks like a silent success. Marker rows for unknown slugs are always written; the "publishes nothing" marker appears only when the run would otherwise be empty.
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), 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: the careers sites requested and found, offers per site, what the filters cut, filter corrections, filters without a match, 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 ApifyClientclient = 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 marker row). 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.
| Run | Rows | Cost |
|---|---|---|
| One careers site, whole board | 25 | $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 each | 120 | $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 postings | 3 + 1 marker | $0.001 + 4 × $0.0015 = $0.007 |
The start event is charged on every run, including a run that finds nothing. Apify platform usage (compute) is billed by Apify on top and is tiny here: the actor runs at 256 MB without a browser, and a typical run takes 20–40 seconds. maxItems is a hard stop, so a run can never cost more than 0.001 + maxItems × 0.0015.
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 marker row with the reason; a run fails only when no careers site answered at all.
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