# Google Organic Rank Tracker (`automation-lab/seo-rank-tracker`) Actor

Track domain positions in Google organic search by keyword, country, language, and desktop or mobile device.

- **URL**: https://apify.com/automation-lab/seo-rank-tracker.md
- **Developed by:** [Stas Persiianenko](https://apify.com/automation-lab) (community)
- **Categories:** SEO tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

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

## Google Organic Rank Tracker

Track where your domains appear in **Google organic** search results across keywords, countries, languages, and desktop or mobile layouts.

The Actor writes one dated rank snapshot for every domain × keyword × locale × device combination. Schedule it daily or weekly to build position history in Apify datasets, Google Sheets, a database, or your SEO dashboard.

It reports both found and not-found checks, so missing positions do not disappear from monitoring pipelines.

### What does Google Organic Rank Tracker do?

For each requested Google query, the Actor:

1. requests localized search results;
2. identifies external organic heading links;
3. excludes Google navigation and non-organic modules;
4. compares each result hostname with your tracked domains;
5. records the first matching organic position, URL, and title;
6. writes a timestamped row even when no match is found.

Ads, Maps packs, shopping modules, and AI modules are not counted as organic ranks.

### Who is it for?

- **SEO specialists** checking important keyword positions without copying SERPs by hand;
- **SEO agencies** monitoring client and competitor domains by market;
- **Content teams** checking whether a new or updated page starts ranking;
- **Growth teams** comparing desktop and mobile visibility;
- **Data teams** feeding repeatable rank snapshots into BI tools or warehouses.

### Why use this rank tracker?

- Check several domains against one SERP without repeating the Google request.
- Combine multiple keywords, countries, languages, and devices in one run.
- Keep explicit `found: false` records for stable time-series analysis.
- Match subdomains to their parent tracked domain.
- Receive the matched ranking URL and title, not only a position number.
- Use typed default-dataset rows with Apify schedules, webhooks, API clients, and integrations.

### What Google organic data is extracted?

| Field | Meaning |
| --- | --- |
| `checkedAt` | UTC timestamp shared by the run's snapshots |
| `keyword` | Google query checked |
| `trackedDomain` | Normalized input domain |
| `countryCode` | Requested Google country market |
| `languageCode` | Requested result language |
| `device` | `desktop` or `mobile` layout |
| `position` | One-based organic position, or `null` |
| `rankingUrl` | Matched result URL, or `null` |
| `title` | Matched organic result title, or `null` |
| `found` | Whether the domain was found |
| `organicResultsScanned` | Recognized organic links inspected |
| `searchUrl` | Localized Google query URL |

### How to track Google organic rankings

1. Open the Actor input page.
2. Enter one or more domains, such as `apify.com`.
3. Add the keywords you want to monitor.
4. Choose country/language locale pairs.
5. Select desktop, mobile, or both devices.
6. Start with the default 20 requested results, or raise it to 100 for broader rank coverage.
7. Start the Actor.
8. Open the **Organic rank history** dataset view.
9. Add an Apify schedule if you want recurring snapshots.

### Input parameters

#### `domains`

Required list of domains or website URLs. Inputs are normalized to hostnames. A result on `blog.example.com` matches tracked domain `example.com`.

Maximum: 20 domains.

#### `keywords`

Required list of Google queries.

Maximum: 50 keywords.

#### `locales`

Country/language objects such as:

```json
[
  { "countryCode": "US", "languageCode": "en" },
  { "countryCode": "DE", "languageCode": "de" }
]
```

Maximum: 10 locale pairs.

#### `devices`

Choose `desktop`, `mobile`, or both.

#### `maxResults`

Number of organic results requested from Google per SERP. Accepted range: 10–100. Google can return fewer recognizable organic links than requested.

#### `maxRequestRetries`

Retries transient proxy or upstream failures using the same sticky session. Accepted range: 0–5.

A run may create at most 1,000 domain rank-check rows.

### Input example

```json
{
  "domains": ["apify.com", "scrapingbee.com"],
  "keywords": ["apify scrapingbee"],
  "locales": [
    { "countryCode": "US", "languageCode": "en" }
  ],
  "devices": ["desktop"],
  "maxResults": 100,
  "maxRequestRetries": 2
}
```

### Output example

A real local check returned this shape:

```json
{
  "checkedAt": "2026-07-29T03:49:41.722Z",
  "keyword": "apify scrapingbee",
  "trackedDomain": "apify.com",
  "countryCode": "US",
  "languageCode": "en",
  "device": "desktop",
  "position": 1,
  "rankingUrl": "https://apify.com/alternatives/scrapingbee-alternative",
  "title": "ScrapingBee Alternative",
  "found": true,
  "organicResultsScanned": 8,
  "searchUrl": "http://www.google.com/search?q=apify+scrapingbee&hl=en&gl=us&num=100&filter=0&pws=0"
}
```

When a domain is absent, `position`, `rankingUrl`, and `title` are `null`, while `found` is `false`.

### How much does it cost to track Google organic rankings?

Pricing uses a one-time **$0.005 run-start event** plus one `rankCheck` event for every output row.

At the current BRONZE price of **$0.00504 per domain rank check**:

| Workload | Rank checks | Estimated event price |
| --- | ---: | ---: |
| 1 domain × 5 keywords | 5 | $0.005 start + 5 × $0.00504 rank checks |
| 2 domains × 25 keywords | 50 | $0.005 start + 50 × $0.00504 rank checks |
| 5 domains × 100 keyword/locale/device combinations | 500 | $0.005 start + 500 × $0.00504 rank checks |

Higher usage tiers reduce the per-check event price. Platform compute and proxy usage are covered by event pricing; your final run charge is shown by Apify.

### Scheduling position history

The Actor intentionally emits timestamped snapshots rather than hiding history in private internal storage.

To build a time series:

1. save a Task with your production input;
2. attach a daily or weekly Apify schedule;
3. export each run's dataset through a webhook or integration;
4. compare `position` by `checkedAt`, `keyword`, `trackedDomain`, locale, and device.

This model keeps each run reproducible and works with your preferred retention system.

### Google Sheets and data-pipeline workflows

Useful integrations include:

- append snapshots to Google Sheets;
- send completed-run webhooks to Make or Zapier;
- copy rows into BigQuery, Snowflake, PostgreSQL, or a data lake;
- trigger Slack alerts when a downstream workflow detects a large position change;
- join rankings with Search Console clicks and impressions outside the Actor.

The Actor does not calculate changes against prior runs itself. Compare snapshots downstream using their stable dimensions.

### Run with the Apify API using cURL

```bash
curl -X POST \
  "https://api.apify.com/v2/acts/automation-lab~seo-rank-tracker/runs?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "domains": ["apify.com"],
    "keywords": ["web scraping platform"],
    "locales": [{"countryCode":"US","languageCode":"en"}],
    "devices": ["desktop"],
    "maxResults": 100
  }'
```

Use `run-sync-get-dataset-items` instead of `runs` when a short synchronous client request is appropriate.

### 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/seo-rank-tracker').call({
  domains: ['apify.com'],
  keywords: ['web scraping platform'],
  locales: [{ countryCode: 'US', languageCode: 'en' }],
  devices: ['desktop'],
  maxResults: 100,
});

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/seo-rank-tracker').call(run_input={
    'domains': ['apify.com'],
    'keywords': ['web scraping platform'],
    'locales': [{'countryCode': 'US', 'languageCode': 'en'}],
    'devices': ['desktop'],
    'maxResults': 100,
})

