# Robota.ua Scraper (`solidcode/robota-ua-scraper`) Actor

\[💰 $1.8 / 1K] Extract job listings from robota.ua, Ukraine's largest job board. Search by keyword and location, filter by experience, work schedule, salary, and remote work, and get structured jobs with titles, companies, salaries, descriptions, contacts, and apply links.

- **URL**: https://apify.com/solidcode/robota-ua-scraper.md
- **Developed by:** [SolidCode](https://apify.com/solidcode) (community)
- **Categories:** Jobs, Automation, Developer tools
- **Stats:** 8 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.80 / 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/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

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

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Robota.ua Scraper

Pull job listings from robota.ua — Ukraine's largest job board — at scale: titles, employers, salary ranges in UAH, work schedules, full descriptions, required skills, recruiter contacts, and map coordinates for every vacancy. Search by keyword and city, narrow with filters, or paste robota.ua URLs directly and let the data come to you as clean, structured rows. Built for Ukrainian recruiters, HR-tech platforms, and labor-market analysts who need fresh robota.ua vacancy data without copy-pasting listings one page at a time.

### Why This Scraper?

- **40 structured fields per vacancy** — every job is flattened into one tidy row: title, company, salary, schedule, robota.ua's own job level (Worker through Top manager), city in both Ukrainian and English, geo, description, skills, contacts, apply link, and posting time.
- **7 work schedules you can actually filter on** — full-time (90,000+ live listings), on-site (86,000+), shift work, part-time, hybrid, remote and project-based, each selectable by name with no numeric IDs to look up. Internship and seasonal/temporary are still offered because robota.ua still lists them, but no employer currently posts under either, so choosing one tells you that in the run instead of finishing empty with no explanation.
- **Salary ranges in UAH with min/max** — parsed `salaryMin` and `salaryMax` in Ukrainian hryvnia, plus a `salaryHidden` flag so you can separate disclosed pay from "salary on request".
- **Map coordinates on every located job** — latitude and longitude, plus city, district, and nearest metro station, ready to plot or geo-filter.
- **Full descriptions in text and HTML** — both a clean plain-text version and the original formatted HTML, alongside listed skills and listing badges.
- **Every city name in English as well as Ukrainian** — `cityEn` gives you "Kyiv", "Lviv", "Dnipro" resolved from robota.ua's own 3,751-city directory, so a spreadsheet filter or a join against your CRM works without transliterating Cyrillic by hand.
- **Sortable UTC posting times on every row** — robota.ua shows ages as "2 дні тому"; every row here carries a real `postedDate` timestamp in UTC (plus the original wording in `postedDateRaw`), so freshness sorting and date filters just work.
- **Recruiter contacts extracted for you** — contact person (about 85% of listings) and phone (about 60-65%), plus any emails, phones, and social profiles (LinkedIn, Telegram, Viber, Facebook) published in the listing.
- **Search the way you already work** — keywords in any language ("developer", "менеджер", "бухгалтер") with a city filter like Kyiv, Lviv, or Odesa, or paste a tuned robota.ua search or vacancy link and the filters baked into it are honored exactly.
- **Deduplicated by vacancy ID** — each job comes back once even when it appears across several pages or overlapping keyword searches, so you never pay twice for the same listing.

### Use Cases

**Recruitment & Sourcing**

- Build candidate-facing job feeds for a niche, city, or industry
- Pull recruiter contact details to reach hiring managers directly
- Track which employers are actively hiring for a given role
- Mirror a dialed-in robota.ua search and refresh it on a schedule

**Labor-Market Research**

- Map vacancy volume by city, district, and work schedule
- Measure remote vs. on-site vs. hybrid demand across Ukraine
- Track entry-level hiring with the no-experience filter
- Spot which skills employers ask for most in a profession

**Salary Benchmarking**

- Compile UAH salary ranges by role and region
- Compare disclosed vs. undisclosed pay across employers
- Benchmark min/max offers for a job title over time
- Feed compensation models with structured salary data

**Job-Board Aggregation**

- Power a vertical job aggregator or meta-search with fresh listings
- Sync new vacancies into your own database or ATS
- Keep a curated board updated with deduplicated postings

**Lead Generation for HR-Tech**

- Identify companies posting many roles as outreach targets
- Capture verified-employer signals for account scoring
- Build prospect lists of actively hiring organizations

### Getting Started

#### Search by Keyword

The simplest run — one keyword across all of Ukraine:

```json
{
    "keywords": ["developer"],
    "maxResults": 100
}
```

#### Keyword + City with Filters

Remote developer roles in Kyiv paying at least 50,000 UAH:

```json
{
    "keywords": ["developer", "data analyst"],
    "location": "Kyiv",
    "employmentType": ["remote"],
    "minSalary": 50000,
    "maxResults": 200
}
```

#### Paste robota.ua URLs

Already have a search tuned on the website? Paste the link — search, city and vacancy URLs all work:

```json
{
    "startUrls": [
        "https://robota.ua/zapros/developer/ukraine",
        "https://robota.ua/kyiv",
        "https://robota.ua/company123456/vacancy7654321"
    ],
    "includeDescription": true,
    "maxResults": 300
}
```

### Input Reference

#### Search

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `keywords` | array | `["developer"]` | Job titles, roles, or keywords to search for (e.g. "developer", "менеджер", "accountant"). Each keyword runs its own search. Up to 50 per run. |
| `location` | string | `""` | City or region to search in (e.g. "Kyiv", "Lviv", "Odesa"). Leave empty to search all of Ukraine. |
| `startUrls` | array | `[]` | Paste full robota.ua search-result, city (`https://robota.ua/kyiv`) or individual vacancy URLs to collect them directly. Any filters baked into the URL are respected, and a vacancy you name by URL is always collected first. Up to 50 per run. |

#### Filters & Limits

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `noExperienceOnly` | boolean | `false` | When on, only show jobs open to candidates with no prior experience (entry-level roles). |
| `employmentType` | array | `[]` | Only return jobs with these work schedules: Full-time, Part-time, Remote, Project-based, Shift work, Hybrid, On-site. Leave empty for all. Internship and Seasonal / Temporary are also offered, but no employer currently posts under either — pick one and the run says so rather than finishing empty. |
| `remoteOnly` | boolean | `false` | Only show jobs that can be done remotely. |
| `minSalary` | integer | `null` | Only show jobs offering at least this monthly salary in UAH. Jobs without a disclosed salary are excluded when this is set. |
| `maxResults` | integer | `100` | Maximum listings to collect per keyword (or per start URL). Set to 0 to collect all available results. |
| `onlyUniqueJobs` | boolean | `true` | Return each job once even if it appears across several pages or searches. Deduplicates by vacancy ID. |

#### Output

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `includeDescription` | boolean | `true` | Include the full job description (text and HTML), plus contact info, skills, industry, geo, and company verification from each vacancy page. Turn off for lighter, faster records — note the description-only fields (including `industry`, `companyUrl`, `companyVerified`, contacts and geo) are empty when off. |

### Output

Each job is one flat row. Example (trimmed for readability):

```json
{
    "jobId": "7654321",
    "title": "Python Developer",
    "company": "Tech Solutions LLC",
    "companyId": "123456",
    "companyUrl": "https://robota.ua/company123456",
    "companyVerified": true,
    "industry": "IT, computers, internet",
    "location": "Київ",
    "city": "Київ",
    "cityEn": "Kyiv",
    "district": "Печерський",
    "metro": "Печерська",
    "address": "вул. Хрещатик, 1",
    "latitude": 50.4501,
    "longitude": 30.5234,
    "salary": "60000-90000 UAH",
    "salaryMin": 60000,
    "salaryMax": 90000,
    "salaryCurrency": "UAH",
    "salaryHidden": false,
    "employmentType": "Full-time, Remote",
    "experienceLevel": "Senior specialist",
    "isRemote": true,
    "isHot": true,
    "description": "We are looking for an experienced Python developer...",
    "descriptionHtml": "<p>We are looking for an experienced Python developer...</p>",
    "skills": ["Python", "Django", "PostgreSQL"],
    "badges": ["Verified employer"],
    "contactPerson": "Olena Koval",
    "contactPhone": "+380671234567",
    "contactEmail": "",
    "extractedEmails": [],
    "extractedPhones": ["+380671234567", "+380951234567"],
    "socialProfiles": { "telegram": "https://t.me/techsolutions_hr" },
    "applyUrl": "https://robota.ua/company123456/vacancy7654321",
    "jobUrl": "https://robota.ua/company123456/vacancy7654321",
    "postedDate": "2026-06-10T09:30:00Z",
    "postedDateRaw": "3 дні тому",
    "searchQuery": "developer",
    "scrapedAt": "2026-06-13T12:00:00Z"
}
```

#### Core Fields

| Field | Type | Description |
|-------|------|-------------|
| `jobId` | string | robota.ua vacancy ID (the deduplication key). |
| `title` | string | Vacancy title. |
| `employmentType` | string | Work schedule(s) in English, e.g. "Full-time, Remote". Identical whether or not full descriptions are fetched. |
| `experienceLevel` | string | robota.ua's job level for the role: Worker, Entry-level specialist, Specialist, Senior specialist, Head of department or Top manager. Filled on effectively every vacancy — only the ~1.6% of listings where the employer left the level unset come back empty. Identical whether or not full descriptions are fetched. |
| `isRemote` | boolean | True when the job can be done remotely. |
| `isHot` | boolean | True for "hot" / featured listings. |
| `postedDate` | string | When the job was published, as a UTC ISO-8601 timestamp. With full descriptions on, this is robota.ua's exact publication time. With them off, robota.ua reveals only the listing's age ("3 days ago"), so the value is the most recent moment that age allows: exact to the minute for jobs posted in the last hour, to the hour for jobs posted today, and within a day for anything older. |
| `postedDateRaw` | string | The posting age exactly as robota.ua worded it, e.g. "3 дні тому" or the site's own timestamp. |
| `searchQuery` | string | The keyword or URL that produced this row. |
| `scrapedAt` | string | ISO timestamp of when the row was collected. |

#### Company

| Field | Type | Description |
|-------|------|-------------|
| `company` | string | Employer / company name. |
| `companyId` | string | Company ID on robota.ua. |
| `companyUrl` | string | Company profile URL. |
| `companyVerified` | boolean | Whether the employer is verified. |
| `industry` | string | Employer's industry / job category (e.g. "IT, computers, internet"). Populated when full descriptions are fetched. |

#### Salary

| Field | Type | Description |
|-------|------|-------------|
| `salary` | string | Human-readable salary label (e.g. "60000-90000 UAH"). |
| `salaryMin` | number | Parsed minimum salary. |
| `salaryMax` | number | Parsed maximum salary. |
| `salaryCurrency` | string | Currency (UAH when an amount is disclosed). |
| `salaryHidden` | boolean | True when the salary is not disclosed. |

#### Location & Geo

| Field | Type | Description |
|-------|------|-------------|
| `location` | string | Free-text location label for the job, as robota.ua shows it (Ukrainian). |
| `city` | string | City name in Ukrainian, e.g. "Київ". |
| `cityEn` | string | The same city in English, e.g. "Kyiv" — resolved from robota.ua's city directory. Filter and group on this one. |
| `district` | string | District (raion) when present. |
| `metro` | string | Nearest metro station when present. |
| `address` | string | Street address when present. |
| `latitude` | number | Latitude when present. |
| `longitude` | number | Longitude when present. |

#### Description & Skills

| Field | Type | Description |
|-------|------|-------------|
| `description` | string | Full job description in plain text. |
| `descriptionHtml` | string | Full job description in original HTML. |
| `skills` | array | Listed skills and tags. |
| `badges` | array | Listing badges (e.g. verified employer). |

#### Contacts & Links

| Field | Type | Description |
|-------|------|-------------|
| `contactPerson` | string | Recruiter / contact name. Published on roughly 85% of listings. |
| `contactPhone` | string | Primary contact phone, in international `+380...` form. Published on roughly 60% of listings. |
| `contactEmail` | string | Contact email. Ukrainian employers rarely publish one on robota.ua — expect it on only 1-2% of listings, so plan outreach around the contact name and phone instead. |
| `extractedEmails` | array | Any email addresses found in the listing text (same 1-2% coverage as `contactEmail`). |
| `extractedPhones` | array | Every phone number on the listing, in the same `+380...` form: the published contact number first, then any extra numbers the employer listed or wrote into the ad text. Always includes `contactPhone` when there is one. |
| `socialProfiles` | object | Detected social links (LinkedIn, Telegram, Viber, Facebook, etc.). |
| `applyUrl` | string | Apply / "respond" URL. |
| `jobUrl` | string | Canonical vacancy URL. |

### Tips for Best Results

- **Type city names however you know them.** "Kyiv" and "Київ" both resolve, as do "Lviv"/"Львів", "Odesa"/"Одеса" and older spellings like "Kiev" or "Kharkov". If nothing matches, the run stops and suggests the closest real cities instead of quietly returning jobs from all over Ukraine.
- **Sort by `postedDate` to get the freshest listings first.** It is a UTC timestamp on every row, and `postedDateRaw` keeps robota.ua's own wording next to it. Turn on full descriptions when you need the exact publication minute rather than the age the listing page shows.
- **Mirror a tuned website search with `startUrls`.** Set your filters on robota.ua, copy the address-bar URL, and paste it in — schedule and experience filters baked into the link are honored exactly. A plain city page such as `https://robota.ua/lviv` works the same way and covers every job in that city.
- **Setting `minSalary` hides "salary on request" jobs.** A salary floor excludes every listing that doesn't disclose pay, so leave it empty if you want undisclosed-salary postings too.
- **Turn off `includeDescription` for fast, lightweight runs.** When you only need titles, companies, locations, salaries, and apply links, skipping the full description makes each run faster and leaner.
- **Use multiple keywords to cover synonyms.** "developer", "розробник", and "програміст" surface different listings; add them all and `onlyUniqueJobs` keeps the merged result clean.
- **Set `maxResults` to 0 to sweep an entire search.** Useful for full market snapshots; the run stops once a search is exhausted.
- **Filter for entry-level talent with `noExperienceOnly`, then split the results by `experienceLevel`.** robota.ua's search offers one no-experience switch rather than graduated bands, but every row carries the site's own job level — Worker through Top manager — so you can separate junior from senior after the run without reading a single description.

### Pricing

**From $1.80 per 1,000 results** — flat, pay-only-for-what-you-collect pricing that undercuts comparable robota.ua data tools. Bronze, Silver, and Gold subscribers pay progressively less; the table below shows total cost at each discount tier.

| Results | No discount | Bronze | Silver | Gold |
|---------|-------------|--------|--------|------|
| 100 | $0.22 | $0.20 | $0.19 | $0.18 |
| 1,000 | $2.15 | $2.00 | $1.90 | $1.80 |
| 10,000 | $21.50 | $20.00 | $19.00 | $18.00 |
| 100,000 | $215.00 | $200.00 | $190.00 | $180.00 |

A "result" is one job row pushed to your dataset. No compute or time-based charges — you pay per result, plus a small fixed per-run start fee.

### Integrations

Export data in JSON, CSV, Excel, XML, or RSS. Connect to 1,500+ apps via:

- **Zapier** / **Make** / **n8n** — Workflow automation
- **Google Sheets** — Direct spreadsheet export
- **Slack** / **Email** — Notifications on new results
- **Webhooks** — Trigger custom workflows on run completion
- **Apify API** — Full programmatic access

### Legal & Ethical Use

This scraper collects publicly available job-listing data for legitimate business purposes such as recruitment, market research, and analytics. You are responsible for using the collected data in compliance with robota.ua's terms of service, applicable data-protection laws (including the GDPR where it applies), and any restrictions on processing personal data such as recruiter contact details. Use the data responsibly, respect individuals' privacy, and do not use it for spam or unsolicited outreach that violates local regulations.

# Actor input Schema

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

Job titles, roles, or keywords to search for, such as 'developer', 'менеджер', or 'accountant'. Each keyword runs its own search. Leave empty to collect the broadest result set for the chosen location and filters. Up to 50 keywords per run.

## `location` (type: `string`):

City or region to search in, such as 'Kyiv', 'Lviv', or 'Odesa'. Matches what you would type into robota.ua's location box. Leave empty to search all of Ukraine.

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

Paste full robota.ua search-result, city or individual vacancy URLs (e.g. https://robota.ua/zapros/developer/ukraine, https://robota.ua/kyiv) to scrape them directly. Useful when you already have a search dialed in on the website — any filters baked into the URL are respected. A city page like https://robota.ua/kyiv collects every job in that city. Ukrainian and Russian links both work, so you can copy the address straight from your browser whichever language you browse robota.ua in. Up to 50 URLs per run.

## `noExperienceOnly` (type: `boolean`):

When on, only show jobs open to candidates with no prior experience (entry-level roles). robota.ua does not offer graduated experience bands, so this is an on/off entry-level filter. Leave off to include jobs at every experience level.

## `employmentType` (type: `array`):

Only return jobs with these work schedules. Leave empty to include all types. Employers stop using some schedules from time to time — if you pick one robota.ua currently has no jobs for, the run says so instead of finishing empty with no explanation.

## `remoteOnly` (type: `boolean`):

Only show jobs that can be done remotely.

## `minSalary` (type: `integer`):

Only show jobs offering at least this monthly salary, in Ukrainian hryvnia (UAH). Leave empty for no salary floor. Jobs that do not disclose a salary are excluded when this is set.

## `maxResults` (type: `integer`):

Maximum number of job listings to collect per keyword (or per start URL). Set to 0 to collect all available results across pagination. Results are fetched in pages: the actor stops once your cap is reached but always keeps the whole final page, so a small cap can return up to a full page more than you asked for. Listings repeated across pages are deduplicated, so the final count can also run slightly below your cap.

## `onlyUniqueJobs` (type: `boolean`):

When on, each job is returned once even if robota.ua lists it across several pages or searches. Deduplicates by the vacancy ID. Turn off to keep every copy.

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

Include the full job description text and HTML for each listing, plus contact info, skills, industry, geo, and company verification from the vacancy page. Leave on for the most complete data. Turn off for lighter, faster records when you only need titles, companies, locations, salaries, and apply links from the results list. Note: the description-only fields (description, descriptionHtml, skills, contacts, industry, district/metro/address, latitude/longitude, companyUrl and companyVerified) are empty when this is off — companyVerified reads false then because the verification flag isn't fetched, not because the employer is unverified.

## Actor input object example

```json
{
  "keywords": [
    "developer"
  ],
  "startUrls": [],
  "noExperienceOnly": false,
  "employmentType": [],
  "remoteOnly": false,
  "maxResults": 100,
  "onlyUniqueJobs": true,
  "includeDescription": true
}
```

# Actor output Schema

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

Table of scraped job listings with key fields like title, company, city in English, salary, work schedule, and posting time.

## `detail` (type: `string`):

Complete job data including full descriptions, parsed salary fields, company block, geo, skills, contacts, both city spellings, and metadata.

# 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 = {
    "keywords": [
        "developer"
    ],
    "location": "",
    "startUrls": [],
    "noExperienceOnly": false,
    "employmentType": [],
    "remoteOnly": false,
    "maxResults": 100,
    "onlyUniqueJobs": true,
    "includeDescription": true
};

// Run the Actor and wait for it to finish
const run = await client.actor("solidcode/robota-ua-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 = {
    "keywords": ["developer"],
    "location": "",
    "startUrls": [],
    "noExperienceOnly": False,
    "employmentType": [],
    "remoteOnly": False,
    "maxResults": 100,
    "onlyUniqueJobs": True,
    "includeDescription": True,
}

# Run the Actor and wait for it to finish
run = client.actor("solidcode/robota-ua-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 '{
  "keywords": [
    "developer"
  ],
  "location": "",
  "startUrls": [],
  "noExperienceOnly": false,
  "employmentType": [],
  "remoteOnly": false,
  "maxResults": 100,
  "onlyUniqueJobs": true,
  "includeDescription": true
}' |
apify call solidcode/robota-ua-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,solidcode/robota-ua-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/1Ow5zAABxBOFvggFg/builds/VdjhRV1OA78T2CdAp/openapi.json
