Hh.ru Jobs Scraper
Pricing
from $0.88 / 1,000 item extracteds
Hh.ru Jobs Scraper
Search public hh.ru jobs by keyword, location, filters, or URL. Export normalized vacancy, employer, salary, experience, schedule, skills, and canonical URL records.
Pricing
from $0.88 / 1,000 item extracteds
Rating
0.0
(0)
Developer
Automation Lab
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
2
Monthly active users
a day ago
Last modified
Categories
Share
Collect public hh.ru jobs by keyword, location, and source URL. The Actor exports one normalized vacancy record per result, including title, employer, location, experience, compensation context, publication context, and a canonical URL.
Use lightweight search extraction for recurring labor-market snapshots, or enable detail enrichment to add full descriptions, employment terms, schedules, and key skills.
What can Hh.ru Jobs Scraper do?
- Search hh.ru by keyword and one or more area IDs.
- Process an existing filtered hh.ru search URL.
- Extract a known public hh.ru vacancy URL.
- Filter by experience, employment form, schedule, publication period, and sort order.
- Deduplicate vacancies across areas and search URLs by stable vacancy ID.
- Normalize displayed salary bounds, currency, gross/net and salary-period context.
- Include visible work-format labels, such as remote availability, without extra requests.
- Optionally enrich every result from its detail page.
- Stop predictably at
maxItemsandmaxPageslimits.
The Actor reads server-rendered public pages without a browser. This keeps ordinary runs fast and economical while retaining the source's displayed context.
Who is it for?
Recruiting intelligence teams
Build repeatable snapshots of active vacancies by role and region. Compare dataset exports from scheduled runs to see changes in hiring demand.
Labor-market analysts
Aggregate titles, employers, locations, experience requirements, and displayed compensation into a spreadsheet, warehouse, or BI workflow.
Recruiters and sourcing teams
Research employer activity and role requirements before designing a sourcing campaign. Enable details when full descriptions and key skills matter.
Developers and data teams
Feed typed vacancy records into an internal pipeline through the Apify API, webhooks, integrations, or MCP.
What hh.ru job data is extracted?
| Field | Meaning |
|---|---|
vacancyId | Stable hh.ru vacancy identifier |
title | Public vacancy title |
url | Canonical https://hh.ru/vacancy/{id} URL |
employer | Employer name, when displayed |
employerUrl | Public hh.ru employer page |
location | City or address displayed by hh.ru |
salary | Object with from, to, currency, gross, period, and original text |
experience | Displayed experience requirement |
employment | Employment form from the detail page, or null |
schedule | Work schedule from the detail page, or null |
workFormat | Visible card label(s) or detail work-format text; null when absent |
publishedAt | Source timestamp when exposed, or null |
snippet | Search-card requirements and responsibilities |
description | Full text when includeDetails is enabled |
keySkills | Skills from the vacancy detail page |
sourceSearchUrl | Filtered search page that produced the row |
scrapedAt | UTC collection timestamp |
Optional fields are null when hh.ru does not display them or detail enrichment is disabled. salary.period is one of hour, day, shift, week, month, year, or null; it is normalized only from an explicit displayed unit. A missing unit is never assumed monthly. workFormat preserves visible text, not a guessed enum; a listing remote label and a detail multi-format description may differ.
How to search hh.ru jobs
- Open the Actor input page.
- Enter a
query, such asPython developer. - Set one or more
areaIds;1is Moscow,2is Saint Petersburg, and113is Russia. - Choose filters and a sort order.
- Keep
includeDetailsoff for a quick listing feed, or enable it for full vacancy content. - Set
maxItemsandmaxPagesto control scope. - Start the run and download the default dataset as JSON, CSV, Excel, XML, or another supported format.
A query or at least one start URL is required.
Input parameters
| Input | Type | Default | Description |
|---|---|---|---|
query | string | — | Search words plus a strict title-only relevance gate on all records. |
areaIds | string/number array | Schema/platform: ['1']; raw runtime fallback: [113] | hh.ru location IDs; one search per area. Ignored with supplied URLs. |
startUrls | URL array | — | Public hh.ru search or vacancy URLs; overrides generated searches. |
experience | string array | — | hh.ru experience IDs such as between1And3. |
employment | string array | — | Employment-form values supported by hh.ru search. |
schedule | string array | — | Schedule values such as remote or fullDay. |
periodDays | integer | — | Include vacancies from the past 1–30 days. |
orderBy | string | Schema/platform: publication_time; raw runtime fallback: source/URL order | Relevance, publication time, or salary order. |
includeDetails | boolean | false | Add descriptions, employment, schedule, and skills. |
maxItems | integer | 50 | Maximum unique records across all searches. |
maxPages | integer | 5 | Maximum result pages per search or area. |
Supplied start URLs take precedence over generated area searches. A nonempty query overrides their search text and is also applied to every resulting title only, not employer or description. Every case-insensitive query token must be a title substring; limited English/Russian aliases cover analyst, data, developer and engineer. This deliberately rejects some loose hh.ru matches. Omit query for a URL-only workflow. Other supplied filters replace corresponding search-URL parameters; they do not filter individual vacancy URLs. Upstream filter coverage remains controlled by hh.ru.
The schema/form supplies Moscow (1) and newest-first (publication_time); Apify also injected these defaults in verified platform API runs. At the raw runtime level only, an absent or empty areaIds searches Russia (113) and an absent orderBy preserves URL/source order. Do not assume omission bypasses platform/schema defaults; use explicit supported values for reproducible workflows. Limits must be finite integers: maxItems 1–10,000 globally after filtering/deduplication; maxPages 1–100 per search/area. Zero is not unlimited. periodDays accepts 1–30; arrays and booleans must have their stated types. The API runtime accepts string URLs or { "url": "…" } objects; the Console request-list editor uses objects.
Example input: Moscow Python vacancies
{"query": "Python developer","areaIds": ["1"],"orderBy": "publication_time","includeDetails": false,"maxItems": 20,"maxPages": 2}
Example enriched output
Selected fields from a public direct-vacancy control captured on 2026-10-22 (https://hh.ru/vacancy/136083468, no query). Detail text is omitted here; the live run returns the full description. Public availability and values can change.
{"vacancyId": "136083468","title": "Senior backend developer (PHP, Symfony) в Fintech","url": "https://hh.ru/vacancy/136083468","salary": {"from": 400000,"to": 600000,"currency": "RUB","gross": false,"period": "month","text": "от 400 000 до 600 000 ₽ за месяц на руки"},"workFormat": "Формат работы: на месте работодателя, удалённо или гибрид","keySkills": ["Git", "Symfony", "PHP", "SQL", "Laravel", "Английский — B1 — Средний"],"publishedAt": "2026-10-17T04:46:32.251Z","sourceSearchUrl": null}
Listing mode or detail enrichment?
Use includeDetails: false for frequent market snapshots where title, employer, location, salary, experience, snippet, and URL are sufficient. It performs one request per result page.
Use includeDetails: true when the workflow needs descriptions, key skills, schedules, detailed addresses, or employment terms. It performs an additional public page request per vacancy and therefore runs longer.
Both modes emit the same stable record shape. Detail-only fields remain nullable in listing mode.
How much does it cost to extract hh.ru vacancies?
The Actor uses pay-per-event pricing:
- a one-time Start event for each run;
- one Item processed event for each vacancy saved to the dataset.
The current initial curve starts at $0.02 per run and $0.001468 per vacancy on the BRONZE tier. Final billed prices depend on your Apify plan tier. For example, at BRONZE, a run returning 25 vacancies costs about $0.0567 in Actor charges: $0.02 + 25 × $0.001468. A 100-vacancy run costs about $0.1668.
Failed, rejected, duplicate, or filtered-out vacancy records do not generate an item event. Apify displays the applicable event rates before you start a paid run.
Recurring recruiting-intelligence workflow
- Configure a narrow role, region, and
periodDayswindow. - Sort by
publication_time. - Save the task and add a schedule in Apify Console.
- Export each run's dataset to Google Sheets, a webhook, or cloud storage.
- Compare rows by
vacancyIdto identify additions, removals, and changed public content.
The Actor does not maintain cross-run state or label changes itself. Stable IDs and timestamps make that comparison straightforward in your destination system.
Integrations and exports
Use Apify integrations to send the default dataset to:
- Google Sheets for recruiter review;
- webhooks for downstream automation;
- Make, Zapier, or n8n workflows;
- Amazon S3, Google Cloud Storage, or Azure storage;
- a database or warehouse through your own API client.
CSV is convenient for analysts. JSON preserves nested salary data and skill arrays for applications and data pipelines.
Run through the Apify API with cURL
curl -X POST \"https://api.apify.com/v2/acts/automation-lab~hh-ru-job-listings-scraper/runs?token=$APIFY_TOKEN" \-H 'Content-Type: application/json' \-d '{"query":"Data analyst","areaIds":["113"],"periodDays":7,"maxItems":25}'
Do not commit API tokens to source control. Use an environment variable or secret manager.
Run with JavaScript
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: process.env.APIFY_TOKEN });const run = await client.actor('automation-lab/hh-ru-job-listings-scraper').call({query: 'Python developer',areaIds: ['1'],maxItems: 50,includeDetails: false,});const { items } = await client.dataset(run.defaultDatasetId).listItems();console.log(items);
Run with Python
import osfrom apify_client import ApifyClientclient = ApifyClient(os.environ['APIFY_TOKEN'])run = client.actor('automation-lab/hh-ru-job-listings-scraper').call(run_input={'query': 'Data analyst','areaIds': ['113'],'periodDays': 7,'includeDetails': True,'maxItems': 10,})items = client.dataset(run['defaultDatasetId']).list_items().itemsprint(items)
Use with MCP and AI assistants
Add the Actor to Claude Code:
claude mcp add --transport http apify \"https://mcp.apify.com?tools=automation-lab/hh-ru-job-listings-scraper"
Claude Desktop, Cursor, and VS Code setup
Claude Desktop, Cursor, and VS Code can use this equivalent MCP configuration:
{"mcpServers": {"apify": {"url": "https://mcp.apify.com?tools=automation-lab/hh-ru-job-listings-scraper"}}}
Example prompts:
- “Find the newest 20 Python developer vacancies in Moscow on hh.ru and summarize salary ranges.”
- “Extract this hh.ru vacancy URL with details and list the required skills.”
- “Collect Russian data analyst vacancies posted in the last seven days for my weekly market report.”
Follow one run and retrieve bounded results
- Discover the actual tool names and argument schemas with scoped
tools/listusing the Actor-specific URL above. It also exposes run/data helpers (get-actor-run,get-dataset-items,get-key-value-store-record,abort-actor-run), not exactly one tool. Discovering tools does not authorize starting or aborting a run; use only the needed Actor/run/data subset. - For an authorized invocation, start once and retain the returned run ID, status and storage IDs. These are metadata, not vacancy rows.
call-actorwaits at mostwaitSecs(0–45 seconds, default 30); configure the client timeout above that wait plus transport margin. If nonterminal, follow the same run withget-actor-run, for example after 2, 4 and 8 seconds, capped at 10 seconds between checks and a 120-second total deadline. On a client timeout, check the known run rather than restarting; if its ID is unknown, report uncertainty. At the deadline, report pending with the run ID and stop. Report terminal failures honestly; any saved rows are partial. - After success, read the returned
defaultDatasetIdusingget-dataset-itemsand the discovered argument schema: explicitoffset: 0,limit: 20and needed fields such asvacancyId,title,url,salary,workFormat. Prefer untransformed JSON. Advance source offsets using pagination metadata, not transformed row counts. Across all pages, admit at most 100 rows or 64 KiB of serialized UTF-8 result content to model context, whichever comes first; stop earlier at dataset exhaustion. Process pages host-side before injection, including oversized single rows/pages: omit or summarize them with disclosure rather than silently claiming completeness. A hard byte ceiling requires a client that can intercept responses; otherwise disclose that limitation and do not request unbounded results. - Report returned rows, continuation offset and any partial/truncated result. Keep full exports in Apify storage or downstream processing outside model context. These consumer budgets do not reduce Actor collection limits or output fields. Discovery caching, progressive loading and prompt caching depend on the client/capabilities; no universal token-savings guarantee is made.
The waits, deadline and context budgets above are example consumer choices, not Actor or Apify defaults.
Reliability, limits, and responsible usage
hh.ru controls page availability and markup. The Actor makes at most three attempts for transient network, rate-limit, and server errors (two retries). Stable 4xx responses, unsupported URLs, and unrecognized pages fail clearly rather than returning a misleading empty dataset.
Only an explicit hh.ru no-results marker is treated as zero source cards. Unknown/challenge/login pages and broken vacancy cards fail clearly. Valid cards can still yield zero output after title filtering; bounded progress counters report search pages, detail requests, HTTP attempts/retries, parsed, filtered, duplicate and saved rows without raw page bodies or query logging.
Search results can change during a paginated run. Very broad queries may be capped by hh.ru's own search behavior. maxPages is a per-search safety limit; maxItems is the global dataset limit.
The Actor uses direct public pages and has no automatic residential-proxy fallback. If source access changes, review run logs instead of blindly repeating runs.
Diagnostics, data handling and support
This Actor uses no AI and no paid third-party data API or proxy service. Public vacancy output is stored in your Apify run dataset under your account's retention/deletion settings; remove runs and datasets in Console when no longer needed. There is no persistent cross-run cache. Source HTML is processed in memory, not stored as a diagnostic attachment.
Failed operations send sanitized diagnostic input, exceptions and Actor/build/run IDs to our private GlitchTip service for repair. Secret fields, URL queries, caller search text and complete start URLs are removed; reports are retained for 30 days. Telemetry credentials are protected runtime configuration, not user inputs. Diagnostics do not change item charging or expose source HTML. For problems or deletion requests, open an issue on this Actor's Apify Issues tab; include a run link, not credentials.
Legal and ethical use
This Actor extracts information that hh.ru displays publicly. You are responsible for ensuring your use complies with hh.ru's terms, applicable database and copyright rules, privacy law, and employment regulations.
Collect only fields needed for a legitimate purpose. Do not use vacancy or employer data for spam, harassment, discrimination, or decisions that require human review. Respect deletion requests and set appropriate retention periods in downstream systems.
This Actor is not affiliated with or endorsed by HeadHunter or hh.ru.
Troubleshooting
Why did the run reject my URL?
Only public hh.ru/search/vacancy... and hh.ru/vacancy/{numeric-id} URLs are supported. Profile, résumé, login, employer-admin, and other pages are intentionally rejected.
Why are detail fields null?
Enable includeDetails. Some vacancies still omit salary, key skills, schedule, or other optional fields at the source.
Why did I receive fewer records than maxItems?
The filtered source may have fewer vacancies, duplicate IDs may occur across searches, a supplied query may not match an explicit vacancy, or maxPages may stop pagination first.
Why did the Actor fail instead of returning zero records?
A valid no-result search exits successfully with zero rows. A challenge page, unsupported response, or deterministic source error fails so an automation does not mistake a source outage for “no vacancies.”
Related Automation Lab Actors
- IrishJobs.ie Job Listings & Salaries Scraper for Irish vacancy intelligence.
- GCC Job Listings Aggregator for normalized Gulf-region hiring feeds.
- Built In Job Listings Scraper for technology-company vacancy research.
Choose this Actor when the required source is hh.ru and the workflow needs stable vacancy IDs, normalized salary context, or optional detail enrichment.
FAQ
Does it require an hh.ru account?
No. It processes public search and vacancy pages only.
Can it search multiple locations?
Yes. Add multiple hh.ru IDs to areaIds. Results are deduplicated across generated searches.
Can I provide a fully filtered search URL?
Yes. Put it in startUrls. Input filters are then applied to that URL before pagination.
Does it scrape résumés or personal candidate profiles?
No. The product scope is public job vacancies and displayed employer context.
Does it detect newly added or changed jobs?
It emits stable vacancyId and scrapedAt values. Schedule tasks and compare run datasets in your own spreadsheet or pipeline; the Actor does not persist a monitoring database.
Can I increase maxItems?
Yes, up to 10,000. Increase maxPages as needed, and prefer listing mode unless details are required.