# hh.ru Scraper - HeadHunter Jobs, Salaries & Skills (`haketa/hh-scraper`) Actor

Scrape job vacancies from hh.ru (HeadHunter), the #1 job board in Russia & CIS: title, company, salary, city, work format, experience, key skills, application counts and full description. Export to JSON, CSV or Excel.

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

## Pricing

from $1.75 / 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.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#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 Scraper 🇷🇺

**Extract job vacancies from hh.ru (HeadHunter) — the #1 job board in Russia & the CIS — into clean, structured data.** Search by keyword, region or filters and export thousands of jobs with **salary**, company, city, work format, required experience, **key skills**, application counts, geolocation and the full job description. Built for recruiters, job aggregators, salary benchmarking, market research and talent analytics.

[![Actor](https://img.shields.io/badge/Apify-Actor-00b04f?style=flat-square\&logo=apify)](https://apify.com)
[![Market](https://img.shields.io/badge/Market-Russia%20%26%20CIS%20🇷🇺-0039a6?style=flat-square)](https://apify.com)
[![Salary](https://img.shields.io/badge/Salary-Structured-brightgreen?style=flat-square)](https://apify.com)
[![Key skills](https://img.shields.io/badge/Key%20skills-Extracted-success?style=flat-square)](https://apify.com)
[![Output](https://img.shields.io/badge/Output-JSON%20%7C%20CSV%20%7C%20Excel-2b7489?style=flat-square)](https://apify.com)
[![Pagination](https://img.shields.io/badge/Pagination-Automatic-blue?style=flat-square)](https://apify.com)
[![Remote filter](https://img.shields.io/badge/Remote-Filter-orange?style=flat-square)](https://apify.com)
[![No code](https://img.shields.io/badge/No--code-Friendly-9cf?style=flat-square)](https://apify.com)
[![Maintained](https://img.shields.io/badge/Maintained-Yes-brightgreen?style=flat-square)](https://apify.com)

***

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

HeadHunter (hh.ru) is the largest employment marketplace in Russia and across the CIS — including **hh.kz** (Kazakhstan) and **hh.uz** (Uzbekistan) — with hundreds of thousands of active vacancies in every industry and region. This actor turns any hh.ru search into a structured dataset you can download or feed into your own tools.

Give it a **search URL** (or just a **keyword**) and it will:

- 🔎 Open the hh.ru results page and **read every vacancy** on it
- 📄 Automatically **paginate** through all result pages until it reaches your limit
- 💰 Capture **structured salary** — from / to / currency / gross-net / period
- 🧠 Extract **key skills** and the **full description** (optional detail mode)
- 📊 Include **application counts** — a unique signal of how competitive a role is
- 🌍 Flag **remote / hybrid / on-site** and add **metro station & geo-coordinates**
- 🧹 Return **clean, deduplicated, structured records** — no HTML, no clutter
- ⬇️ Export to **JSON, CSV, Excel, HTML or via API** in one click

No login, no browser extensions, no manual copy-paste. Point it at a search and get a spreadsheet.

***

### ✨ Key features

| Feature | Description |
| --- | --- |
| **Keyword or URL input** | Paste an hh.ru / hh.kz / hh.uz search URL, or just type keywords like `python`, `marketing`, `менеджер`. |
| **Structured salary** | Salary `from`, `to`, `currency`, gross/net flag and period — ready for benchmarking. |
| **Key skills** | The role's tagged skills (e.g. Java, Spring, SQL) — perfect for demand analysis. |
| **Application counts** | How many people have already applied — a competition/demand signal you won't find elsewhere. |
| **Full description** | Optional detail mode adds the complete job description and public contacts. |
| **Remote filter** | Toggle **Only remote jobs** to keep just remote-friendly roles. |
| **Geo & metro** | City, full address, nearest metro station and latitude/longitude. |
| **Experience & work format** | Required experience level, employment type and remote/hybrid/on-site format. |
| **Automatic pagination** | Walks through all result pages up to your `maxItems` limit. |
| **Deduplicated output** | Each vacancy appears once, identified by its unique hh.ru ID. |
| **Clean exports** | JSON, CSV, Excel, HTML table, RSS or JSON API. |

***

### 📥 Input

The actor accepts a simple, no-code input form. You can drive it two ways:

#### Option A — by keyword (easiest)

```json
{
    "keywords": ["python", "data analyst"],
    "area": "1",
    "maxItems": 200
}
```

`area` is an optional hh.ru region ID (e.g. `1` = Moscow, `2` = Saint Petersburg, `113` = all Russia, `40` = Kazakhstan). Leave it empty to search everywhere.

#### Option B — by search URL (most control)

Configure any filters you like on hh.ru (region, salary, experience, work format, schedule…), copy the URL from your browser, and paste it in:

```json
{
    "startUrls": [
        { "url": "https://hh.ru/search/vacancy?text=java&area=1&salary=200000&only_with_salary=true" }
    ],
    "includeDescription": true,
    "onlyRemote": true,
    "maxItems": 500
}
```

#### Input parameters

| Field | Type | Description |
| --- | --- | --- |
| `startUrls` | array | hh.ru / hh.kz / hh.uz results-page URLs. Configure your filters on the site, then paste the URL. |
| `keywords` | array | Job keywords to search. Used when no `startUrls` are given. |
| `area` | string | Optional region ID to combine with `keywords`. |
| `includeDescription` | boolean | Also fetch each detail page for full description, key skills and contacts. Default `false`. |
| `onlyRemote` | boolean | Keep only remote-friendly vacancies. Default `false`. |
| `maxItems` | integer | Maximum number of vacancies to collect. Default `200`. |
| `proxyConfiguration` | object | Proxy settings. A proxy is recommended for reliable results. |

***

### 📤 Output

Each vacancy is returned as a clean JSON object:

```json
{
    "id": 133213936,
    "url": "https://hh.ru/vacancy/133213936",
    "title": "Backend Team Lead (Java)",
    "company": "BANK URALSIB",
    "companyId": 89,
    "companyUrl": "https://hh.ru/employer/89",
    "salaryFrom": 300000,
    "salaryTo": 424400,
    "salaryCurrency": "RUR",
    "salaryGross": false,
    "salaryPeriod": "MONTH",
    "city": "Moscow",
    "address": "Moscow, Efremova, 8",
    "metro": ["Frunzenskaya"],
    "latitude": 55.727462,
    "longitude": 37.58022,
    "workSchedule": "remote",
    "workFormats": ["REMOTE", "HYBRID"],
    "experience": "6+ years",
    "employment": "FULL",
    "responsesCount": 1035,
    "onlineUsers": 14,
    "publishedAt": "2026-08-13T20:15:23.203+03:00",
    "description": "We are looking for a backend team lead…",
    "keySkills": ["Java", "Spring Framework", "REST", "SQL", "Spring Boot"],
    "contactName": null,
    "contactEmail": null,
    "contactPhones": null
}
```

#### Output fields

| Field | Type | Description |
| --- | --- | --- |
| `id` | number | Unique hh.ru vacancy ID. |
| `url` | string | Direct link to the vacancy. |
| `title` | string | Job title. |
| `company` / `companyId` / `companyUrl` | | Employer name, ID and profile link. |
| `salaryFrom` / `salaryTo` | number | Salary range bounds (null when not stated). |
| `salaryCurrency` | string | Currency code (RUR, USD, EUR, KZT…). |
| `salaryGross` | boolean | Whether the salary is gross (before tax). |
| `salaryPeriod` | string | Salary period (e.g. MONTH). |
| `city` / `address` | string | Location city and full address. |
| `metro` | array | Nearest metro station(s), when available. |
| `latitude` / `longitude` | number | Office geo-coordinates, when available. |
| `workSchedule` | string | Work schedule (remote, full day…). |
| `workFormats` | array | Work format tags (REMOTE, HYBRID, ON\_SITE). |
| `experience` | string | Required experience (No experience, 1-3 years, 3-6 years, 6+ years). |
| `employment` | string | Employment type (FULL, PART…). |
| `responsesCount` | number | Number of applications already received. |
| `onlineUsers` | number | Users currently viewing the vacancy. |
| `publishedAt` | string | Publish date/time (ISO 8601). |
| `description` | string | Full job description *(when `includeDescription` is enabled)*. |
| `keySkills` | array | Tagged key skills *(when `includeDescription` is enabled)*. |
| `contactName` / `contactEmail` / `contactPhones` | | Public recruiter contacts *(when shown and enabled)*. |

You can download the dataset as **JSON, CSV, Excel, HTML, RSS or XML**, or fetch it programmatically through the Apify API.

***

### 🚀 How to use hh.ru Scraper (step by step)

1. Click **Try for free** / **Start**.
2. Type a **keyword** (e.g. `python`) and an optional **region ID** — or paste an **hh.ru search URL** with your filters.
3. (Optional) Enable **Include full description & key skills** and/or **Only remote jobs**.
4. Set **maxItems** to how many vacancies you want.
5. Click **Save & Start**.
6. When the run finishes, open the **Dataset** tab and **Export** to your preferred format — or grab it via API.

That's it. No code required.

***

### 💡 Use cases

- **Salary benchmarking** — Aggregate structured salary ranges by role, region, experience and skill to build compensation datasets.
- **Recruitment & sourcing** — Monitor who is hiring for which roles across Russia and the CIS, and reach out directly.
- **Skills-demand analytics** — Quantify which key skills are most requested per role or region over time.
- **Job boards & aggregators** — Feed fresh CIS vacancies into your own portal or newsletter.
- **Competitive intelligence** — Track a competitor's open roles (and how many applicants they attract) to infer growth and hiring priorities.
- **Market research** — Measure demand by region, experience level, remote-work share and application volume.
- **HR & talent teams** — Benchmark your own postings and salaries against the live market.

***

### 📈 Why the application count matters

Unlike a plain job list, this actor also returns **how many people have already applied** to each vacancy (`responsesCount`) and how many are **viewing it right now** (`onlineUsers`). That's a direct signal of demand and competition — invaluable for sourcing strategy, salary negotiation and market analysis.

***

### ⏱️ How many jobs can I collect?

As many as the search returns (hh.ru shows up to ~2,000 results per search). Set `maxItems` to control the volume, and **split large sweeps by region or keyword** across multiple runs for the freshest, most complete coverage.

***

### 🔌 Integrations & automation

Because this runs on Apify, you can:

- 📅 **Schedule** runs (hourly, daily, weekly) to keep a live feed of new vacancies.
- 🔗 Push results to **Google Sheets, Airtable, Slack, Zapier, Make, or any webhook**.
- 🧩 Call it from your backend with the **Apify API** and SDKs (JavaScript / Python).
- 🔔 Use **webhooks** to trigger downstream workflows the moment a run completes.

#### Call via API (example)

```bash
curl -X POST "https://api.apify.com/v2/acts/YOUR_ACTOR_ID/runs?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
        "keywords": ["python"],
        "area": "1",
        "maxItems": 200
      }'
```

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

const client = new ApifyClient({ token: 'YOUR_TOKEN' });
const run = await client.actor('YOUR_ACTOR_ID').call({
    keywords: ['java'],
    includeDescription: true,
    maxItems: 500,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

***

### ❓ FAQ

**Do I need an hh.ru account or API key?**
No. Just provide a keyword or a search URL.

**Does it include salary?**
Yes — when the employer states it. Salary is returned as structured `from` / `to` / `currency` / gross-net / period. Many listings hide salary; those fields are simply `null`.

**Can I get the full job description and key skills?**
Yes. Enable **Include full description & key skills** and each vacancy's detail page is fetched for the complete description, skill tags and any public contacts.

**Does it work with hh.kz (Kazakhstan) and hh.uz (Uzbekistan)?**
Yes. Paste a search URL from those regional sites and it works the same way.

**Can I filter by region, salary, experience or work format?**
Yes. Apply any filters on hh.ru itself, then paste the resulting URL into `startUrls`. All of the site's filters are supported this way. You can also pass a `keywords` + `area` combination.

**Can I get only remote jobs?**
Yes — enable **Only remote jobs**.

**Are results deduplicated?**
Yes. Each vacancy is uniquely identified and appears only once per run.

**What formats can I export?**
JSON, CSV, Excel, HTML, RSS and XML — via the dashboard or the API.

**How do I keep data fresh?**
Schedule the actor to run on the interval you need; each run returns the current live listings.

***

### 📌 Tips for best results

- **Start narrow, then widen.** Test with a single keyword and a small `maxItems`, confirm the fields look right, then scale up.
- **Use search URLs for precision.** hh.ru's own filters (region, salary, experience, work format) give you exactly the segment you want — just paste the URL.
- **Enable descriptions only when you need them.** The list already includes salary, skills-context and metadata; turn on detail mode when you want the full text and key skills.
- **Split large sweeps.** For nationwide coverage, run several searches (by region or role) in parallel for speed and completeness.
- **Schedule for freshness.** The job market moves daily; a scheduled run keeps your dataset current.

***

### ⚖️ Legal & responsible use

This actor is intended for lawful data collection such as market research, analytics and recruitment. You are responsible for how you use the collected data. Only collect publicly available information, respect the target website's Terms of Service and `robots.txt`, and comply with all applicable laws and regulations, including data-protection rules such as the **GDPR** when processing any personal data. Do not use the data for spam or any unlawful purpose. This actor is an independent tool and is **not affiliated with, endorsed by, or connected to HeadHunter (hh.ru)** or its subsidiaries. All trademarks belong to their respective owners.

***

### 🛟 Support

Questions, feature requests or an issue with the data? Open an issue on the actor's page — feedback is welcome and helps improve the tool.

***

⭐ **If this actor saves you time, please leave a review — it really helps!**

# Actor input Schema

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

Search on hh.ru (or hh.kz / hh.uz) with any keyword and filters — region, salary, experience, work format — then paste the results-page URL here (e.g. https://hh.ru/search/vacancy?text=python\&area=1). The scraper paginates automatically. Leave empty to use the keyword below.

## `keywords` (type: `array`):

One or more job keywords to search (e.g. python, marketing, менеджер). Used only when no search URL is provided above.

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

hh.ru region ID to combine with the keywords above (e.g. 1 = Moscow, 2 = Saint Petersburg, 113 = all Russia, 40 = Kazakhstan). Leave empty to search everywhere.

## `includeDescription` (type: `boolean`):

Fetch each vacancy's detail page to add the full job description, key skills and public contacts. More complete, but slower.

## `onlyRemote` (type: `boolean`):

Keep only vacancies that offer remote work.

## `maxItems` (type: `integer`):

Maximum number of vacancies to collect across all URLs / keywords.

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

Proxy settings. A proxy is recommended for reliable, uninterrupted results.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://hh.ru/search/vacancy?text=python&area=1"
    }
  ],
  "keywords": [
    "python"
  ],
  "includeDescription": false,
  "onlyRemote": false,
  "maxItems": 200,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `id` (type: `string`):

hh.ru vacancy ID

## `url` (type: `string`):

Direct link to the vacancy

## `title` (type: `string`):

Job title

## `company` (type: `string`):

Hiring company name

## `companyUrl` (type: `string`):

Employer profile URL

## `salaryFrom` (type: `string`):

Lower salary bound

## `salaryTo` (type: `string`):

Upper salary bound

## `salaryCurrency` (type: `string`):

Salary currency

## `city` (type: `string`):

Job location city

## `workSchedule` (type: `string`):

Work schedule (remote/full day)

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

Required experience

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

Employment type

## `responsesCount` (type: `string`):

Number of applications received

## `publishedAt` (type: `string`):

Publish date/time

## `description` (type: `string`):

Full description (when enabled)

# 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 = {
    "startUrls": [
        {
            "url": "https://hh.ru/search/vacancy?text=python&area=1"
        }
    ],
    "keywords": [
        "python"
    ],
    "maxItems": 200
};

// Run the Actor and wait for it to finish
const run = await client.actor("haketa/hh-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 = {
    "startUrls": [{ "url": "https://hh.ru/search/vacancy?text=python&area=1" }],
    "keywords": ["python"],
    "maxItems": 200,
}

# Run the Actor and wait for it to finish
run = client.actor("haketa/hh-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 '{
  "startUrls": [
    {
      "url": "https://hh.ru/search/vacancy?text=python&area=1"
    }
  ],
  "keywords": [
    "python"
  ],
  "maxItems": 200
}' |
apify call haketa/hh-scraper --silent --output-dataset

```

## MCP server setup

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