HeadHunter RU $1๐Ÿ’ฐ URL | Filters | Enriched Job Data avatar

HeadHunter RU $1๐Ÿ’ฐ URL | Filters | Enriched Job Data

Pricing

from $1.00 / 1,000 results

Go to Apify Store
HeadHunter RU $1๐Ÿ’ฐ URL | Filters | Enriched Job Data

HeadHunter RU $1๐Ÿ’ฐ URL | Filters | Enriched Job Data

From $1/1K. Scrape hh.ru job listings with 50+ fields including salary, experience, schedule, employment, and role. Search by filters or use URLs. Detail enrichment adds full descriptions, key skills, contact info, and employer logos.

Pricing

from $1.00 / 1,000 results

Rating

0.0

(0)

Developer

AbotAPI

AbotAPI

Maintained by Community

Actor stats

0

Bookmarked

40

Total users

16

Monthly active users

10 days ago

Last modified

Share

hh.ru Jobs Scraper

Pull rich job listings from hh.ru (HeadHunter), Russia's largest job board, with 50+ fields per record. Filter by keywords, area, experience, schedule, employment, salary, and professional role; or paste search URLs directly. Detail-page enrichment adds full descriptions, key skills, contact info, and employer logos.

Why this scraper?

  • 50+ fields per vacancy, 4x richer than typical hh.ru scrapers. Includes keySkills, full HTML description, employer logos, GPS coordinates, metro stations, salary ranges (from/to + currency + gross/net), work formats, and contact info when public.
  • Three modes: pick keywords + area + filters, paste hh.ru search URLs as-is, or hand in a list of specific vacancy URLs / IDs and get the full detail card for each.
  • Concurrent detail-page enrichment with per-record streaming push: results show up in the dataset as they finish, mid-run aborts preserve partial progress.
  • Free-tier friendly: works on Apify default datacenter proxy, no residential plan needed.
  • Full filter coverage: experience, employment, schedule, salary range, only-with-salary, professional role, industry, sort order.
  • Forward-walking pagination that respects hh.ru's 40-page (2000-listing) hard cap per filter.

Data you get

Sample shape, values are illustrative placeholders, not from a live listing.

FieldExample
vacancyId"100000001"
url"https://hh.ru/vacancy/100000001"
name"Sample Job Title"
publicationDate"2026-05-05T09:00:00.000+03:00"
creationDate"2026-05-01T09:00:00.000+03:00"
lastChangeTime"2026-05-04T09:00:00.000+03:00"
validThroughTime"2026-06-04T09:00:00.000+03:00"
salaryFrom200000
salaryTo300000
salaryCurrency"RUR"
salaryGrossfalse
salaryMode"MONTH"
salaryFrequency"TWICE_PER_MONTH"
area.id1
area.name"ะœะพัะบะฒะฐ"
area.path".113.1."
address.city"ะœะพัะบะฒะฐ"
address.street"Sample Street"
address.lat55.7558
address.lng37.6173
address.metroStations[{ "name": "Sample Station", "lineColor": "#000000" }]
address.district"Sample District"
company.id1000000
company.name"Sample Company"
company.visibleName"Sample Company"
company.isAccreditedITtrue
company.isTrustedtrue
company.siteUrl"https://www.example.com/"
company.logoUrl"https://hh.ru/employer-logo-original/0000000.png"
company.logos[{ "type": "ORIGINAL", "url": "..." }, ...]
company.badges[{ "type": "hrbrand", "description": "Sample badge" }]
workExperience"between1And3"
employmentForm"FULL"
employment"FULL"
workSchedule"fullDay"
workScheduleByDays["FIVE_ON_TWO_OFF"]
workFormats["ON_SITE"]
workingHours["HOURS_8"]
nightShiftsfalse
internshipfalse
acceptHandicappedfalse
acceptLaborContracttrue
description"<p>Full HTML description goes here...</p>"
descriptionText"Full plain-text description goes here..."
keySkills["Python", "Django", "PostgreSQL"]
contactInfo.name"Contact Name"
contactInfo.email"contact@example.com"
contactInfo.phones[{ "country": "7", "city": "495", "number": "0000000", "formatted": "7 495 0000000" }]
contactInfo.contactsHiddenfalse
professionalRoleIds[96, 156]
responsesCount42
totalResponsesCount100
onlineUsersCount5
isAdvfalse
searchUrl"https://hh.ru/search/vacancy?text=python&area=1&page=0"
searchSessionId"00000000-0000-0000-0000-000000000000"
scrapedAt"2026-05-05T12:00:00.000Z"

How to use

{
"mode": "search",
"queries": ["python"],
"areas": ["1"],
"maxPages": 5,
"maxListings": 100,
"fetchDetails": true
}

Search with filters (experience + remote + salary)

