# Yandex Search Engine Scraper (SERP API) (`dami_studio/yandex-search-engine-scraper`) Actor

Scrape Yandex web search: rank, title, URL, displayed domain, snippet, sitelinks and result type (organic, ad, featured). Target any Yandex region by lr code - Moscow, Istanbul, Almaty, Minsk. No API key. At $3.40 per 1,000 results it is the lowest-priced established Yandex SERP scraper on Apify.

- **URL**: https://apify.com/dami\_studio/yandex-search-engine-scraper.md
- **Developed by:** [Dami's Studio](https://apify.com/dami_studio) (community)
- **Categories:** SEO tools, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.40 / 1,000 search result returneds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## Yandex Search Engine Scraper (SERP API)

Scrapes Yandex web search result pages and returns one row per result: rank, title, URL, the domain
Yandex displays, the snippet, sitelinks, and whether the slot is an organic listing, a Yandex Direct
ad, or a featured block.

Yandex is the default search engine for a lot of Russia, Turkey, Kazakhstan, Belarus and Uzbekistan.
If you track rankings, watch competitors or build datasets for those markets, Google-only data has a
hole in it. This fills the hole.

No Yandex account, no API key, no captcha solving service.

**$3.40 per 1,000 results.** That is below every tier of the nearest competing Yandex SERP scraper
on Apify, which charges $7.00 per 1,000 on its entry tier and $4.00 on its highest volume tier.

### What you get

One row per result:

| Field | Example |
|---|---|
| `position` | `2` — rank in the result list as Yandex ordered it, ads and widgets included |
| `organicPosition` | `1` — rank counting organic listings only (`null` for ads and featured blocks) |
| `type` | `organic`, `ad` or `featured` |
| `featureType` | `video`, `images`, `related`, `neuro-answer`, … (`null` for organic and ads) |
| `title` | `Amazon.com: Discover Ergonomic Desk Chairs Engineered...` |
| `url` | `https://www.amazon.com/ergonomic-chair/s?k=ergonomic+chair` |
| `displayedUrl` | `amazon.com › ergonomic-chair` — the breadcrumb Yandex prints |
| `displayedDomain` | `amazon.com` |
| `brandName` | `Amazon` — the site name Yandex shows above the title |
| `snippet` | `Discover ergonomic desk chairs engineered with adjustable components…` |
| `sitelinks` | `[{ "title": "Rental cars", "url": "https://…" }]` |
| `isAd` | `false` |
| `page`, `positionOnPage` | which SERP page it came from |
| `region`, `regionRequested` | the Yandex `lr` region code that produced this ranking |
| `domain`, `searchUrl`, `searchQuery`, `scrapedAt` | provenance |

### Input

```json
{
  "queries": ["купить пластиковые окна", "ergonomic office chair"],
  "region": 213,
  "maxItemsPerQuery": 50,
  "maxItems": 200
}
```

- `queries` — keywords, any language. Cyrillic, Turkish and Kazakh all work.
- `startUrls` — paste Yandex search URLs instead, e.g. `https://yandex.com/search/?text=pizza&lr=213`.
  The query, region and start page are read out of the URL.
- `region` — the Yandex `lr` code that decides which regional ranking you get. All of these are
  tested and confirmed working: 225 Russia, 213 Moscow, 2 Saint Petersburg, 65 Novosibirsk,
  54 Yekaterinburg, 43 Kazan, 47 Nizhny Novgorod, 39 Rostov-on-Don, 35 Krasnodar, 51 Samara,
  187 Ukraine, 143 Kyiv, 149 Belarus, 157 Minsk, 159 Kazakhstan, 163 Astana, 10393 Almaty,
  983 Turkey, 11508 Istanbul, 171 Uzbekistan, 10335 Tashkent, 168 Azerbaijan, 167 Armenia,
  10262 Yerevan, 169 Georgia, 84 USA, 102 Germany, 95 United Kingdom.
- `maxItemsPerQuery` / `maxItems` — the actor pages through Yandex on its own until it hits either.
- `includeAds`, `includeFeatured` — set either to `false` if you only want the organic listings.

### Region really does change the ranking

Same query, different `lr`, first organic result:

| `lr` | Region | Top result for `pizza` |
|---|---|---|
| 213 | Moscow | `youtube.com` |
| 983 | Turkey | `dominos.com.tr` |
| 11508 | Istanbul | `nefisyemektarifleri.com` |
| 10393 | Almaty | `nefisyemektarifleri.com` |

### Pagination

Yandex re-ranks between pages, so consecutive pages overlap. The actor deduplicates as it goes,
which means `maxItemsPerQuery: 100` gives you 100 distinct results, not 100 rows with 20 repeats in
them. A 20-page run on one keyword returned 171 unique results from 180 raw cards.

### Speed, from real runs

| Run | Results | Time |
|---|---|---|
| One keyword, first page | 5 | 4.1 s |
| One keyword with ads, Istanbul (`lr=11508`) | 15 | 7.8 s |
| Three keywords, English + Russian + Turkish, 10 pages deep | 250 | 58.9 s |

Roughly 4 results per second, and it holds at depth.

### What this does not do

- **No total result count.** Modern Yandex SERPs no longer print "about 12,000,000 results" into the
  HTML, so there is nothing honest to return. The field does not exist rather than being guessed.
- **Ad destination URLs are Yandex click trackers.** A Yandex Direct ad links through
  `yabs.yandex.ru/count/…`. The advertiser's real domain is in `displayedDomain`, taken from the
  breadcrumb Yandex prints. We do not follow the tracker — that would cost you a request per ad and
  register a fake ad click.
- **The AI answer ("Yazeka") is a teaser only.** Yandex loads that text with JavaScript after the
  page renders. You get the fact that the block occupied a rank slot, not its body text.
- **Not images, video, maps or news search.** This is `/search/`, the web index. Yandex Maps places
  and reviews are separate actors.
- **Not a live rank checker down to position 500.** Yandex gets progressively less willing to serve
  very deep pages. Fifteen pages per query is the default ceiling and around 20 is realistic.

### Reliability

Yandex defends its SERPs with SmartCaptcha, and it is genuinely aggressive. Two things make this
actor work where a plain HTTP client gets a wall of "are you a robot":

1. A Firefox TLS fingerprint. Measured on the same addresses: a Chrome fingerprint got 0 pages out
   of 8; Firefox got 5 out of 8. Plain Node.js gets none.
2. A different exit address on every single request. Yandex challenges an address after a handful of
   searches and then keeps challenging it, so reusing one address is what kills a run — not the
   address being "bad". With a new proxy session per request, a 20-page run needed exactly 20
   requests, no retries.

When Yandex does challenge a page anyway, the actor retries on new addresses, and if it still cannot
get through it writes an uncharged diagnostic row saying so. You are never billed for a captcha page.

### FAQ

**Is there a Yandex search API I could use instead?**
Yandex's XML search interface (`xmlsearch.yandex.ru`) answers HTTP 403 without a paid Yandex Cloud
key, and the paid Search API is billed per query with a signup that expects a Russian legal entity.
This actor needs neither.

**Do I need my own proxies?**
No. Leave the proxy input alone and it uses Apify's datacenter proxy, which is what it is tuned for.
You can supply your own proxy URLs if you want a specific country.

**Does it work for Russian-language queries?**
Yes, and that is the main use case. Pass Cyrillic straight through in `queries`.

**Can I get results as they look in Moscow while running from anywhere?**
Yes. Set `region: 213`. The region code, not the proxy country, is what selects the ranking, and the
row records which region Yandex actually served in `region`.

**How many results per keyword can I get?**
About 10-20 on the first page and roughly 9 per page after that. 100-170 unique results per keyword
is comfortable. Set `maxItemsPerQuery` to whatever you need and the actor pages until it gets there.

**What counts as a charged result?**
Every row that carries a real result — organic, ad or featured. Diagnostic rows, the empty-input
sample row, and captcha pages are never charged. A 250-result run billed exactly 250 result events
and one actor-start event.

**Is it really cheaper than the alternatives?**
Yes, and the numbers are checkable on the store. The two established Yandex search scrapers on
Apify charge $0.007 per result (dropping to $0.004 at their highest volume tier) and $0.10 per page
processed. This one is $0.0034 per result, flat, with no tiers to qualify for.

**Can I track a competitor's rank over time?**
Yes. Run it on a schedule with `queries` set to your keyword list and read `organicPosition` for the
domain you care about. `displayedDomain` is there to make that a one-line filter.

**Why does the run use yandex.com.tr by default?**
Every Yandex front-end serves the same index and obeys the same `lr` region code, but they do not
defend themselves equally. Over 20 consecutive paginated requests, `yandex.com.tr` and `yandex.uz`
answered every one on the first attempt while `yandex.com` and `yandex.ru` served page 1 and then
challenged every deeper page. You can override it with the `domain` input.

# Actor input Schema

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

Keywords to search on Yandex. One row per result. Works in any language — Russian, Turkish, English, Kazakh.

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

Paste full Yandex search URLs instead of keywords, for example https://yandex.com/search/?text=pizza\&lr=213. The query, region and start page are read from the URL.

## `region` (type: `integer`):

Yandex region id that decides which regional ranking you get. Verified codes: 225 Russia, 213 Moscow, 2 Saint Petersburg, 65 Novosibirsk, 54 Yekaterinburg, 43 Kazan, 47 Nizhny Novgorod, 39 Rostov-on-Don, 35 Krasnodar, 51 Samara, 187 Ukraine, 143 Kyiv, 149 Belarus, 157 Minsk, 159 Kazakhstan, 163 Astana, 10393 Almaty, 983 Turkey, 11508 Istanbul, 171 Uzbekistan, 10335 Tashkent, 168 Azerbaijan, 167 Armenia, 10262 Yerevan, 169 Georgia, 84 USA, 102 Germany, 95 United Kingdom. Leave empty to let Yandex choose.

## `maxItemsPerQuery` (type: `integer`):

How many results to return for each query. Yandex serves about 10-20 per page and this actor pages through automatically.

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

Hard cap across all queries. Every returned result is one charged event, so this is also your budget cap.

## `includeAds` (type: `boolean`):

Include Yandex Direct ad slots (type "ad"). Their URL is Yandex's click tracker; the advertiser's own domain is in displayedDomain.

## `includeFeatured` (type: `boolean`):

Include non-organic SERP blocks such as video carousels, image strips, related searches and the AI answer teaser (type "featured"). They occupy a rank slot, which matters for rank tracking. Turn off if you only want the ten blue links.

## `maxPagesPerQuery` (type: `integer`):

Safety stop on how deep to page for one query.

## `domain` (type: `string`):

Which Yandex front-end to query. All of them serve the same index and honour the same region code, but yandex.com.tr and yandex.uz answer automated requests far more reliably — the default is there for a reason. Change it only if you specifically need another host.

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

Leave as is. Apify datacenter proxy with a new session per request is what keeps Yandex answering; residential proxy is not needed and costs far more.

## Actor input object example

```json
{
  "queries": [
    "купить пластиковые окна",
    "ergonomic office chair"
  ],
  "startUrls": [
    "https://yandex.ru/search/?text=%D0%BF%D0%B8%D1%86%D1%86%D0%B0&lr=213"
  ],
  "region": 213,
  "maxItemsPerQuery": 20,
  "maxItems": 100,
  "includeAds": true,
  "includeFeatured": true,
  "maxPagesPerQuery": 15,
  "domain": "yandex.com.tr",
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# 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 = {
    "queries": [
        "ergonomic office chair"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("dami_studio/yandex-search-engine-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 = { "queries": ["ergonomic office chair"] }

# Run the Actor and wait for it to finish
run = client.actor("dami_studio/yandex-search-engine-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 '{
  "queries": [
    "ergonomic office chair"
  ]
}' |
apify call dami_studio/yandex-search-engine-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,dami_studio/yandex-search-engine-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/ejJSDtNVjRXDlt3vK/builds/DAZT81lj4MLUcY0un/openapi.json
