How to use Career Site Jobs Scraper
Step by step: in the Apify Store, over the API, from JavaScript or Python, in n8n, and from AI agents through MCP or LangChain.
$1.80 per 1,000 jobs on the Free plan, down to $1.08 on higher plans (live Apify price on 2026-10-31).
1. In the Apify Store (no code)
- Open arcadium/career-site-jobs and sign in to Apify (a free account works).
- Fill in the input form. The fields below are the ones the example uses.
- Set a maximum cost per run in the run options if you want a hard cap.
- Start the run. Rows appear in the Output tab as they arrive.
- Export the dataset as JSON, CSV or Excel, or connect an integration.
| Input field | Type | Description |
|---|---|---|
companies | array | One entry per company: a company domain (stripe.com), a careers page URL, a job board URL (https://jobs.lever.co/zoox) or system:token, e.g. greenhouse:stripe or ashby:openai. Domains and careers pages are resolved automatically. For Workday paste the board address (https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite) or write workday:nvidia/wd5/NVIDIAExternalCareerSite. Supported: Greenhouse, Lever, Ashby, Workable, Recruitee, Personio, Teamtailor, SmartRecruiters, Workday. |
includeDescriptions | string | Plain text, the original HTML, or none. Email addresses and phone numbers inside descriptions are always removed. 'None' is the lightest option and still returns pay when the job board publishes it as a field. |
maxJobsPerCompany | integer | Upper limit of jobs delivered per company. In 'only changes' mode it caps the new jobs per run. |
Example input:
{
"companies": [
"stripe.com",
"ashby:openai",
"lever:zoox"
],
"includeDescriptions": "text",
"maxJobsPerCompany": 50
}
2. Over the Apify API
The synchronous endpoint runs the actor and returns its rows in one call (for runs up to about five minutes; use the asynchronous run endpoint for longer ones).
curl -X POST "https://api.apify.com/v2/acts/arcadium~career-site-jobs/run-sync-get-dataset-items" \
-H "Authorization: Bearer $APIFY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"companies":["stripe.com","ashby:openai","lever:zoox"],"includeDescriptions":"text","maxJobsPerCompany":50}'
3. From JavaScript
// npm install apify-client
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('arcadium/career-site-jobs').call({
"companies": [
"stripe.com",
"ashby:openai",
"lever:zoox"
],
"includeDescriptions": "text",
"maxJobsPerCompany": 50
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
4. From Python
# pip install apify-client
import os
from apify_client import ApifyClient
client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("arcadium/career-site-jobs").call(run_input={
"companies": ["stripe.com", "ashby:openai", "lever:zoox"],
"includeDescriptions": "text",
"maxJobsPerCompany": 50,
})
items = client.dataset(run["defaultDatasetId"]).list_items().items
print(items)
5. In n8n
Paste this node into a workflow (it is a plain HTTP Request node). Create a Header Auth credential with name Authorization and value Bearer <APIFY_TOKEN>; each returned row becomes one item for the next node.
{
"nodes": [
{
"name": "Run arcadium/career-site-jobs",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4,
"position": [
0,
0
],
"parameters": {
"method": "POST",
"url": "https://api.apify.com/v2/acts/arcadium~career-site-jobs/run-sync-get-dataset-items",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "{\"companies\":[\"stripe.com\",\"ashby:openai\",\"lever:zoox\"],\"includeDescriptions\":\"text\",\"maxJobsPerCompany\":50}",
"options": {
"timeout": 300000
}
}
}
],
"connections": {}
}
6. From an AI assistant (MCP)
Add Apify’s MCP server to Claude Desktop, Cursor, VS Code or any MCP client, limited to this actor:
{
"mcpServers": {
"apify": {
"url": "https://mcp.apify.com/?tools=arcadium/career-site-jobs",
"headers": {
"Authorization": "Bearer <APIFY_TOKEN>"
}
}
}
}
More on the MCP page.
7. From LangChain
# pip install langchain-apify (reads APIFY_API_TOKEN from the environment)
from langchain_apify import ApifyActorsTool
tool = ApifyActorsTool("arcadium/career-site-jobs")
rows = tool.invoke({"run_input": {
"companies": ["stripe.com", "ashby:openai", "lever:zoox"],
"includeDescriptions": "text",
"maxJobsPerCompany": 50,
}})
# or give `tool` to an agent: create_react_agent(llm, [tool])
Sample output
One row, from the example in the actor’s README. Long text is shortened and lists are cut to two items.
{
"rowType": "job",
"id": "ashby___34413f8d-26bf-4bbc-8ade-eb309a0e2245",
"ats": "ashby",
"company": "Ramp",
"companyToken": "ramp",
"title": "Security Engineer, Cloud",
"department": "Engineering",
"team": "Backend",
"locations": [
"New York, NY (HQ)",
"Remote (Canada)"
],
"countries": [
"US",
"CA"
],
"remote": false,
"employmentType": "Full-time",
"salary": {
"min": 211400,
"max": 290600,
"currency": "USD",
"period": "year",
"source": "structured"
},
"postedAt": "2026-04-07T17:12:35.753Z",
"url": "https://jobs.ashbyhq.com/ramp/34413f8d-26bf-4bbc-8ade-eb309a0e2245",
"description": "About Ramp\n..."
}
| Field | What it holds |
|---|---|
rowType | Kind of row. Filter on this first. Values: 'job': one open position (charged). 'closed': a position that was open before and is gone now (free). 'coverage': stated total vs delivered and why reading stopped (free). 'error': an input could not be read; see code and message (free). |
id | Stable job id '<ats>___<native id>'. |
ats | Job system the posting comes from, e.g. greenhouse, lever, workday. |
company | Company name. |
companyToken | The company's board name on the job system. |
title | Job title. |
department | Department, null if not given. |
team | Team, null if not given. |
locations | Locations as published, e.g. ['Berlin, Germany', 'Remote (US)']. |
countries | Two-letter country codes when the job system states or implies them. |
remote | true = remote, false = on-site or hybrid, null = unknown. |
employmentType | Full time, part time, contract and so on, as published. |
seniority | Seniority level, null if not given. |
salary | Pay range { min, max, currency, period (year|month|week|day|hour), source ('structured' = field of the job system, 'text' = read from the description) }; null when no pay is published. |
postedAt | ISO timestamp the job was published, null if unknown. |
updatedAt | ISO timestamp of the last update, null if unknown. |
url | Public job page. |
applyUrl | Link to apply, null if not given. |
description | Job description as plain text or HTML (see input), null when descriptions are off. |
language | Language of the posting, null if unknown. |
requisitionId | The company's own requisition id, null if not given. |
closedAt | 'closed' rows: ISO timestamp of the run that noticed the job is gone. |
detection | Coverage rows: how the job board was found, 'explicit', 'url', 'html' or 'guess'. |
newJobs | Coverage rows, only-changes mode: jobs that are new since the last run. |
closedJobs | Coverage rows, only-changes mode: jobs that closed since the last run. |
unchangedJobs | Coverage rows, only-changes mode: jobs that did not change (not delivered again). |
filteredOut | Coverage rows: jobs dropped by your keyword, country or age filters. |
scrapedAt | ISO timestamp of when the row was collected. |
stated | Coverage rows: total the site states for the entity, null when it does not say. |
delivered | Coverage rows: number of rows delivered for the entity in this run. |
skippedSeen | Coverage rows: items skipped because an earlier run already delivered them (only-new mode). |
duplicates | Coverage rows: repeats the site returned that were dropped. |
stopReason | Coverage rows: why reading stopped. limit (your maximum reached), end (no more pages), dateFrom (older than your date), onlyNew (reached already delivered items), budget (run charge limit), cap (site-side depth limit), error (ended by a failure), filter (ended with a filter active). |
capSource | Coverage rows: who limited the result, 'user' (your maximum or budget) or 'site' (a limit of the website). Null if nobody. |
shortfall | Coverage rows: true when fewer rows were delivered than the site states although no maximum or filter was set. |
input | Error and coverage rows: the input (URL, id or search text) the row is about. Example: 'https://www.example.com/page'. |
code | Error rows: machine-readable reason. One of invalid_input (fix the input), not_found (page does not exist), blocked (site refused, retry later), unavailable (temporary error, retry later), filter_unsupported (the filter cannot be applied on this site), no_match (name search found nothing). |
message | Error rows: short human-readable explanation. Do not parse it, use code. |
Schedule it
In Apify Console, create a Task from your input and add a Schedule (for example daily). Each run writes a new dataset; integrations can push the rows to Google Sheets, Slack, a webhook or your database.