What the Mercor scraper collects
Scrapes mercor.com in three modes: the marketplace index (390 roles in one response), each listing's detail page, and Mercor's own salaried openings from its careers page. Rows share one schema with rate ranges and periods, eligibility, slot counters and company details, written to JSON or CSV.
Marketplace index
Input: No input needed — work.mercor.com/explore by defaultEvery role the index serves in one response: rate range and period, the site's domain category, work and residence eligibility, slot counters and posting date.
Job detail pages
Input: A work.mercor.com job URL, or nothing to enumerate all listings from the sitemap and indexOne row per listing, adding the company website and description, spot counts and the rate currency — and covering listings the index omits.
Mercor careers
Input: No input needed — www.mercor.com/careers by defaultMercor's own salaried roles from its Ashby ATS: department, team, salary tiers and the apply URL.
From zero to Mercor data in 3 steps
Install
Clone the repository and install one browser engine. Python 3.9+.
$ git clone https://github.com/2scraper/mercor-scraper.git && cd mercor-scraper $ pip install -r requirements.txt -r requirements-playwright.txt $ playwright install chromium $ python3 playwright_scraper.py
Run a scraper
Pick what you need and run it. Every flag is documented in the README.
$ python3 playwright_scraper.py --mode listings --format both --out mercor$ python3 playwright_scraper.py --mode job --pages 462 --concurrency 4$ python3 playwright_scraper.py --mode careersGet structured data
Rows land in JSON or CSV (
--format), with a.meta.jsonsidecar recording status, pages fetched and why the run stopped.
Mercor data fields
40 columns per row in a predictable schema. The most useful ones:
| Field | What it holds |
|---|---|
sku | Mercor listing id (a UUID for careers rows) |
title | Role title |
domain | The site's own category, e.g. Finance |
commitment | Time asked for: hourly, part-time, task-based, full-time |
rate_min | Lower bound of the pay rate |
rate_max | Upper bound of the pay rate |
rate_period | hourly, per-task, one-time or yearly — read before doing arithmetic |
rate_currency | Currency, stated on detail pages; null on index rows |
referral_amount | Referral bonus offered |
work_arrangement | e.g. remote |
eligible_locations | Country codes eligible to work the role |
company_name | Client company, when Mercor names it |
company_brand_visible | The site's flag: false means the company is withheld |
remaining_slots | Open slots left on the listing |
department | Department (careers rows) |
data_source | Route the row came from: explore, detail or careers |
[
{
"source": "mercor.com",
"scraped_at": "2026-09-18T07:18:26Z",
"url": "https://work.mercor.com/jobs/list_AAABoLBL8QQESPRVXb9Aybnn/private-equity-associate-con…",
"sku": "list_AAABoLBL8QQESPRVXb9Aybnn",
"title": "Private Equity Associate – Consumer & Leisure (France/Europe)",
"listing_type": "standard",
"status": "active",
"domain": "Finance",
"department": null,
"team": null,
"description": "Mercor is hiring experienced **private equity associates** to build a financial model f…",
"commitment": "hourly",
"employment_type": null,
"hours_per_week": null,
"rate_min": 100.0,
"rate_max": 120.0,
"rate_currency": null,
"rate_period": "hourly",
"compensation": null,
"offers_equity": null,
"referral_amount": 560.0,
"location": "France / Europe (remote)",
"work_arrangement": "remote",
"eligible_locations": [
"FRA",
"GBR",
"DEU",
"… 7 more"
],
"eligible_residence_locations": null,
"company_name": "Pearson",
"company_brand_visible": true,
"company_website": null,
"company_description": null,
"remaining_slots": 3,
"supplied_slots": 0,
"available_spots": null,
"active_contractors_count": null,
"recent_candidates_count": 0,
"posted_at": "2026-09-17T16:56:02Z",
"created_at": "2026-09-17T16:56:02Z",
"apply_url": null,
"data_source": "explore",
"page": 1,
"position": 1
},
{
"source": "mercor.com",
"scraped_at": "2026-09-18T07:18:26Z",
"url": "https://work.mercor.com/jobs/list_AAABoKORCF2dakrez4FCAo_G/generalist-expert",
"sku": "list_AAABoKORCF2dakrez4FCAo_G",
"title": "Generalist Expert",
"listing_type": "evergreen",
"status": "active",
"domain": "Miscellaneous",
"department": null,
"team": null,
"description": "The research teams behind the best-known AI models come to Mercor for careful human jud…",
"commitment": "part-time",
"employment_type": null,
"hours_per_week": 40,
"rate_min": 40.0,
"rate_max": 70.0,
"rate_currency": null,
"rate_period": "hourly",
"compensation": null,
"offers_equity": null,
"referral_amount": 280.0,
"location": "Remote",
"work_arrangement": "remote",
"eligible_locations": null,
"eligible_residence_locations": null,
"company_name": null,
"company_brand_visible": false,
"company_website": null,
"company_description": null,
"remaining_slots": null,
"supplied_slots": null,
"available_spots": null,
"active_contractors_count": null,
"recent_candidates_count": 0,
"posted_at": "2026-09-15T05:36:26Z",
"created_at": "2026-09-15T05:36:26Z",
"apply_url": null,
"data_source": "explore",
"page": 1,
"position": 20
}
]First 2 of 9 rows from a real run — sample_output.json. Long values shortened here.
Handles what breaks naive Mercor scrapers
Reads the full cache, not the short JSON-LD
/explore ships a JSON-LD ItemList of 326 URLs beside a cache of 390 full records. The scraper reads the cache; a JSON-LD parser would silently lose 16% of the catalogue.
Covers listings the index omits
The sitemap and the index each hold listings the other lacks (462 in the union). Job mode enumerates both, and the sidecar records both totals.
Withheld companies are not missing data
291 of 390 listings name no company. company_brand_visible is the site's own flag, so a null company_name beside false reads as withheld, not as a parsing gap.
No defaulted currency or unit
Rates come in four periods and the index states no currency. rate_period is always written, and rate_currency stays null until a detail page states it.
Scraping infrastructure included
The same tested core runs every 2scraper repository, so the hard parts work the same way on every site.
CAPTCHA handling
Detects the challenge a page really serves and, with a 2Captcha key, solves it only when it blocks the data — a capped number of solves per page.
Proxy rotation
Single proxy or a pool from a file, rotated on blocks with a fresh browser per exit. Credentials stay out of argv and logs.
Fingerprints
Optional complete device identity from the 2Captcha Fingerprint API — user agent, client hints, locale and timezone that agree with each other.
Scraping Browser API
Point --cdp-endpoint at a managed Chromium with its own exit country — no browser infrastructure to run yourself.
Honest run metadata
Every run writes a .meta.json sidecar and distinct exit codes: complete, blocked, empty and partial are never confused, and a failed run never overwrites good data.
Diff two runs
diff_runs.py compares two complete Mercor snapshots by id — new, removed and changed rows for monitoring.
What teams do with Mercor data
AI-training contract rate research
Compare hourly and per-task rates across domains such as finance, law and engineering.
Eligibility mapping
See which countries can work which roles from eligible_locations and residence eligibility.
Marketplace demand tracking
Diff daily runs to watch new roles, closed roles and slot counts change over time.
Hiring intelligence on Mercor itself
Careers mode lists Mercor's own salaried openings by department and team.
Free to run. Pay only for what the site requires.
Runs with no key, proxy or account
Every route this scraper reads was served in full to a bare datacentre address with no credentials, measured Sep 18 2026, and the daily canary runs from a GitHub runner with no secrets. Proxies buy volume and politeness for the 462-page job mode, the Scraping Browser API buys a specific exit country and no local Chromium, and fingerprints are optional. The marketplace runs an invisible reCAPTCHA that never challenged a run, so no solve is bought.
Optional 2Captcha services — captcha solving, the Scraping Browser API, residential proxies and fingerprints — work behind one key.
Good to know
- The /explore index held 390 of 462 listings on 2026-09-18; use --mode job to cover the rest.
- Careers rows are a different population and cannot be diffed against marketplace rows.
- The reCAPTCHA variant on the marketplace host is inferred, not confirmed, since it never challenged a run.
2scraper vs. hosted scraper APIs
| 2scraper | Typical hosted API | |
|---|---|---|
| Code | Open source, MIT — read and patch it | Closed |
| Cost | Free; optional services billed only when used | Subscription or per request / per result |
| Runs on | Your machine, server or CI | The vendor's infrastructure |
| Output | JSON/CSV files + run metadata | API responses |
| When the site changes | Fix it yourself or open an issue / PR | Wait for the vendor |
Mercor scraper FAQ
Do I need a proxy or API key?
No. Every route was served to a datacentre address with no credentials. The paid products buy volume and a chosen exit country, not access.
Why does the listings mode fetch only one page?
Mercor's index accepts no query parameters and returns everything in one response; --pages above 1 is ignored there. Job mode is the one that paginates, one job per page.
Why is rate_currency null on listings rows?
The index publishes no currency anywhere. Detail pages do (USD on 40 of 40 sampled), and --mode job fills it.
Which engine supports --concurrency?
Playwright only. Selenium and Puppeteer accept the flag, log that they ignore it, and fetch one page at a time.
What formats does it write?
JSON, CSV or both, plus a run sidecar with status and totals. A run that finds nothing writes nothing unless you pass --allow-empty.
Is it legal?
It reads only paths Mercor's robots.txt allows and public listings. Keep request rates civil, do not republish personal data, and follow the site's terms and the law.