# hh.ru Jobs Scraper (`parsebird/hh-ru-jobs-scraper`) Actor

Scrape job vacancies from hh.ru (HeadHunter) by keyword, region, experience, schedule, and employment type. Extract title, company, salary in RUR, skills, full description, work format, and apply links. Export as JSON, CSV, or Excel.

- **URL**: https://apify.com/parsebird/hh-ru-jobs-scraper.md
- **Developed by:** [ParseBird](https://apify.com/parsebird) (community)
- **Categories:**
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 1 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.09 / 1,000 vacancy scrapeds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

### hh.ru Jobs Scraper

Scrape job vacancies from **[hh.ru](https://hh.ru)** (HeadHunter) — Russia's largest job board — into clean, structured JSON. The hh.ru Jobs Scraper pulls the vacancy title, hiring company, salary in RUR, key skills, the full job description, work format, schedule, and apply link from any keyword search or vacancy URL, with no login and no hh.ru API key.

<table><tr>
<td style="border-left:4px solid #D6001C;padding:12px 16px;font-weight:600">
Search HeadHunter by keyword, region code, experience, schedule, and employment type — get the full salary range, key-skills list, and plain-text plus HTML description for every vacancy, paginated automatically.
</td>
</tr></table>

##### Copy to your AI assistant

Copy this block into ChatGPT, Claude, Cursor, or any LLM to start using this actor.

```
Actor: parsebird/hh-ru-jobs-scraper (Apify). Scrapes vacancies from hh.ru (HeadHunter). Call with ApifyClient.
Input JSON fields:
  startUrl: string — an hh.ru search URL (https://hh.ru/search/vacancy?...) or a single vacancy URL (https://hh.ru/vacancy/123). Overrides the fields below.
  text: string — search keyword, e.g. "python developer"
  area: string — hh.ru region code (1 = Moscow, 2 = Saint Petersburg, 113 = all of Russia). Full list at https://api.hh.ru/areas
  experience: "" | "noExperience" | "between1And3" | "between3And6" | "moreThan6"
  schedule: "" | "remote" | "fullDay" | "shift" | "flexible" | "flyInFlyOut"
  employment: "" | "full" | "part" | "project" | "volunteer" | "probation"
  results_wanted: int (default 20, hh.ru returns at most 2000 per search) — number of billed rows
  max_pages: int (default 20, 50 vacancies per page)
  api_only: bool (default true) — keep the summary record when a vacancy detail page is unavailable
  include_raw_data: bool (default false) — attach the raw hh.ru payload to each item
  proxyConfiguration: { "useApifyProxy": false }  (residential recommended only for very large runs)
Output: one dataset item per vacancy — vacancy_id, title, company, employer_id, employer_url, location, salary, salary_from, salary_to, salary_currency, salary_is_gross, experience, employment_type, schedule, work_format[], work_schedule_by_days[], working_hours[], skills[], description_text, description_html, date_posted, date_expires, responses_count, url, apply_url, source, source_page, source_position, scraped_at.
Example: { "text": "python developer", "area": "1", "results_wanted": 50 }
API: https://api.apify.com/v2/acts/parsebird~hh-ru-jobs-scraper  ·  Token: https://console.apify.com/account/integrations
```

### What does hh.ru Jobs Scraper do?

[hh.ru](https://hh.ru) (HeadHunter) carries millions of active vacancies across Russia and the CIS. Its public API blocks datacenter and non-Russian IP addresses, so this hh.ru scraper reads the website's own embedded data instead — which works from any location. It extracts:

- 💼 **Vacancy basics** — title, hiring company, employer profile link, and location.
- 💰 **Salary** — the published range, currency (usually RUR), and whether it is gross or net.
- 🧠 **Key skills** — the skill tags the employer attached to the role.
- 📄 **Full description** — both plain text and the original HTML.
- 🏢 **Work terms** — experience level, employment type, schedule (remote / full day / shift), and work format (on-site / remote / hybrid).
- 📅 **Dates** — publication and expiry timestamps, plus the current response count.
- 🔗 **Apply link** — the direct hh.ru application URL.

Common uses: salary and skills-demand research for the Russian market, recruitment intelligence, building a jobs aggregator, and monitoring hiring at specific companies.

### Input parameters

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| startUrl | string | No | — | An hh.ru search URL or a single vacancy URL. Overrides the search fields. |
| text | string | No | — | Search keyword (role or skill). |
| area | string | No | `1` | hh.ru region code. `1` Moscow, `2` Saint Petersburg, `113` all of Russia. |
| experience | string | No | Any | `noExperience`, `between1And3`, `between3And6`, `moreThan6`. |
| schedule | string | No | Any | `remote`, `fullDay`, `shift`, `flexible`, `flyInFlyOut`. |
| employment | string | No | Any | `full`, `part`, `project`, `volunteer`, `probation`. |
| results\_wanted | integer | No | 20 | Max vacancies to collect (billed rows). hh.ru returns at most 2,000 per search. |
| max\_pages | integer | No | 20 | Safety limit on search pages scanned (50 vacancies per page). |
| api\_only | boolean | No | true | Keep the summary record when a detail page cannot be loaded. |
| include\_raw\_data | boolean | No | false | Attach the raw hh.ru payload under a `raw` field. |
| proxyConfiguration | object | No | Off | Proxy settings. Residential recommended only for very large runs. |

#### Example inputs

Python developer vacancies in Moscow:

```json
{ "text": "python developer", "area": "1", "results_wanted": 50 }
```

Remote data-analyst roles across Russia with 1–3 years of experience:

```json
{
  "text": "data analyst",
  "area": "113",
  "experience": "between1And3",
  "schedule": "remote",
  "results_wanted": 100,
  "max_pages": 10
}
```

Start from a saved search URL:

```json
{
  "startUrl": "https://hh.ru/search/vacancy?area=2&experience=between1And3&search_field=description&text=golang",
  "results_wanted": 25
}
```

Collect one fully detailed vacancy:

```json
{ "startUrl": "https://hh.ru/vacancy/132160978", "results_wanted": 1 }
```

### What data can you extract from hh.ru?

| Field | Description |
|-------|-------------|
| vacancy\_id | hh.ru vacancy ID |
| title | Vacancy title |
| company / employer\_id / employer\_url | Hiring company and its hh.ru profile |
| location | City and address when available |
| salary / salary\_from / salary\_to / salary\_currency / salary\_is\_gross | Salary summary and structured bounds |
| experience | Required experience level |
| employment\_type | full, part, project, volunteer, probation |
| schedule | fullDay, remote, shift, flexible, flyInFlyOut |
| work\_format | ON\_SITE, REMOTE, HYBRID |
| work\_schedule\_by\_days / working\_hours | Working pattern when published |
| skills | Key skills listed for the role |
| description\_text / description\_html | Plain-text and HTML job description |
| date\_posted / date\_expires | Publication and expiry timestamps |
| responses\_count | Current number of applicants |
| url / apply\_url | Direct vacancy and apply links |
| source / scraped\_at | Source label and collection timestamp |

### Output example

```json
{
  "vacancy_id": "114811554",
  "title": "SQL-программист / Разработчик баз данных",
  "company": "Аптечная сеть Ваша №1 х Таблетка.ру",
  "employer_id": "3344320",
  "employer_url": "https://hh.ru/employer/3344320",
  "location": "Москва",
  "salary": "from 150000 RUR net",
  "salary_from": 150000,
  "salary_currency": "RUR",
  "salary_is_gross": false,
  "experience": "between3And6",
  "employment_type": "full",
  "schedule": "fullDay",
  "work_format": ["ON_SITE"],
  "skills": ["SQL", "Python", "PostgreSQL", "MS SQL"],
  "description_text": "Full plain text vacancy description...",
  "description_html": "<p>Full HTML vacancy description...</p>",
  "date_posted": "2026-07-17T11:51:56.233+03:00",
  "date_expires": "2026-08-16T11:51:56+03:00",
  "responses_count": 42,
  "url": "https://hh.ru/vacancy/114811554",
  "apply_url": "https://hh.ru/applicant/vacancy_response?vacancyId=114811554",
  "source": "hh.ru",
  "scraped_at": "2026-08-10T07:14:35.150Z"
}
```

Download the dataset as **JSON, CSV, Excel, HTML, or XML** from the Storage tab or the [Apify API](https://docs.apify.com/api/v2).

### Use cases

- **Salary benchmarking** — aggregate published RUR ranges by role, region, and experience.
- **Skills-demand research** — track which skills appear most across a keyword or region over time.
- **Recruitment intelligence** — watch response counts and time-to-expiry to see how competitive a role is.
- **Company monitoring** — start from a `search_field=company_name` URL and collect everything one employer posts.
- **Jobs aggregator** — power a Russian-market job product with structured, deduplicated vacancies.

### How it works

1. **Search** — the scraper requests hh.ru's search results page and reads the embedded state (50 vacancies per page), applying `text`, `area`, `experience`, `schedule`, and `employment`.
2. **Detail** — each vacancy page is fetched and parsed for the full salary, key skills, description (text + HTML), work format, and expiry.
3. **Resilience** — the HTTP session rotates automatically and backs off when hh.ru throttles, so large runs stay complete.
4. Rows stream to the dataset and stop at `results_wanted` (or hh.ru's 2,000-result ceiling).

### How much does it cost to scrape hh.ru?

This actor is **pay per result** — charged once per vacancy saved.

| Plan | Price per 1,000 vacancies |
|------|---------------------------|
| Free | **$1.39** |
| Bronze | **$1.29** |
| Silver | **$1.19** |
| Gold | **$1.09** |

A small platform **Actor start** fee also applies per run. `results_wanted` is the exact number of billed rows. Scraping 1,000 vacancies on the Free plan costs about **$1.39**. Apify's monthly free platform usage covers small runs.

### How to scrape hh.ru

1. Click **Try for free** and sign in to Apify.
2. Enter a **search keyword** and a **region code** (1 = Moscow, 2 = Saint Petersburg, 113 = all of Russia).
3. Optionally set **experience**, **schedule**, or **employment**.
4. Set **results wanted** (start low, e.g. 20).
5. Click **Start**, then watch rows appear in the **Output** tab.
6. Export from **Storage**, or pull results via the API.

#### Run it via API

Python:

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("parsebird/hh-ru-jobs-scraper").call(run_input={
    "text": "python developer",
    "area": "1",
    "results_wanted": 50,
})
for v in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(v["title"], "-", v["company"], "-", v.get("salary"))
```

JavaScript:

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

const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('parsebird/hh-ru-jobs-scraper').call({
    text: 'golang',
    area: '113',
    experience: 'between3And6',
    results_wanted: 100,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

Schedule recurring runs, add webhooks, or push results to Google Sheets, Slack, Zapier, and Make with [Apify integrations](https://docs.apify.com/platform/integrations).

### FAQ

**Do I need an hh.ru account or API key?**
No. The scraper reads public hh.ru vacancy pages only. hh.ru's official API blocks datacenter and non-Russian IPs, so this actor reads the website's embedded data instead.

**How current is the data?**
Live. Each run reads hh.ru at that moment. Use Apify's scheduler to keep a dataset fresh.

**Why is `results_wanted` limited in practice?**
hh.ru never returns past the 2,000th result of any search. Narrow the search (region, keyword, filters) to reach vacancies beyond that.

**Some vacancies came back without a description or skills — why?**
Those rows are summary records: the vacancy's detail page could not be loaded on that pass (usually a transient block). With `api_only: true` (default) they are kept anyway; re-running usually fills them in.

**Where do I find a region code?**
Open <https://api.hh.ru/areas> or read the `area=` value in any hh.ru search URL.

**Something's not working.**
Open an issue on the **Issues** tab with your input and the run link — we respond quickly.

### Is it legal to scrape hh.ru?

Scraping publicly available pages is legal in most jurisdictions, and this actor only reads vacancy pages any visitor can see — no login, no private data. You are responsible for how you use the data — respect hh.ru's Terms of Use and applicable law (including Russian data-protection rules) when handling any personal data, and consult a lawyer if you are unsure. See Apify's guide on [the legality of web scraping](https://blog.apify.com/is-web-scraping-legal/).

### Related actors

- [Instahyre Jobs Scraper](https://apify.com/parsebird/instahyre-jobs-scraper) — India tech jobs with employer context.
- [MyCareersFuture Singapore Jobs Scraper](https://apify.com/parsebird/mycareersfuture-jobs-scraper) — Singapore's national jobs portal.
- [Indeed Jobs Scraper](https://apify.com/parsebird/indeed-jobs-scraper) — job listings from Indeed worldwide.
- [Freshersworld Jobs Scraper](https://apify.com/parsebird/freshersworld-jobs-scraper) — India fresher and entry-level jobs.

Browse all [ParseBird actors](https://apify.com/parsebird) on Apify Store.

# Actor input Schema

## `startUrl` (type: `string`):

An hh.ru search URL (https://hh.ru/search/vacancy?...) or a single vacancy URL (https://hh.ru/vacancy/123). Overrides the search fields below.

## `text` (type: `string`):

Role title or skill, e.g. "python developer", "аналитик данных".

## `area` (type: `string`):

hh.ru area code. 1 = Moscow, 2 = Saint Petersburg, 113 = all of Russia. Find other codes at https://api.hh.ru/areas

## `experience` (type: `string`):

Required experience filter.

## `schedule` (type: `string`):

Work schedule filter.

## `employment` (type: `string`):

Employment type filter.

## `results_wanted` (type: `integer`):

Maximum vacancies to collect. This is the number of billed rows. hh.ru itself returns at most 2,000 results per search.

## `max_pages` (type: `integer`):

Safety limit on search result pages scanned (50 vacancies per page).

## `api_only` (type: `boolean`):

When a vacancy's detail page cannot be loaded, keep the shorter summary record and continue instead of skipping it.

## `include_raw_data` (type: `boolean`):

Add the full raw hh.ru payload to each item under a `raw` field. Makes the dataset much larger.

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

hh.ru works from datacenter IPs. Enable a residential proxy only for very large runs.

## Actor input object example

```json
{
  "text": "python developer",
  "area": "1",
  "experience": "",
  "schedule": "",
  "employment": "",
  "results_wanted": 20,
  "max_pages": 20,
  "api_only": true,
  "include_raw_data": false,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `dataset` (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 = {
    "startUrl": "",
    "text": "python developer",
    "area": "1",
    "results_wanted": 20,
    "max_pages": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("parsebird/hh-ru-jobs-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 = {
    "startUrl": "",
    "text": "python developer",
    "area": "1",
    "results_wanted": 20,
    "max_pages": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("parsebird/hh-ru-jobs-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 '{
  "startUrl": "",
  "text": "python developer",
  "area": "1",
  "results_wanted": 20,
  "max_pages": 20
}' |
apify call parsebird/hh-ru-jobs-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,parsebird/hh-ru-jobs-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/mLKv0hcQ5wa6ojVUe/builds/rgeOSFlE7H37lXUij/openapi.json
