Arcadium Data Scrapers
Home / Guides / ATS Lookup

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)

  1. Open arcadium/ats-lookup and sign in to Apify (a free account works).
  2. Fill in the input form. The fields below are the ones the example uses.
  3. Set a maximum cost per run in the run options if you want a hard cap.
  4. Start the run. Rows appear in the Output tab as they arrive.
  5. Export the dataset as JSON, CSV or Excel, or connect an integration.
Input fieldTypeDescription
companiesarrayExample: ["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"
}
FieldWhat it holds
rowTypeKind 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).
inputWhat you entered for this company, e.g. 'stripe.com'.
domainCompany rows: the company's own domain when the input was one, e.g. 'stripe.com'; null for names, ATS URLs and system:token.
atsCompany rows: the applicant tracking system: greenhouse, lever, ashby, workable, recruitee, personio, teamtailor, smartrecruiters or workday.
companyCompany rows: company name as the job system states it, null if not stated.
companyTokenCompany rows: the board name on the ATS, e.g. 'stripe'. Use it as 'system:token' in Career Site Jobs or Hiring Changes.
boardIdCompany rows: stable board id, 'ats___token' or 'workday___host/site'.
boardUrlCompany rows: public job board page, e.g. 'https://job-boards.greenhouse.io/stripe'.
detectionCompany 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).
matchConfidenceCompany 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).
sourceCompany rows: 'live' = read from the board during this run; 'database' = from our jobs directory (see dataAsOf).
openJobsCompany rows: open jobs on the board now (the board's stated total when it gives one), e.g. 512.
openJobsIsLowerBoundCompany rows: true when the board states no total and reading stopped at maxJobsPerCompany, so openJobs is at least this many.
sampledJobsCompany rows: jobs the mix and shares were computed from (at most maxJobsPerCompany).
departmentMixCompany 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).
countryMixCompany rows: [{ name (two-letter country code), jobs, share (0-1) }]; a job in several countries counts in each. 'Unspecified' = no country stated.
remoteJobsCompany rows: sampled jobs marked remote.
remoteShareCompany rows: remoteJobs / sampledJobs (0-1), null when the board has no jobs.
jobsWithPayCompany rows: sampled jobs that post a pay range in a pay field (pay written only in descriptions is not read here).
payShareCompany rows: jobsWithPay / sampledJobs (0-1), a pay-transparency signal; null when the board has no jobs.
payCompany rows: posted pay bands, one per currency and period: [{ currency, period (year|month|week|day|hour), jobs, low, high, medianMid }].
newestPostedAtCompany rows: ISO date of the newest posting, a recency signal; null when the board gives no dates.
jobsPostedLast30DaysCompany rows: sampled jobs posted in the 30 days before dataAsOf, a hiring-velocity signal; null without posting dates.
dataAsOfCompany rows: ISO timestamp the board was read.
checkedAtCompany rows: ISO timestamp of this run's lookup.
platformError rows: always 'jobs'.
codeError rows: no_match (no supported public board found; do not retry), invalid_input (fix the input), not_found, blocked or unavailable (retry later).
messageError rows: what went wrong, in plain words. Do not parse it, use code.
scrapedAtError 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.