# Yandex SERP Scraper - Парсер поисковой выдачи Яндекс (`abotapi/yandex-serp-scraper`) Actor

Scrape Yandex web, image and video results with URLs, snippets and organic rankings for SEO and competitor research. Парсер Яндекс Поиска, картинок и видео: ссылки, сниппеты и позиции для SEO, мониторинга выдачи и анализа конкурентов.

- **URL**: https://apify.com/abotapi/yandex-serp-scraper.md
- **Developed by:** [Abot API](https://apify.com/abotapi) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 1 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 serp results

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.
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

## Yandex Search SERP Scraper

Scrape organic search results from Yandex Search for any list of queries. Every organic result comes back as clean, structured JSON with title, URL, domain, breadcrumb, snippet, and its exact position in the SERP — ready for rank tracking, SEO monitoring, and competitor research. Ads and vertical blocks are skipped, so positions reflect the true organic ranking.

### Why This Scraper

- Organic results only: ads and vertical blocks (news, images, videos) are filtered out, so every emitted position is a real organic rank.
- Position tracking built in: each result carries its query, 1-based SERP page, and a continuous 1-based position across the whole walk for that query.
- Two domains from one input: `yandex.com` and `yandex.ru`, with access checks handled automatically — no extra setup for either.
- Web, image, and video search: pick the vertical with `searchType`. Image and video rows add media fields (thumbnail, original image URL, duration, views, publish date, embed URL).
- Per-query pagination (page 1 is `&p=0`), stopping early when a page stops yielding new results.
- A hard total cap (`maxItems`) keeps run size and cost predictable.

### Data You Get

> Sample shape, values are illustrative placeholders, not from a live SERP.

| Field | Example |
| --- | --- |
| query | best coffee grinder |
| page | 1 |
| position | 1 |
| title | Sample Store — Burr Coffee Grinders |
| url | https://www.example-store.com/burr-grinders |
| domain | example-store.com |
| breadcrumb | example-store.com › burr-grinders |
| snippet | Compare burr coffee grinders in stock at Sample Store... |

### How to Use

Search yandex.com:

```json
{
  "queries": ["best coffee grinder", "espresso machine review"],
  "maxPages": 3,
  "maxItems": 100
}
```

Search yandex.ru with a region and pagination:

```json
{
  "queries": ["купить кофемолку"],
  "domain": "yandex.ru",
  "lr": 213,
  "maxPages": 2
}
```

### Input Parameters

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| queries | array | | One or more search queries. Each query gets its own SERP walk. |
| searchType | string | web | `web`, `images`, or `videos`. Image and video rows carry extra media fields. Image search always runs on yandex.com and ignores `domain` and `lr`. |
| maxPages | integer | 1 | SERP pages per query, 1–10. The walk stops early if a page returns no new organic results. |
| domain | string | yandex.com | `yandex.com` or `yandex.ru`. |
| lr | integer | none | Yandex region code for the SERP locale, e.g. 213 (Moscow), 2 (Saint Petersburg), 21265 (San Francisco). Only meaningful on `yandex.ru`. Leave empty for auto-detection. |
| maxItems | integer | none | Hard cap on results emitted across the whole run. Leave empty for no cap (very large runs need a long run timeout). |
| proxySettings | object | Apify residential | Proxy configuration. Residential is the default (RU for `yandex.ru`, US for `yandex.com`) and returns the fullest result pages. |

### Access Checks

Yandex occasionally asks the search session to confirm it is operated by a person. The actor resolves these checks automatically and continues the walk. Resolving a check may involve a small per-check service fee (typically under half a cent, resolved within about a minute), and total check spending is capped: if a run would spend more than **$1 per 1000 dataset items** on checks, it stops with a clear message instead of spending more. The run summary always reports how many checks were resolved and the total spent.

### Output Example

> Sample shape, values are illustrative placeholders, not from a live SERP.

```json
{
  "query": "best coffee grinder",
  "page": 1,
  "position": 1,
  "title": "Sample Store — Burr Coffee Grinders",
  "url": "https://www.example-store.com/burr-grinders",
  "domain": "example-store.com",
  "breadcrumb": "example-store.com › burr-grinders",
  "snippet": "Compare burr coffee grinders in stock at Sample Store..."
}
```

### Plan Requirement

The default run works on any plan that includes Apify Proxy. An RU-region proxy selection is recommended for `yandex.ru` runs.

### Resume & Recurring Updates

- **Resume** (`resumeFromRunId`): paste a previous run ID or dataset ID to continue an interrupted pull — results already collected there are skipped, never re-emitted. Cannot be combined with incremental mode.
- **Incremental mode** (`incrementalMode`): for daily or recurring monitoring of the same search. The first run returns everything as NEW; later runs return only NEW and UPDATED rows, with `changeType`, `changedFields`, `firstSeenAt` and `lastSeenAt` on every row. UNCHANGED rows are suppressed unless `emitUnchanged` is on; vanished rows are tombstoned EXPIRED only after a fully completed scan and only when `emitExpired` is on. State is keyed per search (or your optional `stateKey`) in a named key-value store.
- Position and page are not content: a pure rank move classifies UNCHANGED (the row still carries the live position), so only title/snippet/URL changes mark a row UPDATED.

# Actor input Schema

## `searchType` (type: `string`):

Which Yandex vertical to search: web results, images, or videos. Images and videos return media fields like thumbnails and durations.

## `queries` (type: `array`):

One SERP walk per query. Each query gets a fresh proxy session.

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

SERP pages per query (page 1 is \&p=0). Stops early if a page returns no new organic results.

## `lr` (type: `integer`):

Yandex region code for the SERP locale, e.g. 213 (Moscow), 2 (Saint Petersburg), 21265 (San Francisco). Only meaningful on yandex.ru. Leave empty for auto-detection.

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

Hard cap on emitted dataset items across the whole run. Leave empty for no cap (very large runs need a long run timeout — each query holds one browser session).

## `proxySettings` (type: `object`):

Proxy configuration. Residential is the default (RU for yandex.ru, US for yandex.com) — it returns the fullest result pages.

## `resumeFromRunId` (type: `string`):

Paste a previous run ID or dataset ID to continue an interrupted pull without returning results already collected there. Cannot be combined with incremental mode.

## `incrementalMode` (type: `boolean`):

Turn this on for daily or recurring monitoring. The first run returns every matching result as NEW. Later runs normally return only NEW and UPDATED results. Position changes alone are not content changes, so a pure rank move is not an UPDATED row. Cannot be combined with resumeFromRunId.

## `stateKey` (type: `string`):

Optional. Name this monitoring campaign to keep its state stable, or deliberately share state across differently configured runs. Leave empty to derive a key automatically from the search settings.

## `emitUnchanged` (type: `boolean`):

Off by default. Turn on to also return records that have not changed since the last run, marked UNCHANGED. This returns, and bills, extra rows you already have.

## `emitExpired` (type: `boolean`):

Off by default. Turn on to also return records that were present in a previous run but are no longer found, marked EXPIRED. Only produced once a run has fully scanned the tracked search.

## Actor input object example

```json
{
  "searchType": "web",
  "queries": [
    "best coffee grinder"
  ],
  "maxPages": 1,
  "proxySettings": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  },
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false
}
```

# Actor output Schema

## `organicResults` (type: `string`):

No description

## `runSummary` (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 = {
    "searchType": "web",
    "queries": [
        "best coffee grinder"
    ],
    "proxySettings": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    },
    "incrementalMode": false,
    "emitUnchanged": false,
    "emitExpired": false
};

// Run the Actor and wait for it to finish
const run = await client.actor("abotapi/yandex-serp-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 = {
    "searchType": "web",
    "queries": ["best coffee grinder"],
    "proxySettings": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
    "incrementalMode": False,
    "emitUnchanged": False,
    "emitExpired": False,
}

# Run the Actor and wait for it to finish
run = client.actor("abotapi/yandex-serp-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 '{
  "searchType": "web",
  "queries": [
    "best coffee grinder"
  ],
  "proxySettings": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  },
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false
}' |
apify call abotapi/yandex-serp-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,abotapi/yandex-serp-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/aTD9PlMHIXn9O9boV/builds/VRGe6U8oRfOtH8KVk/openapi.json
