# Levels.fyi Salary & Compensation Scraper — Enriched Records (`corvuslab/levelsfyi-scraper`) Actor

Scrape individual tech compensation records from Levels.fyi by company and job family — base, stock, bonus, total comp, level, focus, YOE, location and demographics. Every row is pre-enriched with the full company profile, level metadata and p10–p90 percentile bands, plus incremental monitoring.

- **URL**: https://apify.com/corvuslab/levelsfyi-scraper.md
- **Developed by:** [Corvuslab](https://apify.com/corvuslab) (community)
- **Categories:** Jobs, Lead generation, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 results

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

## Levels.fyi Scraper

> **Turn Levels.fyi into clean, structured tech-compensation data — every submission pre-enriched with company, level and percentile context.**

Scrape individual tech compensation records from **Levels.fyi** by company and job family: **base salary, stock, bonus, total compensation, level, focus, years of experience, years at company, location, offer date and demographics** — one row per real salary submission. Unlike a flat export, every record ships **pre-enriched**: the full **company profile** (industry, size, ticker, HQ, founded, vesting schedule), the level's **leveling metadata** (skill index, workforce %, canonical titles), and the role's **p10–p90 percentile bands**. No code, with **JSON / CSV / Excel / API / AI-agent** output and cheap incremental monitoring that only bills for newly-added submissions.

**Why this scraper**

- ⚡ **Fast & low-cost** — a single company × role page yields dozens of individual records, so large runs stay cheap.
- 🧾 **50+ fields per record** — not raw comp numbers: every submission includes the company profile, leveling metadata and percentile bands that flat exports leave out.
- ♻️ **Cheap to monitor** — incremental mode bills only for submissions added since the last run (see below).
- 🔔 **Notifications built in** — Telegram, Slack, Discord, WhatsApp or any webhook.
- 🤖 **AI- & API-ready** — compact output, MCP-friendly, one-click integrations.

***

### ✨ Key features

- 🏢 **Company + job-family targeting** — pick any companies and from 75+ job families (Software Engineer, Product Manager, Data Scientist, and many more), or paste Levels.fyi company/role URLs directly.
- 🧾 **Individual comp submissions** — one row per offer: base, stock, bonus, total compensation, level, focus, years of experience, years at company, location, offer date and demographics.
- 🏦 **Company profile on every row** — industry, employee count, company type, stock ticker, year founded, HQ city/state, website, LinkedIn and the **vesting schedule/type** — context most flat exports omit.
- 🪜 **Leveling metadata on every row** — canonical level titles, level order, **skill index**, **% of workforce** and target bonus % for the level, so you can compare ladders across companies without a separate lookup.
- 📊 **Percentile bands on every row** — the role's **p10 / p25 / p50 / p75 / p90** for base, total comp, stock and bonus, so each submission arrives with its full market context built in.
- 📍 **Location scoping** — narrow any role to a metro or country (e.g. Bay Area, New York, India, United States).
- ♻️ **Incremental monitoring** — schedule it and get only newly-added submissions (tagged NEW / UPDATED / UNCHANGED / EXPIRED); already-seen records are suppressed so a scheduled run bills next to nothing.
- 🔔 **Notifications** — Telegram, Slack, Discord, WhatsApp or any webhook (n8n / Make / Zapier).
- 🤖 **AI-ready** — compact and drop-empty output modes keep payloads small for LLMs and MCP.

***

### ⚙️ Input & output

#### Input

Configure it in the visual editor — no code needed — or pass JSON via the API.

| Field | What it does |
|---|---|
| `companies` | Company names or slugs to scrape (e.g. `Google`, `Meta`, `goldman-sachs`). |
| `jobFamilies` | Which roles to pull, from 75+ job families (defaults to Software Engineer). |
| `locations` | Optional metro/country scope (e.g. `san-francisco-bay-area`, `india`, `united-states`). |
| `startUrls` | Paste Levels.fyi company or role URLs directly. |
| `maxResults` | Cap the number of records (0 = unlimited). |
| `compact` | Emit only the core compensation fields (ideal for AI agents). |
| `incrementalMode` | Emit only submissions added since the last run. |
| `proxyConfiguration` | Optional — runs direct by default at typical volumes. |

…and **20 inputs** in total — the table shows the essentials; the rest cover output/AI modes, incremental state, notification channels and advanced tuning, all in the visual editor.

**Example inputs**

```json
{ "companies": ["Google", "Meta", "Amazon"], "jobFamilies": ["software-engineer"], "maxResults": 300 }
```

```json
{ "companies": ["Nvidia"], "jobFamilies": ["hardware-engineer", "software-engineer"], "locations": ["san-francisco-bay-area"] }
```

```json
{ "companies": ["Stripe"], "jobFamilies": ["product-manager"], "incrementalMode": true }
```

#### Output

Most scrapers hand you flat comp numbers and stop there. This scraper **pre-enriches every record** with three layers of market context baked into a single row — no joins, no lookups, no post-processing:

- 🏦 **Company profile** — industry, employee count, type, stock ticker, year founded, HQ, website, LinkedIn, vesting schedule and vesting type.
- 🪜 **Leveling metadata** — canonical level titles, level order, **skill index**, **% of workforce** and target bonus % — compare engineering ladders across companies in a single query.
- 📊 **Percentile bands** — the role's **p10 / p25 / p50 / p75 / p90** for base, total comp, stock and bonus, so every submission arrives with its full market context.

**Example output**

```json
{
  "id": "90114291-1e67-42fc-9ab0-a430fa6971f4",
  "url": "https://www.levels.fyi/salaries/90114291-1e67-42fc-9ab0-a430fa6971f4",
  "source": "levels.fyi",
  "company": "Google",
  "jobFamily": "Software Engineer",
  "title": "Software Engineer",
  "level": "L3",
  "focus": "Web Development (Front-End)",
  "baseSalary": 150000,
  "stockGrantValue": 35000,
  "bonus": 0,
  "totalCompensation": 185000,
  "yearsOfExperience": 0,
  "yearsAtCompany": 0,
  "location": "San Jose, CA",
  "gender": "male",
  "offerDate": "2026-07-20T02:17:04.218+00:00",
  "levelTitles": ["L3", "SWE II"],
  "levelOrder": 0,
  "percentWorkforce": 20,
  "skillIndex": 10.2,
  "rolePercentiles": {
    "baseSalary": { "p10": 150000, "p25": 170000, "p50": 200000, "p75": 232000, "p90": 253000 },
    "totalCompensation": { "p10": 175000, "p25": 210000, "p50": 305200, "p75": 412000, "p90": 513000 },
    "stock": { "p10": 25000, "p25": 40000, "p50": 80000, "p75": 142000, "p90": 212500 },
    "bonus": { "p10": 0, "p25": 0, "p50": 25200, "p75": 38000, "p90": 47500 },
    "locationName": "United States"
  },
  "companyIndustry": "Tech",
  "companySize": 188000,
  "companyType": "public",
  "companyTicker": "GOOG",
  "companyYearFounded": 1998,
  "companyHqCity": "Mountain View",
  "companyHqState": "California",
  "companyWebsite": "https://www.google.com",
  "vestingType": "RSU",
  "vestingSchedule": "38/12,32/12,20/12,10/12|33/12,33/12,22/12,12/12",
  "scrapedAt": "2026-08-18T16:45:07.418085+00:00"
}
```

Every field is present in standard mode (missing values are `null`); **compact mode** returns the core compensation fields only, for lean AI/MCP payloads.

***

### 📚 What data can you extract from Levels.fyi?

**Core compensation** — base salary, stock grant value, bonus and total compensation, annualized and normalized to USD.

**Role & submission** — job family, title, level, focus, years of experience, years at company, location, offer date, and demographics (gender where submitted).

**Enriched context (unique to this scraper):**

- 🏦 **Company profile** — name, slug, website, industry, size, type, stock ticker, year founded, HQ city/state, LinkedIn, logo, **vesting schedule and vesting type**. Most scrapers require you to look this up separately; here it's on every row.
- 🪜 **Leveling metadata** — canonical level titles, level order, **skill index**, **% of workforce**, target bonus %. Compare ladders across companies without a separate data source.
- 📊 **Percentile bands** — role-scoped **p10 / p25 / p50 / p75 / p90** for base, total comp, stock and bonus. Each submission comes with its market position built in.

**Change tracking** — content hash and `changeType` (NEW / UPDATED / UNCHANGED / EXPIRED) for incremental runs.

***

### 💡 What can you do with Levels.fyi data?

- **Compensation benchmarking** — build salary bands by company, level and location from real submissions, each pre-loaded with its p10–p90 context so you skip the aggregation step.
- **Recruiting & offer strategy** — see what candidates are actually being paid at target companies and levels, with the vesting schedule and stock mix already attached, before you extend an offer.
- **Market & talent research** — track how pay, leveling and stock mix vary across companies and industries using the company profile and skill-index data on every row.
- **Comp monitoring** — schedule it with incremental mode + notifications for a live feed of newly-added offers at the companies you track.
- **Enrichment & aggregation** — feed clean, typed compensation data — already joined with company, level and percentile context — into your own app, sheet or warehouse.
- **AI agents & pipelines** — compact output plugs straight into LLM/MCP workflows with all the context an agent needs in one record.

***

### ♻️ Incremental monitoring — pay for new submissions, not repeats

Schedule the actor and turn on **incremental mode**: each run compares against the last and emits only **NEW / UPDATED / EXPIRED** records — already-seen submissions are suppressed, so a daily watch costs a fraction of a full re-scrape.

| Daily churn | of 1,000 tracked | billable records | you save |
|---|---|---|---|
| 5 % | 1,000 | 50 | **95 %** |
| 15 % | 1,000 | 150 | **85 %** |
| 30 % | 1,000 | 300 | **70 %** |

The first run seeds the baseline and bills in full; every run after that bills only the delta.

***

### 🚀 How to scrape Levels.fyi

1. Open the actor and add one or more **companies** (e.g. `Google`) and pick the **job families** you want — or paste a Levels.fyi URL.
2. (Optional) Add **locations** to scope each role, and set **Max records**.
3. (Optional) Turn on **incremental mode** and a **notification** channel, then **Schedule** it.
4. Click **Start**.
5. Download the data as **JSON, CSV or Excel**, or pull it from the **API**.

New to Apify? Create a free account — it comes with monthly credit, no credit card required.

***

### 🔌 Integrations & export

Export to **JSON, CSV, Excel** or an HTML table, or pull from the **REST API** and the **JavaScript / Python** clients. Runs on a **schedule**, connects to **Google Sheets, Slack, Make, Zapier and n8n**, and works as an **MCP tool** for AI agents — compact mode keeps token usage small.

***

### ❓ FAQ

**Do I need a proxy or login?** No — it runs out of the box; Apify Proxy is available under Advanced for high-volume runs.

**What counts as one record?** One individual compensation submission — a single reported offer — enriched with its company, level and percentile context.

**Can I get only new submissions on a schedule?** Yes — turn on incremental mode and schedule it; each run emits only what changed and can notify your channel.

**Which roles are supported?** 75+ job families, from Software Engineer, Product Manager and Data Scientist to hardware, design, finance, legal, sales and more.

**What formats can I export?** JSON, CSV, Excel, HTML table, or via the API.

**Is it good for AI agents?** Yes — enable compact mode; the output is MCP-friendly.

**How many records can I get?** As many as the site exposes per company × role — set `maxResults` (0 = unlimited).

**What makes this scraper different from a flat CSV export?** Every record ships pre-enriched with the company profile, leveling metadata and p10–p90 percentile bands — 50+ structured fields per submission, ready to use without joins or lookups.

**Is scraping this legal?** The actor collects only **publicly available** data. You are responsible for how you use it, including any personal data and GDPR-style obligations.

***

### ⚖️ Is it legal to scrape Levels.fyi?

This actor accesses only publicly available data on Levels.fyi. You are responsible for how you use the extracted data — in particular any personal information — and for complying with the site's terms and applicable law (including the GDPR where it applies). Not affiliated with, endorsed by, or sponsored by Levels.fyi.

***

**Keywords:** levels.fyi scraper · levels.fyi api · levels.fyi salary scraper · levels.fyi.com scraper · tech compensation data · tech salary scraper · salary data scraper · compensation benchmarking · total compensation data · software engineer salaries · salary percentile bands · stock and RSU data · salary monitoring · export to CSV/Excel/JSON · no-code scraper · MCP tool for AI agents

# Actor input Schema

## `companies` (type: `array`):

Company names or levels.fyi slugs, e.g. "Google", "Meta", "goldman-sachs". Names are matched automatically. Leave empty to demo with Google.

## `jobFamilies` (type: `array`):

Which roles to pull. Selecting several fetches each one. Defaults to Software Engineer.

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

Optional. levels.fyi location slugs (e.g. "united-states", "san-francisco-bay-area", "new-york-city-area", "india") to scope each role to a metro/country. Leave empty for the default (worldwide/US) scope.

## `startUrls` (type: `array`):

Optional. Paste levels.fyi company or role URLs directly, e.g. https://www.levels.fyi/companies/nvidia/salaries/hardware-engineer

## `maxResults` (type: `integer`):

Maximum number of compensation records to return across all targets. Set 0 for unlimited (bounded by how many the site exposes).

## `compact` (type: `boolean`):

Emit only the core compensation fields. Ideal for AI agents and MCP clients.

## `excludeEmptyFields` (type: `boolean`):

Remove null, empty-string and empty-array fields from each record.

## `incrementalMode` (type: `boolean`):

Track state between runs and tag every record with a changeType (NEW / UPDATED / UNCHANGED / EXPIRED). Already-seen records are suppressed so a scheduled run only bills for new submissions.

## `stateKey` (type: `string`):

Stable name for the tracked target set. Leave empty to derive one automatically from your companies + job families.

## `emitUnchanged` (type: `boolean`):

Also emit records already seen on a previous run.

## `emitExpired` (type: `boolean`):

Emit records present last run but gone now.

## `telegramToken` (type: `string`):

Bot token from @BotFather.

## `telegramChatId` (type: `string`):

Chat or channel ID, e.g. "-100123456789" or "@yourchannel".

## `slackWebhookUrl` (type: `string`):

Slack incoming-webhook URL.

## `discordWebhookUrl` (type: `string`):

Discord incoming-webhook URL.

## `webhookUrl` (type: `string`):

Any HTTPS endpoint. Receives a JSON POST with the matched records — works with n8n, Make and Zapier.

## `webhookHeaders` (type: `object`):

Extra headers for the webhook request, e.g. {"Authorization": "Bearer xyz"}.

## `notificationLimit` (type: `integer`):

How many records to include in each notification message.

## `proxyConfiguration` (type: `object`):

Optional. The scraper runs direct by default (cheapest and fastest). If runs get challenged at volume, enable Apify Proxy — Residential is the most reliable fallback.

## `maxRequestRetries` (type: `integer`):

How many times to retry a failed request before giving up on it.

## Actor input object example

```json
{
  "companies": [
    "Google",
    "Meta",
    "Amazon"
  ],
  "jobFamilies": [
    "software-engineer"
  ],
  "maxResults": 100,
  "compact": false,
  "excludeEmptyFields": false,
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "notificationLimit": 5,
  "proxyConfiguration": {
    "useApifyProxy": false
  },
  "maxRequestRetries": 3
}
```

# Actor output Schema

## `overview` (type: `string`):

No description

## `allItems` (type: `string`):

No description

# 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 = {
    "companies": [
        "Google"
    ],
    "jobFamilies": [
        "software-engineer"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("corvuslab/levelsfyi-scraper").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 = {
    "companies": ["Google"],
    "jobFamilies": ["software-engineer"],
}

# Run the Actor and wait for it to finish
run = client.actor("corvuslab/levelsfyi-scraper").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 '{
  "companies": [
    "Google"
  ],
  "jobFamilies": [
    "software-engineer"
  ]
}' |
apify call corvuslab/levelsfyi-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,corvuslab/levelsfyi-scraper"
        }
    }
}

```

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/S6lImtbaNeM2WrMue/builds/aPNWmjkY1l9wV8EPE/openapi.json