items = client.dataset(run['defaultDatasetId']).list_items().items
print(items)
```

### Use with MCP and AI agents

Add this Actor to Claude Code through Apify MCP:

```bash
claude mcp add --transport http apify \
  "https://mcp.apify.com?tools=automation-lab/seo-rank-tracker"
```

**Claude Desktop**, **Cursor**, and **VS Code** can use the same HTTP MCP server configuration in their MCP settings:

```json
{
  "mcpServers": {
    "apify": {
      "url": "https://mcp.apify.com?tools=automation-lab/seo-rank-tracker"
    }
  }
}
```

Example prompts:

- “Check apify.com for `web scraping platform` in US desktop Google results.”
- “Compare apify.com and scrapingbee.com for `apify scrapingbee`.”
- “Run mobile and desktop checks for these SEO keywords in US and UK markets.”

### Accuracy, limits, and failure behavior

- Google results change by time, market, device, personalization, and experiments.
- `gl` and `hl` localize country and language; the Actor does not claim city or GPS-level localization.
- Google may return fewer organic links than `maxResults` requests.
- The first matching URL for a domain determines its position.
- Ads, Maps, shopping, AI, and Google navigation are excluded from organic positions.
- The parser relies on recognizable linked result headings; upstream markup can change.
- A challenge or unrecognizable empty SERP fails the run instead of producing false not-found rows.
- Results are observations, not guarantees of what every individual user sees.

### Responsible and legal use

Use the Actor only for lawful SEO measurement and public search-result analysis.

Respect applicable laws, contractual obligations, privacy requirements, and Apify platform rules. Do not use rankings to profile sensitive individuals or to evade access controls. Keep request volumes bounded and schedule only the checks you need.

Google is a trademark of Google LLC. This Actor is independent and is not endorsed by Google.

### FAQ

#### Why is `position` null?

The tracked domain was not present in the recognized organic results returned for that keyword, locale, and device. Check `organicResultsScanned`, verify the domain spelling, and try a more relevant keyword.

#### Why did the run fail instead of returning null positions?

The Actor detected a challenge, proxy failure, or result page without recognizable organic links. Failing protects monitoring pipelines from treating a blocked SERP as a genuine ranking loss. Retry later or inspect the run log.

#### Why do mobile and desktop positions differ?

Google can rank and display results differently by device. Treat each device as a separate time series.

#### Can I track a city?

Not in this release. Country/language localization is supported; city/GPS-level location is not claimed.

#### Does the Actor include ads or Maps positions?

No. It reports linked organic web results only.

### Related automation-lab Actors

- [Google Autocomplete Scraper](https://apify.com/automation-lab/google-autocomplete-scraper) for keyword suggestion discovery.
- [Google News Scraper](https://apify.com/automation-lab/google-news-scraper) for news-result monitoring.
- [SEO Audit Tool](https://apify.com/automation-lab/seo-audit-tool) for on-page technical SEO checks.

Use this Actor when the primary job is recurring organic domain position tracking rather than broad SERP extraction.

# Actor input Schema

## `domains` (type: `array`):

Domains or website URLs to match in organic results. Subdomains count as matches for their parent domain.

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

Google search queries whose organic positions should be checked.

## `locales` (type: `array`):

Country and language pairs for localized Google results. Use ISO two-letter country codes and language codes such as en, de, or pt-br.

## `devices` (type: `array`):

Check desktop, mobile, or both result layouts.

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

Maximum number of organic results requested from Google for each keyword, locale, and device combination.

## `maxRequestRetries` (type: `integer`):

Retries for transient proxy or Google response failures while keeping the same sticky session.

## Actor input object example

```json
{
  "domains": [
    "apify.com"
  ],
  "keywords": [
    "web scraping platform",
    "web scraper"
  ],
  "locales": [
    {
      "countryCode": "US",
      "languageCode": "en"
    }
  ],
  "devices": [
    "desktop"
  ],
  "maxResults": 20,
  "maxRequestRetries": 2
}
```

# Actor output Schema

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

Open the default dataset table containing keyword, domain, locale, device, position, and matched URL fields.

# 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 = {
    "domains": [
        "apify.com"
    ],
    "keywords": [
        "web scraping platform",
        "web scraper"
    ],
    "locales": [
        {
            "countryCode": "US",
            "languageCode": "en"
        }
    ],
    "devices": [
        "desktop"
    ],
    "maxResults": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("automation-lab/seo-rank-tracker").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 = {
    "domains": ["apify.com"],
    "keywords": [
        "web scraping platform",
        "web scraper",
    ],
    "locales": [{
            "countryCode": "US",
            "languageCode": "en",
        }],
    "devices": ["desktop"],
    "maxResults": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("automation-lab/seo-rank-tracker").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "domains": [
    "apify.com"
  ],
  "keywords": [
    "web scraping platform",
    "web scraper"
  ],
  "locales": [
    {
      "countryCode": "US",
      "languageCode": "en"
    }
  ],
  "devices": [
    "desktop"
  ],
  "maxResults": 20
}' |
apify call automation-lab/seo-rank-tracker --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=automation-lab/seo-rank-tracker",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/acts/RMJfH5AHedATal8cA/builds/bQlUlSgUpC1cPisWe/openapi.json
