# Keyword Position Tracker - Rank Change History (`quillray/keyword-position-tracker`) Actor

Track where your site ranks on Google for each keyword and country, and see what moved since your last check. No subscription: pay only for the checks you run.

- **URL**: https://apify.com/quillray/keyword-position-tracker.md
- **Developed by:** [Quillray](https://apify.com/quillray) (community)
- **Categories:** SEO tools, Marketing
- **Stats:** 3 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$2.00 / 1,000 keyword checkeds

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

## Keyword Position Tracker

**Know where your site ranks on Google for every keyword that matters, and see what moved since last time.**

Add your keywords, your domain and the countries you care about. Keyword Position Tracker checks each keyword in each country, finds your site among the organic results and tells you its position. Run it again tomorrow or next week and it shows you what went up, what went down and what dropped out. There is no subscription. You pay only for the checks you run.

### What it does

- Checks each keyword in each country and returns your position among organic results.
- Compares with your previous run and shows the previous position, the change and the trend (up, down, same, new, lost).
- Handles up to 200 keywords and 20 countries per run, desktop or mobile, in any Google interface language.
- Matches your whole domain, including www and subdomains, and returns the URL that ranks.
- Caps cost with maxPairs. Failed checks are not charged.
- Works with Apify Schedules to build a ranking history automatically.

### How to use it

1. Click **Try for free** (or **Start**).
2. Enter your **keywords** (one per line), your **domain** (for example `example.com`) and the **countries**.
3. Click **Start**. A run with a handful of keywords usually finishes in a minute or two.
4. Open the **Output** tab to see your rankings, or export them as CSV, Excel or JSON.
5. Optional: add a **schedule** so it runs every day or week, and an **integration** (email, Slack, Make, Zapier) to get the results after each run.

Example input:

```json
{
  "keywords": ["rank tracker", "seo tools"],
  "domain": "example.com",
  "countries": ["us", "gb"]
}
```

### Input

| Field        | Default   | What it does                                                                      |
| ------------ | --------- | --------------------------------------------------------------------------------- |
| `keywords`   | required  | 1-200 keywords. Duplicates are removed for you.                                   |
| `domain`     | required  | Your site, e.g. `example.com`. With or without `https://` and `www.`.             |
| `countries`  | `["us"]`  | 1-20 country codes, e.g. `us`, `gb`, `de`.                                        |
| `language`   | `en`      | Google interface language, e.g. `en`, `de`, `pt-BR`.                              |
| `device`     | `desktop` | `desktop` or `mobile` results.                                                    |
| `depth`      | 10        | How many organic results to check per keyword (1-30). 10 means the first page.    |
| `maxPairs`   | 100       | Cost cap: the most keyword x country checks in one run (1-2000).                  |
| `webhookUrl` | none      | Reserved for alerts, which are not available yet. Use Apify integrations for now. |

### Output

One row for each keyword in each country. You can view it as a table in the Output tab or download it.

| keyword      | country | rank           | previousPosition | change | trend | url                         |
| ------------ | ------- | -------------- | ---------------- | ------ | ----- | --------------------------- |
| rank tracker | us      | 4              | 7                | 3      | up    | https://example.com/tracker |
| seo tools    | us      | outside top 10 | 9                | -      | lost  | -                           |
| seo tools    | gb      | 8              | 8                | 0      | same  | https://example.com/tools   |

*Example rows for illustration.*

What the fields mean:

- **position / rank**: your position among organic results (ads are ignored), or "outside top N" when your site is not in the results you asked for.
- **previousPosition / change**: the position from your last successful check and the difference. Positive means you moved up.
- **trend**: `up`, `down`, `same`, `new` (found for the first time or back in the results), `lost` (dropped out), or `unknown` (first check without a position, or a failed check).
- **url**: the page of your site that ranks.
- **checkedAt**: when the keyword was checked.
- **error**: empty when all went well, otherwise the reason the check failed. A failed check is never reported as "not ranked" and is never charged.

### Cost

You pay per check, with no monthly fee. A run has two parts, both billed to your Apify account:

1. **This actor: 2.00 USD per 1,000 checks** (event `keyword-checked`, one per keyword x country with a result). Failed checks are free.
2. **Google search results**, fetched by the official `apify/google-search-scraper` actor that this actor runs for you. On the Apify FREE plan that is about 0.0045 USD per results page (cheaper on paid plans, e.g. 0.0018 USD on GOLD). One keyword at the default depth of 10 uses one page.

**Example:** 200 keywords in one country, checked every day for 30 days (6,000 checks) costs on the FREE plan about 27 USD for the search results plus 12 USD for this actor, around 39 USD in total. On paid Apify plans the search results cost less. Platform compute of this actor is not charged to you.

Tip: set **maxPairs** or a **maximum cost per run** in the run options to stay within budget. Prices can change, and Apify shows the current ones before you run.

### Limitations

- Positions come from live Google results and can shift by a few places between runs.
- Only organic results count: no ads, AI Overviews or local map packs. No city-level targeting.
- Alerts are not built in yet. Use Apify integrations to receive the results after each run.
- Run one schedule per domain at a time: two runs of the same domain at the same moment can overwrite each other's history.
- The actor depends on the `apify/google-search-scraper` actor. If that service has a problem, checks show an error and are not charged by us.

### Privacy and responsibility

- **The actor runs in your Apify account.** The publisher of this actor does not store, receive or see your input, your results or your history.
- **What is stored, and where.** The history (a hash of each keyword, the country, language, device, the last position and the time of the check) is kept in a Key-Value Store named `serp-rank-tracker-history` in your Apify account. Results go to your run's dataset. You can view, export and delete everything at any time in Apify Console.
- **You choose the keywords and domains and you are responsible for that choice.** Check the terms of the services involved and the law that applies to you before you run it.
- **Not affiliated with Google.** This actor is not affiliated with, endorsed by or sponsored by Google LLC. "Google" is used only to describe where the positions come from.

### FAQ

**How do I track my rankings over time?** Run the actor on a schedule with the same domain and keywords. Each run compares with the previous one, and your history builds up automatically.

**Can I start the history from scratch?** Yes. In Apify Console open Storage, then Key-value stores, and delete the record for your domain (or the whole `serp-rank-tracker-history` store). The next run starts fresh.

**Why does a keyword show `unknown`?** It is the first check and your site was not found, the check failed, or you changed `depth` so the old and new positions cannot be compared.

**Am I charged for errors?** No. A check that fails is free on our side. You only pay for the search pages that were actually fetched.

**Do you see my keywords?** No. Everything stays in your own Apify account, and keywords in the history are stored as hashes.

**Something not working, or missing a feature?** Open an issue on this actor's page. We read every one and reply as soon as we can.

# Actor input Schema

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

Search phrases to check (1-200). Newlines and repeated spaces are collapsed, duplicates are dropped. A phrase that starts with http:// or https:// is rejected, because the search actor would open it as a page. Example: seo tools.

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

Domain to track, e.g. example.com. If you give a URL, only its host name is used (the path and query are ignored). Subdomains and www count as the same domain. URLs with a user name or password are rejected.

## `countries` (type: `array`):

Country codes of the search actor, 1-20, e.g. us, gb, de. Use gb for the United Kingdom, not uk; a code the actor does not know is rejected before anything is checked.

## `language` (type: `string`):

Google interface language code from the search actor list, e.g. en, de, pt-BR, zh-CN. he is sent as iw (the code the actor knows); en-US and zh are rejected, use en and zh-CN.

## `device` (type: `string`):

Desktop or mobile results.

## `depth` (type: `integer`):

Organic results checked per keyword (1-30). Each started 10 results cost one search result page.

## `maxPairs` (type: `integer`):

Cost cap: only the first N keyword x country pairs are checked (keywords in the order given).

## `webhookUrl` (type: `string`):

Optional. Public http(s) URL; validated only, not called yet. Example: https://hooks.example.com/serp.

## Actor input object example

```json
{
  "keywords": [
    "seo tools"
  ],
  "domain": "example.com",
  "countries": [
    "us",
    "de"
  ],
  "language": "pt-BR",
  "device": "mobile",
  "depth": 20,
  "maxPairs": 50,
  "webhookUrl": "https://hooks.example.com/serp"
}
```

# Actor output Schema

## `results` (type: `string`):

One row per keyword x country pair: position, previous position, change, trend and the ranking URL.

# 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": [
        "rank tracker",
        "seo tools"
    ],
    "domain": "example.com",
    "countries": [
        "us",
        "gb"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("quillray/keyword-position-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 = {
    "keywords": [
        "rank tracker",
        "seo tools",
    ],
    "domain": "example.com",
    "countries": [
        "us",
        "gb",
    ],
}

# Run the Actor and wait for it to finish
run = client.actor("quillray/keyword-position-tracker").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": [
    "rank tracker",
    "seo tools"
  ],
  "domain": "example.com",
  "countries": [
    "us",
    "gb"
  ]
}' |
apify call quillray/keyword-position-tracker --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,quillray/keyword-position-tracker"
        }
    }
}
```

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/5P1py85TGZexfbgfX/builds/QjCmfl9zsJtkUftcW/openapi.json