{
"mode": "search",
"queries": ["data scientist"],
"areas": ["1", "2"],
"experience": "between3And6",
"schedule": ["remote"],
"salaryMin": 200000,
"onlyWithSalary": true,
"orderBy": "salary_desc",
"maxPages": 10
}

URL mode (paste any hh.ru search URL)

{
"mode": "url",
"urls": [
"https://hh.ru/search/vacancy?text=AI%20engineer&area=1&experience=between1And3&schedule=remote&page=0"
],
"maxPages": 5,
"fetchDetails": true
}
{
"mode": "url",
"urls": [
"https://hh.ru/search/vacancy?text=python&area=1&page=0",
"https://hh.ru/search/vacancy?text=java&area=2&page=0"
],
"maxPages": 3
}

Vacancy mode (scrape specific vacancies by URL or ID)

Hand in an exact list of vacancies you care about and get one full detail card per entry โ€” no search runs, so hh.ru's 2000-result cap never applies. Accepts full URLs, mobile URLs, or bare numeric IDs (mix freely; duplicates are removed).

{
"mode": "vacancy",
"vacancyInput": [
"https://hh.ru/vacancy/123456789",
"https://m.hh.ru/vacancy/123456790",
"123456791"
]
}

Every entry is fetched concurrently and always returns the full card (description, key skills, contact info, logos โ€” fetchDetails is implied). A removed or archived vacancy returns a record with error: true and code: "VACANCY_UNAVAILABLE" so you can tell exactly which IDs dropped off. maxListings still caps the total; maxPages is ignored in this mode.

A note on the 2000-result limit

hh.ru itself serves at most 2000 results per search filter (40 pages ร— 50) โ€” this is a server-side cap, not a limit of this scraper, and no page count can exceed it. When a search matches more than what's reachable, the run logs a warning telling you so. To capture everything beyond 2000, split one broad search into narrower slices that each stay under the cap, e.g.:

  • by area (["1", "2", "4", ...] instead of ["113"]),
  • by salary band (salaryMin windows: 0โ€“100k, 100โ€“200k, 200k+),
  • by professional role or industry,
  • or by recency (orderBy: "publication_time" run on a schedule).

Each slice gets its own fresh 2000-result budget, and results are de-duplicated by vacancyId within a run.

Input parameters

FieldTypeDefaultDescription
modestringsearchsearch, url, or vacancy
queriesstring[]["AI"]Keywords (one search per keyword). Russian fine.
areasstring[]["1"]Area IDs. 1=Moscow, 2=St Petersburg, 113=Russia. Full list at https://api.hh.ru/areas
experienceenumanyany, noExperience, between1And3, between3And6, moreThan6
employmentenum[][]Multi: full, part, project, volunteer, probation
scheduleenum[][]Multi: fullDay, shift, flexible, remote, flyInFlyOut
salaryMininteger(none)Minimum monthly RUB
onlyWithSalarybooleanfalseOnly listings with a published salary
orderByenumrelevancerelevance, publication_time, salary_desc, salary_asc, distance
professionalRolestring[][]Numeric role IDs from https://api.hh.ru/professional_roles
industrystring[][]Numeric industry IDs from https://api.hh.ru/industries
urlsstring[](example URL)Search URLs in URL mode. Filter fields ignored in this mode.
vacancyInputstring[](example)Vacancy URLs or bare IDs in Vacancy mode. Full card per entry; maxPages ignored.
maxPagesinteger51 to 40 (hh.ru hard cap). Search/URL modes only.
maxListingsinteger00 = unlimited
fetchDetailsbooleantrueAdds description, keySkills, contact info, full company logos
proxyobjectApify default datacenterApify Proxy datacenter works fine; Residential RU optional

Send results into your apps (MCP connectors)

Optionally pipe the scraped results into the apps you already use, via Model Context Protocol (MCP) connectors. This is an extra delivery step after the scrape โ€” the Apify dataset is never changed.

