# AI Research Labs Career Tracker (`primesieve/ai-research-labs-tracker`) Actor

Track open roles across 34 AI labs and AI companies: OpenAI, Anthropic, xAI, Cohere, Perplexity, Databricks, Stripe, Harvey, ElevenLabs, Sierra, Notion, Figma, Ramp, Vercel and more. Normalised across Greenhouse, Ashby and Lever with parsed pay ranges plus research, visa and compute signals.

- **URL**: https://apify.com/primesieve/ai-research-labs-tracker.md
- **Developed by:** [Prime Sieve](https://apify.com/primesieve) (community)
- **Categories:** Jobs, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 job rows

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## AI Research Labs Career Tracker

Track open roles across **34 AI labs and AI companies** from a single input.

Every posting is pulled from the company's own public job board, normalised into one
consistent schema, and annotated with signals that matter when you are evaluating AI
roles: research area, pay transparency, visa sponsorship and compute access.

No API keys, no login, no browser rendering. The actor talks directly to the public
ATS endpoints, so a full run over all 34 boards completes in well under a minute.

***

### What you get

One dataset row per open role. Run metadata is written separately to the
`SUMMARY` key-value store record, so the dataset stays purely job rows.

| Field | Notes |
| --- | --- |
| `companyName`, `companySlug`, `labTier` | Who is hiring |
| `title`, `department`, `team` | Role and org placement |
| `employmentType` | Full-time, Part-time, Contract, Internship |
| `locationRaw`, `locationsAll`, `city`, `country` | All locations, including multi-site postings |
| `workplaceType`, `isRemote` | Remote / Hybrid / On-site, taken from the employer's own field |
| `salaryMin`, `salaryMax`, `salaryCurrency`, `salaryInterval`, `salaryText` | Parsed from the pay range the employer published |
| `salaryDisclosed` | Whether a pay range was published at all |
| `postedAt`, `updatedAt`, `jobUrl`, `applyUrl` | Freshness and direct apply link |
| `researchAreas` | Research-area tags (see below) |
| `visaSignals` | H1B / visa-sponsorship / work-authorisation / global-remote language |
| `computeHints` | Mentions of GPU clusters, TPUs, cluster scale, compute budget |
| `descriptionText` | Full plain-text description (only when `includeDescription` is on) |

#### The `SUMMARY` key-value record

Written once per run, it carries `totalJobs`, `labsScraped`, `labsWithResults`, the list
of boards queried, the filters that were applied, and a `salaryStats` object with
per-company pay statistics split into `annual` and `hourly` percentiles.

Example, from a live run:

```json
"OpenAI": {
  "jobsTotal": 200,
  "jobsWithPostedSalary": 162,
  "salaryDisclosureRate": 0.81,
  "annual": { "min": 126000, "p25": 230000, "median": 285000, "p75": 342000, "max": 750000, "currency": "USD", "sampleSize": 322 },
  "hourly": { "min": 57, "median": 70, "max": 70, "currency": "USD", "sampleSize": 2 }
}
```

Hourly and annual postings are summarised separately, so an intern rate never drags
down an annual percentile.

***

### Boards covered

`openai` `anthropic` `xai` `cohere` `perplexity` `cerebras` `suno` `poolside` `character`
`sierra` `harvey` `elevenlabs` `notion` `langchain` `ramp` `figma` `vercel` `deepgram`
`assemblyai` `midjourney` `pika` `runway` `databricks` `snowflake` `scaleai` `supabase`
`stripe` `plaid` `brex` `mercury` `airtable` `vanta` `linear` `verkada`

Pass `labs: ["openai", "anthropic"]` to narrow the run, or leave it empty to query all 34.
Add anything else with `customBoards: ["greenhouse:somecompany"]`.

Board slugs are probed against the live endpoints and recorded in
`scripts/probe-boards*.sh`, so you can re-verify them at any time.

***

### Research-area tagging

Tags are derived from the job title, department, team and full description. Possible
values: `LLM`, `RL`, `NLP`, `Computer Vision`, `Multimodal`, `Robotics`, `Agents`,
`Alignment`, `Safety`, `Theory`, `Infrastructure`, `Applications`, `Code Generation`,
`Audio`, `Data`, `General AI`.

Tagging is confidence-weighted rather than a flat keyword sweep, because a flat sweep
over a long description tags almost everything with almost everything:

- An area named in the role's **title, department or team** is high confidence. Roles
  are rarely mislabelled there, so up to four are kept.
- An area appearing **only in the description body** is a weak signal. It must be
  reinforced by at least two distinct keywords, only the two strongest survive, and they
  are only used when the title/team named nothing at all.
- A role that matches nothing anywhere is tagged `General AI` rather than left blank.

That keeps the average at ~1.3 tags per role instead of ~4, so the tags stay usable as a
filter. A live run over all boards produced this distribution:

```
Applications 837 · Agents 529 · General AI 394 · Infrastructure 360
Safety 128 · LLM 90 · Audio 86 · Code Generation 54 · Alignment 15 · Data 15
RL 10 · NLP 7 · Robotics 5 · Multimodal 5 · Theory 1
```

Tags reflect language in the posting, not a verified research remit — a role can work on
an area without naming it. Treat them as a filter to narrow a list, not as a taxonomy.

***

### Example: research roles that publish a salary

```json
{
  "labs": ["openai", "anthropic", "xai", "cohere", "perplexity"],
  "keywords": ["research", "scientist", "alignment", "safety"],
  "minSalaryUsd": 200000,
  "postedWithinDays": 30,
  "includeDescription": true
}
```

***

### Cost

Pay-per-event: **$0.002 per job row** written to the dataset, plus a flat **$0.00005**
each time the Actor starts (the platform charges one start event per GB of memory).

You are only ever billed for real postings. Board errors and run metadata live in the
`SUMMARY` key-value record, not the dataset, so they are never charged.

A default run (all 34 boards, 200 rows/lab cap) yields around 1,900 rows, which is
roughly **$3.80**. Narrowing `labs` or `keywords` cuts that proportionally — a
five-lab run is typically under $0.50.

Set `maxTotalChargeUsd` on the Actor's pricing configuration if you want a hard
ceiling per run; the platform stops charging (and stops writing rows) once it is hit.

***

### Notes on the data

- **Pay ranges** are parsed only when the employer publishes one. Coverage varies a
  lot by company — OpenAI and Cohere disclose on most postings, Anthropic's board does
  not expose a range through the public API at all.
- **`postedAt`** is the employer's first-published date, not the last-modified date, so
  freshness sorting behaves as you would expect.
- **Signals are keyword-derived**, not guarantees. `visaSignals.likelySponsorsH1b` means
  the posting text mentions H1B — always confirm with the employer.
- Boards that return nothing are recorded in `SUMMARY.boardErrors` as
  `rowType: "unavailable"` or `"board-not-found"` rather than silently dropped. They
  are kept out of the dataset so they are never billed as if they were postings.

***

### Development

```bash
npm install
npm test          # offline unit tests for the parsers and helpers
node src/main.js  # local run using storage/key_value_stores/default/INPUT.json
```

`npm test` covers the compensation parser (multi-currency, ranges, K/M suffixes,
hourly rates, stray-number rejection), HTML-to-text conversion, location splitting and
the salary statistics.

***

Prime Sieve

# Actor input Schema

## `labs` (type: `array`):

Which labs to scrape. Leave empty to track all 34 pre-configured boards. Available keys: openai, anthropic, xai, cohere, perplexity, cerebras, suno, poolside, character, sierra, harvey, elevenlabs, notion, langchain, ramp, figma, vercel, deepgram, assemblyai, midjourney, pika, runway, databricks, snowflake, scaleai, supabase, stripe, plaid, brex, mercury, airtable, vanta, linear, verkada

## `customBoards` (type: `array`):

Add boards outside the pre-configured list. Format 'ats:board-slug'. Supported ATS values: greenhouse, lever, ashby. Example: 'greenhouse:somecompany'.

## `keywords` (type: `array`):

Case-insensitive substring match against job titles. Example: \['research', 'scientist', 'alignment'].

## `departments` (type: `array`):

Case-insensitive substring match against department, team, or office name.

## `locations` (type: `array`):

Case-insensitive substring match against any of the posting's locations.

## `remoteOnly` (type: `boolean`):

Return only postings the employer marks as remote.

## `employmentTypes` (type: `array`):

Exact match on employment type. Typical values: Full-time, Part-time, Contract, Internship, Unknown.

## `postedWithinDays` (type: `integer`):

Only postings first published within the last N days. Set 0 for no limit.

## `maxJobsPerLab` (type: `integer`):

Cap the number of postings returned per lab. Set 0 for no limit.

## `minSalaryUsd` (type: `integer`):

Drop postings whose employer-listed pay range tops out below this figure. Only applies to postings that publish a range; postings without a published range are excluded when this is set.

## `includeDescription` (type: `boolean`):

Include the full plain-text job description in each row. Description text is always read to compute the research-area, visa and compute signals; enabling this only adds it to the output (larger dataset).

## `enrichWithResearchAreas` (type: `boolean`):

Add a researchAreas array to each row, tagged from the title, department and description (LLM, RL, Computer Vision, Alignment, Safety, Multimodal, Robotics, and more).

## `enrichWithVisaSignals` (type: `boolean`):

Add a visaSignals object flagging H1B / visa-sponsorship / work-authorisation / global-remote language found in the posting.

## `computeAccessHints` (type: `boolean`):

Add a computeHints object flagging mentions of GPU clusters, TPUs, cluster scale and compute budgets in the posting.

## Actor input object example

```json
{
  "labs": [],
  "customBoards": [],
  "keywords": [],
  "departments": [],
  "locations": [],
  "remoteOnly": false,
  "employmentTypes": [],
  "postedWithinDays": 30,
  "maxJobsPerLab": 200,
  "minSalaryUsd": 0,
  "includeDescription": false,
  "enrichWithResearchAreas": true,
  "enrichWithVisaSignals": true,
  "computeAccessHints": true
}
```

# Actor output Schema

## `openRoles` (type: `string`):

Dataset of open roles, one row per posting, with parsed pay ranges and research, visa and compute signals.

## `runSummary` (type: `string`):

Per-company salary statistics, boards queried and any board errors for this run.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {};

// Run the Actor and wait for it to finish
const run = await client.actor("primesieve/ai-research-labs-tracker").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {}

# Run the Actor and wait for it to finish
run = client.actor("primesieve/ai-research-labs-tracker").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{}' |
apify call primesieve/ai-research-labs-tracker --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,primesieve/ai-research-labs-tracker"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/3rPaFm0NdMUyZYPRk/builds/0FdU0p1UVAinIVAGK/openapi.json
