How to use ATS Lookup
Step by step: in the Apify Store, over the API, from JavaScript or Python, in n8n, and from AI agents through MCP or LangChain.
$4.00 per 1,000 companies with a board (live Apify price on 2026-10-31).
1. In the Apify Store (no code)
- Open arcadium/ats-lookup 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 | Example: ["stripe.com", "Notion", "https://jobs.lever.co/zoox", "ashby:openai"]. One entry per company: a domain, a company name, a careers page URL, a job board URL or system:token. Systems: greenhouse, lever, ashby, workable, recruitee, personio, teamtailor, smartrecruiters, workday. Workday is found from a careers page that links to it or a board URL. Up to 10,000 per run. Required. Also read from domains, organizations, startUrls. |
Example input:
{
"companies": [
"stripe.com",
"notion.so",
"https://jobs.lever.co/zoox",
"ashby:openai"
]
}
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~ats-lookup/run-sync-get-dataset-items" \
-H "Authorization: Bearer $APIFY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"companies":["stripe.com","notion.so","https://jobs.lever.co/zoox","ashby:openai"]}'
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/ats-lookup').call({
"companies": [
"stripe.com",
"notion.so",
"https://jobs.lever.co/zoox",
"ashby:openai"
]
});
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/ats-lookup").call(run_input={
"companies": ["stripe.com", "notion.so", "https://jobs.lever.co/zoox", "ashby:openai"],
})
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/ats-lookup",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4,
"position": [
0,
0
],
"parameters": {
"method": "POST",
"url": "https://api.apify.com/v2/acts/arcadium~ats-lookup/run-sync-get-dataset-items",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "{\"companies\":[\"stripe.com\",\"notion.so\",\"https://jobs.lever.co/zoox\",\"ashby:openai\"]}",
"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/ats-lookup",
"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/ats-lookup")
rows = tool.invoke({"run_input": {
"companies": ["stripe.com", "notion.so", "https://jobs.lever.co/zoox", "ashby:openai"],
}})
# 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": "company",
"input": "notion.so",
"domain": "notion.so",
"ats": "ashby",
"company": "Notion",
"companyToken": "notion",
"boardId": "ashby___notion",
"boardUrl": "https://jobs.ashbyhq.com/notion",
"detection": "html",
"matchConfidence": "high",
"source": "live",
"openJobs": 137,
"openJobsIsLowerBound": false,
"sampledJobs": 137,
"departmentMix": [
{
"name": "Sales",
"jobs": 48,
"share": 0.35
},
{
"name": "People",
"jobs": 16,
"share": 0.117
}
],
"countryMix": [
{
"name": "US",
"jobs": 76,
"share": 0.555
},
{
"name": "IE",
"jobs": 20,
"share": 0.146
}
],
"remoteJobs": 0,
"remoteShare": 0,
"jobsWithPay": 95,
"payShare": 0.693,
"pay": [
{
"currency": "USD",
"period": "year",
"jobs": 71,
"low": 85000,
"high": 400000,
"medianMid": 210000
},
{
"currency": "EUR",
"period": "year",
"jobs": 23,
"low": 45000,
"high": 420000,
"medianMid": 150000
}
],
"newestPostedAt": "2026-10-22T01:52:52.331Z",
"jobsPostedLast30Days": 38,
"dataAsOf": "2026-10-22T04:38:57.030Z",
"checkedAt": "2026-10-22T04:38:57.030Z"
}
| Field | What it holds |
|---|---|
rowType | Kind of row. Filter on this first. 'company': a company with a public job board, its ATS and a snapshot of its open jobs (charged as company-lookup). 'error': no public board found (code no_match) or the lookup failed; see code and message (free). |
input | What you entered for this company, e.g. 'stripe.com'. |
domain | Company rows: the company's own domain when the input was one, e.g. 'stripe.com'; null for names, ATS URLs and system:token. |
ats | Company rows: the applicant tracking system: greenhouse, lever, ashby, workable, recruitee, personio, teamtailor, smartrecruiters or workday. |
company | Company rows: company name as the job system states it, null if not stated. |
companyToken | Company rows: the board name on the ATS, e.g. 'stripe'. Use it as 'system:token' in Career Site Jobs or Hiring Changes. |
boardId | Company rows: stable board id, 'ats___token' or 'workday___host/site'. |
boardUrl | Company rows: public job board page, e.g. 'https://job-boards.greenhouse.io/stripe'. |
detection | Company rows: how the board was found: 'explicit' (system:token), 'url' (board URL), 'html' (link on the careers page), 'guess' (board named like the domain or name; check company on rare names) or 'directory' (our directory). |
matchConfidence | Company rows: high (system:token, board URL, careers-page link, or a guess whose ATS company name matches), medium (guess, name not confirmed) or low (guessed board with <= 2 jobs or no posting for a year; often a different company with the same name, verify before use). |
source | Company rows: 'live' = read from the board during this run; 'database' = from our jobs directory (see dataAsOf). |
openJobs | Company rows: open jobs on the board now (the board's stated total when it gives one), e.g. 512. |
openJobsIsLowerBound | Company rows: true when the board states no total and reading stopped at maxJobsPerCompany, so openJobs is at least this many. |
sampledJobs | Company rows: jobs the mix and shares were computed from (at most maxJobsPerCompany). |
departmentMix | Company rows: [{ name, jobs, share (0-1) }] per department, biggest first, at most 30 (the tail folded into 'Other'). 'Unspecified' = no department published (always on Workday). |
countryMix | Company rows: [{ name (two-letter country code), jobs, share (0-1) }]; a job in several countries counts in each. 'Unspecified' = no country stated. |
remoteJobs | Company rows: sampled jobs marked remote. |
remoteShare | Company rows: remoteJobs / sampledJobs (0-1), null when the board has no jobs. |
jobsWithPay | Company rows: sampled jobs that post a pay range in a pay field (pay written only in descriptions is not read here). |
payShare | Company rows: jobsWithPay / sampledJobs (0-1), a pay-transparency signal; null when the board has no jobs. |
pay | Company rows: posted pay bands, one per currency and period: [{ currency, period (year|month|week|day|hour), jobs, low, high, medianMid }]. |
newestPostedAt | Company rows: ISO date of the newest posting, a recency signal; null when the board gives no dates. |
jobsPostedLast30Days | Company rows: sampled jobs posted in the 30 days before dataAsOf, a hiring-velocity signal; null without posting dates. |
dataAsOf | Company rows: ISO timestamp the board was read. |
checkedAt | Company rows: ISO timestamp of this run's lookup. |
platform | Error rows: always 'jobs'. |
code | Error rows: no_match (no supported public board found; do not retry), invalid_input (fix the input), not_found, blocked or unavailable (retry later). |
message | Error rows: what went wrong, in plain words. Do not parse it, use code. |
scrapedAt | Error rows: ISO timestamp of the failure. |
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.