# Hh.ru Jobs Scraper (`automation-lab/hh-ru-job-listings-scraper`) Actor

Search public hh.ru jobs by keyword, location, filters, or URL. Export normalized vacancy, employer, salary, experience, schedule, skills, and canonical URL records.

- **URL**: https://apify.com/automation-lab/hh-ru-job-listings-scraper.md
- **Developed by:** [Stas Persiianenko](https://apify.com/automation-lab) (community)
- **Categories:** Jobs
- **Stats:** 2 total users, 1 monthly users, 80.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.88 / 1,000 item extracteds

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

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, and gross/net context.
- Optionally enrich every result from its detail page.
- Stop predictably at `maxItems` and `maxPages` limits.

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`, 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` |
| `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.

### How to search hh.ru jobs

1. Open the Actor input page.
2. Enter a `query`, such as `Python developer`.
3. Set one or more `areaIds`; `1` is Moscow, `2` is Saint Petersburg, and `113` is Russia.
4. Choose filters and a sort order.
5. Keep `includeDetails` off for a quick listing feed, or enable it for full vacancy content.
6. Set `maxItems` and `maxPages` to control scope.
7. 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. Also narrows explicit vacancy URLs. |
| `areaIds` | string array | `['1']` | hh.ru location IDs; one search is run per area. |
| `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 | `publication_time` | 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. |

Filters are appended to supplied search URLs, so explicit and generated searches follow the same requested scope. For a supplied individual vacancy URL, `query` is applied to its title, employer, and full description.

### Example input: Moscow Python vacancies

```json
{
  "query": "Python developer",
  "areaIds": ["1"],
  "orderBy": "publication_time",
  "includeDetails": false,
  "maxItems": 20,
  "maxPages": 2
}
```

### Example enriched output

This shortened record reflects current Actor output; values change as hh.ru vacancies change.

```json
{
  "vacancyId": "134772804",
  "title": "Специалист обработки электронных баз данных (ул. Союзная)",
  "url": "https://hh.ru/vacancy/134772804",
  "employer": "METRO.Торговые центры",
  "location": "Ижевск, Союзная улица, 6",
  "salary": {
    "from": null,
    "to": 55200,
    "currency": "RUB",
    "gross": true,
    "text": "55 200 ₽ за месяц до вычета налогов"
  },
  "experience": "не требуется",
  "employment": "Полная занятость",
  "schedule": "График: 5/2",
  "description": "Full public vacancy description…",
  "keySkills": ["Внимательность", "Ответственность"],
  "sourceSearchUrl": "https://hh.ru/search/vacancy?text=Data+analyst&area=113&page=0",
  "scrapedAt": "2026-09-06T06:44:00.000Z"
}
```

### 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

1. Configure a narrow role, region, and `periodDays` window.
2. Sort by `publication_time`.
3. Save the task and add a schedule in Apify Console.
4. Export each run's dataset to Google Sheets, a webhook, or cloud storage.
5. Compare rows by `vacancyId` to 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

```bash
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

```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

```python
import os
from apify_client import ApifyClient

client = 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().items
print(items)
```

### Use with MCP and AI assistants

Add the Actor to Claude Code:

```bash
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:

```json
{
  "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.”

### Reliability, limits, and responsible usage

hh.ru controls page availability and markup. The Actor retries transient network, rate-limit, and server errors up to three times. Stable 4xx responses, unsupported URLs, and unrecognized pages fail clearly rather than returning a misleading empty dataset.

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.

### 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](https://apify.com/automation-lab/irishjobs-job-listings-scraper) for Irish vacancy intelligence.
- [GCC Job Listings Aggregator](https://apify.com/automation-lab/gcc-job-listings-aggregator) for normalized Gulf-region hiring feeds.
- [Built In Job Listings Scraper](https://apify.com/automation-lab/builtin-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.

# Actor input Schema

## `query` (type: `string`):

Words to find in vacancy titles and descriptions, for example Python developer.

## `areaIds` (type: `array`):

hh.ru area IDs to search. Common values: 1 Moscow, 2 Saint Petersburg, 113 Russia. One search is run per area.

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

Optional public hh.ru search result or individual vacancy URLs. These override generated searches; the filters below still apply.

## `experience` (type: `array`):

Optional hh.ru experience filters.

## `employment` (type: `array`):

Optional employment-form filters supported by hh.ru search.

## `schedule` (type: `array`):

Optional hh.ru schedule IDs, such as remote, fullDay, flexible, or shift.

## `periodDays` (type: `integer`):

Only include vacancies published within this many days.

## `orderBy` (type: `string`):

How hh.ru should order search results.

## `includeDetails` (type: `boolean`):

Fetch each vacancy page to add the full description, employment, schedule, and key skills. This is slower but produces richer records.

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

Maximum number of unique vacancy records saved across all searches.

## `maxPages` (type: `integer`):

Safety limit for hh.ru result pages processed for each search URL or area.

## Actor input object example

```json
{
  "query": "Python developer",
  "areaIds": [
    "1"
  ],
  "startUrls": [
    {
      "url": "https://hh.ru/search/vacancy?text=Data%20Analyst&area=1"
    }
  ],
  "orderBy": "publication_time",
  "includeDetails": false,
  "maxItems": 20,
  "maxPages": 5
}
```

# Actor output Schema

## `overview` (type: `string`):

Normalized vacancy records in the default dataset overview.

# 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 = {
    "query": "Python developer",
    "areaIds": [
        "1"
    ],
    "startUrls": [
        {
            "url": "https://hh.ru/search/vacancy?text=Data%20Analyst&area=1"
        }
    ],
    "maxItems": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("automation-lab/hh-ru-job-listings-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 = {
    "query": "Python developer",
    "areaIds": ["1"],
    "startUrls": [{ "url": "https://hh.ru/search/vacancy?text=Data%20Analyst&area=1" }],
    "maxItems": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("automation-lab/hh-ru-job-listings-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 '{
  "query": "Python developer",
  "areaIds": [
    "1"
  ],
  "startUrls": [
    {
      "url": "https://hh.ru/search/vacancy?text=Data%20Analyst&area=1"
    }
  ],
  "maxItems": 20
}' |
apify call automation-lab/hh-ru-job-listings-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,automation-lab/hh-ru-job-listings-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/S7YIB0GbazgqcvKc6/builds/bAUVkRU7vc2qXr7eb/openapi.json
