# Competitor Keywords — Ranked Keywords of Any Domain (`saliga/competitor-keywords`) Actor

Every Google keyword a competitor's domain or page ranks for, with position, ranking URL, search volume, difficulty and intent. Or find who your organic competitors are. No Semrush or Ahrefs account needed.

- **URL**: https://apify.com/saliga/competitor-keywords.md
- **Developed by:** [Salih Küçükkayış](https://apify.com/saliga) (community)
- **Stats:** 3 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.40 / 1,000 rows

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

See every Google keyword a competitor ranks for: **position, ranking URL, search volume, keyword difficulty, intent and estimated clicks**, highest volume first. Works for a whole domain, a subdomain or one page, in up to 10 countries per run. Or find out who your real organic competitors are. No Semrush or Ahrefs account needed.

**$2 per 1,000 rows** ($1.80, $1.60 and $1.40 on the Starter, Scale and Business plans), **plus $0.02 per lookup.** A lookup is one page of up to 1,000 rows for one domain in one country. Domains with no rankings have no row fee, and failed lookups are free. No subscription, and platform usage is included. (Plus a tiny $0.00005 start fee per run.)

### What you get

- **Every keyword a competitor ranks for.** Up to 10,000 per domain and country, highest search volume first, with the position, the page that ranks and its title.
- **Movement.** Previous position and whether the keyword is new, up or down since the last check.
- **The numbers you sort by.** Search volume, estimated monthly clicks to that page, keyword difficulty (0 to 100), search intent, CPC and 12-month trend on every row.
- **Domain, subdomain or one page.** `asana.com`, `blog.asana.com` or `https://asana.com/pricing`.
- **Filter before you pay.** Position (for example first page only), minimum volume, maximum difficulty, intent and "ranking URL contains /blog/" are applied at the source.
- **Organic competitors.** For any domain, the sites that rank for the most of the same keywords, with how many they share, their average position on them, and their total keywords and estimated clicks. Giant sites like Wikipedia and Amazon are left out unless you want them.
- **Beyond the blue links.** Optionally include paid ads, featured snippets, local packs and AI Overview citations.
- **Many domains in one run.** Paste a list, link a CSV, or point to another Actor's dataset.

### Typical uses

- **Steal a content plan.** Ranked keywords of a competitor's `/blog/`, positions 1 to 10, difficulty under 40: their best-performing articles you can realistically match.
- **Keyword gap by hand.** Run your domain and two competitors, then compare in a spreadsheet (or merge with [Dataset Merge & Dedup Datasets](https://apify.com/saliga/dataset-merge-dedupe)).
- **Track who you are up against.** Competitors mode for your domain shows the sites Google actually puts next to you, which are often not the ones you think.
- **Agency prospecting.** A list of 200 prospect domains, first-page keywords only, to size each one's search presence.

### Input

```json
{
  "domains": ["asana.com", "https://monday.com/blog/"],
  "mode": "ranked",
  "countries": ["US"],
  "maxResults": 1000,
  "maxPosition": 10
}
```

| Field | Meaning |
|---|---|
| `domains` | Domains, subdomains or page URLs, up to 1,000 per run. |
| `mode` | `ranked` (default) or `competitors`. |
| `countries` | Up to 10 countries: two-letter codes or names. Default `US`. |
| `language` | Optional language code. Empty means each country's main language. |
| `maxResults` | Ranked: rows per domain and country, up to 10,000. Default 500. |
| `maxCompetitors` | Competitors: domains per domain and country, up to 1,000. Default 20. |
| `maxPosition` | Only positions up to this one (10 = first page). |
| `minSearchVolume`, `maxKeywordDifficulty`, `intents`, `urlContains` | Ranked: filters. Dropped rows are not charged. |
| `resultTypes` | Ranked: `organic` (default), `paid`, `featured_snippet`, `local_pack`, `ai_overview_reference`. |
| `excludeTopDomains` | Competitors: leave out sites that rank for almost everything. Default `true`. |
| `domainsUrl`, `datasetId`, `domainField` | Other ways to pass domains. Fields named `domain`, `url`, `website` or `target` are found automatically. |

### Output

Ranked keywords, one row per keyword, domain and country:

```json
{
  "target": "asana.com",
  "keyword": "performance improvement plan",
  "country": "US",
  "position": 15,
  "absolutePosition": 20,
  "previousPosition": 43,
  "positionChange": "up",
  "url": "https://asana.com/resources/performance-improvement-plan-template",
  "title": "Performance improvement plan template: Free PIP PDF",
  "resultType": "organic",
  "searchVolume": 1000000,
  "keywordDifficulty": 17,
  "difficultyLevel": "easy",
  "intent": "informational",
  "cpcUsd": 5.29,
  "trend3mPct": -33,
  "trendDirection": "falling",
  "status": "ok",
  "charged": true
}
```

A real row from a run on 17 October 2026 (asana.com, United States: 500 keywords, one lookup).

Competitors, one row per competing domain:

```json
{
  "target": "asana.com",
  "competitor": "monday.com",
  "country": "US",
  "sharedKeywords": 18450,
  "avgPositionShared": 21.4,
  "sharedKeywordsTop10": 2310,
  "competitorKeywords": 96200,
  "competitorEstimatedTraffic": 1240000,
  "type": "competitor",
  "status": "ok",
  "charged": true
}
```

This competitor row shows the shape only; its figures are not real data.

| Field | Meaning |
|---|---|
| `position`, `absolutePosition` | Position among results of the same type, and among all elements on the page |
| `previousPosition`, `positionChange` | Last check's position; `new`, `up`, `down` or `same` |
| `url`, `title`, `resultType`, `isFeaturedSnippet` | The page that ranks and how it appears |
| `searchVolume`, `cpcUsd`, `competitionLevel`, `monthlySearches`, `trend3mPct`, `trendDirection` | Keyword demand and its trend |
| `estimatedTraffic` | Estimated monthly clicks from this keyword to this page, from its position and search volume |
| `keywordDifficulty`, `difficultyLevel`, `intent` | 0 to 100 difficulty with a label, and the main search intent |
| `serpFeatures`, `lastUpdated` | What else is on that results page, and when it was last checked |
| `sharedKeywords`, `avgPositionShared`, `sharedKeywordsTop3`, `sharedKeywordsTop10` | Competitors: keywords both domains rank for, and how well the competitor ranks on them |
| `competitorKeywords`, `competitorEstimatedTraffic` | Competitors: all the competitor's ranking keywords and estimated monthly clicks |
| `status`, `note`, `charged` | `ok`, or why there is no row; `charged` is `true` only on rows you paid for |

Rows with no row fee: `no_data` (no rankings found; the lookup fee is charged), `invalid_target`, `input_error`, `spending_limit`, `free_plan_limit`, `skipped` (ten lookups in a row returned nothing), `throttled` and `provider_error`.

### What it costs

| Run | Price (no discount) |
|---|---|
| Top 500 keywords of 1 domain | $1.02 |
| Top 1,000 keywords of 10 domains | $20.20 |
| First-page keywords of 200 prospects (≈ 50 each) | $24.00 |
| 20 competitors of 1 domain | $0.06 |

These assume every row is delivered. On a paid Apify plan the row fee is 10 %, 20 % or 30 % lower.

### Apify free plan

Every lookup costs us real money at the data source, and free-plan runs earn nothing. So on the Apify free plan this Actor gives one lookup a day with up to 20 rows for the first domain in the first country. Everything else comes back as uncharged `free_plan_limit` rows. Any paid Apify plan has no such limits.

### Covered countries

The same 92 countries as [Keyword Research](https://apify.com/saliga/keyword-research), including the United States, United Kingdom, Canada, Australia, India, most of Europe and Latin America, Japan, South Korea and Türkiye. Other countries come back as an uncharged `input_error` row.

### Using it from an AI agent or code

The Actor works as a tool through the [Apify MCP server](https://mcp.apify.com?tools=saliga/competitor-keywords) and through the Apify API. Send `{"domains": ["example.com"], "countries": ["US"], "maxPosition": 10}`.

### Next steps with your results

- **More ideas around the keywords that work for them:** send the keyword column to [Keyword Research](https://apify.com/saliga/keyword-research).
- **What a competitor's site runs on:** [Tech Stack Detector](https://apify.com/saliga/tech-stack-detector).
- **Every URL on their site:** [Sitemap Extractor](https://apify.com/saliga/sitemap-extractor).
- **Merge and compare several domains:** [Dataset Merge & Dedup Datasets](https://apify.com/saliga/dataset-merge-dedupe).

### Good to know

- Rankings come from a licensed SEO data provider that checks Google results at scale. Nothing is scraped from Semrush, Ahrefs or any other tool, and no account with them is needed.
- Positions and clicks are estimates from the provider's last check of each keyword, not live rankings and not the site's own analytics.
- This Actor is not affiliated with, endorsed by or sponsored by Google, Semrush or Ahrefs. Names are trademarks of their owners.
- A run that is moved to another server, or resurrected, continues where it stopped. Nothing is looked up or charged twice.
- Pricing: one `result` charge per delivered row, one `lookup` charge per page the database answers, plus $0.00005 when a run starts.
- Questions or a missing feature: open an issue on the Actor page.

### FAQ

**Can I look up a single page?** Yes: pass the full URL, for example `https://example.com/pricing`.

**Why are the positions different from what I see in Google?** Google results change by location, device and person. These are the provider's last check for the chosen country, not a live search.

**How do I get only first-page keywords?** Set `maxPosition` to 10.

### More from this developer

- [Keyword Research](https://apify.com/saliga/keyword-research): keyword ideas, suggestions and related keywords with volume, difficulty and search intent, $2 per 1,000 keywords.
- [Domain SEO Overview](https://apify.com/saliga/domain-seo-overview): organic keywords, estimated traffic and top-10 rankings for thousands of domains, $5 per 1,000 domains.
- [Google SERP Rank Tracker](https://apify.com/saliga/serp-rank-tracker): Google positions for your keywords by country and device, with AI Overview checks, $2 per 1,000 result pages.
- [Google Trends API](https://apify.com/saliga/google-trends): interest over time, by region and related queries for hundreds of keywords, $6 per 1,000 queries.
- [Keyword Search Volume](https://apify.com/saliga/keyword-search-volume): Google Ads search volume, CPC and up to 48 months of trend for a keyword list, $2 per 1,000 keywords.
- [Sitemap Extractor](https://apify.com/saliga/sitemap-extractor): every URL a website lists in its sitemaps, with lastmod, images and hreflang, $0.25 per 1,000 URLs.
- [Broken Link Checker](https://apify.com/saliga/broken-link-checker): crawl a site and find broken links and images, with the pages they are on.
- [Tech Stack Detector](https://apify.com/saliga/tech-stack-detector): which CMS, ecommerce platform, analytics and hosting a website uses, $1.50 per 1,000 websites.
- [Website Screenshot + Change Detection](https://apify.com/saliga/website-screenshot-change-detection): screenshots of many URLs on desktop and mobile, with visual change detection between runs.
- [Dataset Merge & Dedup Datasets](https://apify.com/saliga/dataset-merge-dedupe): merge, dedupe and filter the results of several runs into one dataset.

# Actor input Schema

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

Competitor domains (example.com), subdomains (blog.example.com) or single pages (https://example.com/pricing). Up to 1,000 per run.

## `mode` (type: `string`):

Ranked keywords: every keyword the domain or page ranks for, highest search volume first. Competitors: the domains that rank for the most of the same keywords.

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

Up to 10 countries: two-letter codes (US, GB, DE) or names. Each domain is looked up in each country. Covered countries are listed in the README.

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

Optional language code such as en, es or de. Leave empty to use each country's main language.

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

Ranked keywords: how many to return per domain and country, highest search volume first. Up to 10,000.

## `maxCompetitors` (type: `integer`):

Competitors: how many competing domains to return, most shared keywords first.

## `maxPosition` (type: `integer`):

Keep only keywords where the domain ranks at this position or better (10 = first page). Competitors: count only shared keywords up to this position.

## `minSearchVolume` (type: `integer`):

Ranked keywords: keep only keywords with at least this many searches a month.

## `maxKeywordDifficulty` (type: `integer`):

Ranked keywords: keep only keywords at or below this difficulty (0-100). Leave empty for all.

## `intents` (type: `array`):

Ranked keywords: keep only these main intents. Leave empty for all.

## `urlContains` (type: `string`):

Ranked keywords: keep only keywords whose ranking page path contains this text, for example /blog/.

## `resultTypes` (type: `array`):

Ranked keywords: which kinds of Google results count. Organic only by default.

## `excludeTopDomains` (type: `boolean`):

Competitors: leave out sites that rank for almost everything (Wikipedia, YouTube, Amazon and the like).

## `domainsUrl` (type: `string`):

Link to a text file (one domain per line), a CSV with a domain or url column, or a JSON list.

## `datasetId` (type: `string`):

Use the output of another Actor. Set the field name below.

## `domainField` (type: `string`):

The column or field that holds the domain or URL. domain, url, website and target are found without setting this.

## Actor input object example

```json
{
  "domains": [
    "asana.com"
  ],
  "mode": "ranked",
  "countries": [
    "US"
  ],
  "maxResults": 500,
  "maxCompetitors": 20,
  "maxPosition": 100,
  "minSearchVolume": 0,
  "resultTypes": [
    "organic"
  ],
  "excludeTopDomains": true,
  "domainField": "domain"
}
```

# Actor output Schema

## `keywords` (type: `string`):

One row per keyword, domain and country: position, ranking URL, volume, difficulty and intent.

## `competitors` (type: `string`):

One row per competing domain (competitors mode).

## `fullResults` (type: `string`):

Every field, including monthly search volume.

## `summary` (type: `string`):

Counts for the run and why it stopped.

# 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": [
        "asana.com"
    ],
    "countries": [
        "US"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("saliga/competitor-keywords").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": ["asana.com"],
    "countries": ["US"],
}

# Run the Actor and wait for it to finish
run = client.actor("saliga/competitor-keywords").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 '{
  "domains": [
    "asana.com"
  ],
  "countries": [
    "US"
  ]
}' |
apify call saliga/competitor-keywords --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,saliga/competitor-keywords"
        }
    }
}
```

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/bzFAyGsLQMLRBy8f5/builds/soaYBMmp2PcoAzWq4/openapi.json