What gets written to the connector: a condensed, human-readable summary of each record โ€” not the full JSON. Each item becomes one entry with a title and its key fields flattened to plain text. The complete record always stays in the Apify dataset.

  1. Authorize a connector once under Apify โ†’ Settings โ†’ Integrations (Notion, Linear, Airtable, or Apify).
  2. Select it in the "Pipe results into your apps" input field. (If the picker is empty, you haven't authorized a connector yet.)
  3. For Notion, also set notionParentPageUrl to the page where items should be created.

The connection is mediated by Apify's MCP proxy, so this actor never sees your third-party credentials. Leave the field empty to skip.

Output example

Sample shape, values are illustrative placeholders, not from a live listing.

{
"vacancyId": "100000001",
"url": "https://hh.ru/vacancy/100000001",
"name": "Sample Senior Python Developer",
"publicationDate": "2026-05-05T09:00:00.000+03:00",
"creationDate": "2026-05-01T09:00:00.000+03:00",
"lastChangeTime": "2026-05-04T09:00:00.000+03:00",
"validThroughTime": "2026-06-04T09:00:00.000+03:00",
"isAdv": false,
"searchUrl": "https://hh.ru/search/vacancy?text=python&area=1&page=0",
"searchSessionId": "00000000-0000-0000-0000-000000000000",
"company": {
"id": 1000000,
"name": "Sample Company",
"visibleName": "Sample Company",
"isAccreditedIT": true,
"isTrusted": true,
"category": "COMPANY",
"state": "APPROVED",
"siteUrl": "https://www.example.com/",
"logoUrl": "https://hh.ru/employer-logo-original/0000000.png",
"logos": [
{ "type": "ORIGINAL", "url": "https://hh.ru/employer-logo-original/0000000.png" },
{ "type": "vacancyPage", "url": "https://hh.ru/employer-logo/0000000.png" }
],
"badges": [{ "type": "hrbrand", "description": "Sample badge" }]
},
"salaryFrom": 200000,
"salaryTo": 300000,
"salaryCurrency": "RUR",
"salaryGross": false,
"salaryMode": "MONTH",
"salaryFrequency": "TWICE_PER_MONTH",
"area": { "id": 1, "name": "ะœะพัะบะฒะฐ", "path": ".113.1." },
"address": {
"city": "ะœะพัะบะฒะฐ",
"street": "Sample Street",
"building": "1",
"displayName": "ะœะพัะบะฒะฐ, Sample Street, 1",
"lat": 55.7558,
"lng": 37.6173,
"metroStations": [
{ "id": 1, "name": "Sample Station", "lineColor": "#000000" }
],
"district": "Sample District"
},
"workExperience": "between3And6",
"employment": "FULL",
"employmentForm": "FULL",
"workSchedule": "remote",
"workScheduleByDays": ["FIVE_ON_TWO_OFF"],
"workFormats": ["REMOTE"],
"workingHours": ["HOURS_8"],
"nightShifts": false,
"internship": false,
"responseLetterRequired": false,
"acceptHandicapped": false,
"acceptLaborContract": true,
"responsesCount": 42,
"totalResponsesCount": 100,
"onlineUsersCount": 5,
"professionalRoleIds": [96, 156],
"languages": [{ "id": "eng", "name": "English", "level": "b2" }],
"userLabels": [],
"description": "<p>Full HTML description text goes here. Includes responsibilities, requirements, and conditions.</p>",
"descriptionText": "Full plain-text description text goes here. Includes responsibilities, requirements, and conditions.",
"keySkills": ["Python", "Django", "PostgreSQL", "Docker", "Git"],
"contactInfo": {
"name": "Contact Name",
"email": "contact@example.com",
"phones": [
{ "country": "7", "city": "495", "number": "0000000", "comment": null, "formatted": "7 495 0000000" }
],
"contactsHidden": false
},
"scrapedAt": "2026-05-05T12:00:00.000Z"
}

Plan requirement

  • Free Apify plan works. The default proxy is Apify Proxy datacenter, which is included on every plan and passes hh.ru reliably.
  • Residential RU is optional. If you want geo-pinned results (e.g. for region-specific salary surveys), pick Residential + RU in the proxy field. Requires Starter plan or higher.
  • Pagination cap: hh.ru caps each filter at 2000 vacancies (40 pages of 50). Use multiple queries or areas to slice past the cap โ€” see "A note on the 2000-result limit" above. The run warns you when a search exceeds what's reachable.
  • maxListings: 0 = unlimited. Bounded only by maxPages and the 2000-cap above.

Tips

  • Russian keywords work. Pass "queries": ["ะฟั€ะพะณั€ะฐะผะผะธัั‚"] directly.
  • Get the latest postings: set "orderBy": "publication_time" to walk newest-first.
  • Skip detail pages for speed: set "fetchDetails": false to scrape pure SERP data (~30 listings per second). Keeps vacancyId, salary, area, address, company basics, work format, but drops description, keySkills, contactInfo.
  • Detail coverage at scale: hh.ru rate-limits detail-page requests per IP. The actor detects the rate-limit ("light") page, retries blocked pages with backoff on fresh IPs, and runs a sequential recovery pass โ€” so large runs keep full descriptions instead of silently dropping them. Every record carries a detailScraped boolean, and the run log prints Detail descriptions: X/Y fetched (Z%). If you ever see <100%, re-run with a smaller maxListings or switch to Residential RU proxy for the best coverage.
  • Quick fan-out across regions: pass multiple areas (e.g. ["1", "2", "4"] for Moscow + St Petersburg + Novosibirsk) to compare a single keyword across markets.