# Domain SEO Analyzer — Keywords, Traffic & Gap (`cheapapi/domain-seo-analyzer`) Actor

Get organic traffic, ranked keywords, competitors, top pages, subdomains, keyword gap, rank history and bulk traffic for any domain. No login.

- **URL**: https://apify.com/cheapapi/domain-seo-analyzer.md
- **Developed by:** [CheapAPI](https://apify.com/cheapapi) (community)
- **Categories:** SEO tools, Marketing, Developer tools
- **Stats:** 8 total users, 8 monthly users, 100.0% runs succeeded, 2 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event + usage

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

## Domain SEO Analyzer — Keywords, Traffic & Gap

**See how much Google traffic any website gets, which keywords it ranks for, who its competitors are and which keywords you are missing — 9 SEO reports in one Actor, from $2 per 1,000 rows, no SEO tool subscription and no login.**

### Why this Actor

- **$2 per 1,000 rows** ($0.002 per row) plus $0.015 per report. 1,000 ranked keywords of a domain cost **$2.015**; a domain overview costs **$0.017**. SEO suites that show this kind of data usually cost $100+ per month.

- **9 report types in one place**: Domain overview, Ranked keywords, Competitors, Top pages, Subdomains, **Keyword gap**, **SERP competitors**, **Rank history** (monthly, back to October 2020) and **Bulk traffic** (up to 1,000 domains per report at $0.0009 per domain).

- **Rich rows**: 30+ fields per ranked keyword (position, previous position, URL, search volume, 12-month search history, CPC, competition, difficulty 0–100, intent, estimated traffic, SERP features) and 22 metrics per domain overview (keywords per position range, traffic, traffic value, new/improved/declined/lost keywords, ads).

- **249 countries** (plus "All countries" for 4 reports), any search language, up to 10,000 rows per domain, and filters that run before rows are counted, so you only pay for rows you want.

- **Stable `id` on every row** — the same keyword, competitor, page or month gets the same id in every run, so you can deduplicate, upsert into a database or diff weekly runs.
  **Not included** (so you know before you run it):

- **Full backlink profiles** (referring domains, anchors, new/lost links, domain authority) — use [Backlink Checker](https://apify.com/cheapapi/backlink-checker). This Actor only adds per-page backlink and referring-domain counts to Ranked keywords rows.

- **On-page / technical site audit** (broken links, titles, meta tags, page speed) — use [SEO Site Audit](https://apify.com/cheapapi/seo-site-audit).

- **Visibility in ChatGPT, Gemini, Perplexity and other AI assistants** — use [AI Search Visibility Tracker](https://apify.com/cheapapi/ai-search-visibility-tracker). This Actor only reports Google AI Overview citations (`resultType` = `ai_overview_reference`).

- **Keyword ideas for a seed keyword** (not tied to a domain) — use [Keyword Research Tool](https://apify.com/cheapapi/keyword-research-tool); **live SERP pages** for a keyword — use [Google SERP Scraper](https://apify.com/cheapapi/google-serp-scraper).

- **Engagement metrics** (bounce rate, pages per visit, visit duration, traffic sources) — not offered: traffic here is estimated from Google rankings, not measured from visits.

- **Bing, Yandex or other search engines** — Google only.

### Compared with alternatives

Typical runs, priced from each Actor's pay-per-event list (Apify Free plan price, then Bronze–Gold price where the Actor has tiers; Platinum and Diamond pay the Gold price for all of them). The other Actors' prices are their listed fees. For this Actor, Apify platform usage is billed separately by Apify on top of the event fees (at the default 256 MB a small run typically uses about $0.001–$0.005), shown as "+ usage" in our row:

| Actor | 1 domain overview | Traffic of 100 domains | 1,000 ranked keywords of 1 domain | Backlink / authority data | Same price on every plan |
|---|---|---|---|---|---|
| All-in-one SEO toolkit Actor (~2,100 users; one flat fee per result row; its Store page says it does not run on the Apify Free plan) | not on Free (paid plans $0.004–0.005) | not on Free (paid plans $0.40–0.50) | top-keywords summary only (its Store page) | ✓ | ✗ |
| Most-used website-traffic Actor (~4,100 users) | $0.014 ($0.013 per result + $0.001 start) | $1.30 | not offered | ✗ | ✓ |
| Low-cost website-traffic Actor (~3,000 users) | $0.004 (paid plans $0.0011) | $0.30 (paid plans **$0.10**) | not offered | ✗ | ✗ |
| Website-traffic Actor (~1,500 users) | $0.005 (paid plans $0.0015–0.002) | $0.50 (paid plans $0.15–0.20) | not offered | ✗ | ✗ |
| Per-keyword competitor-keywords Actor (~270 users) | not offered | not offered | $2.00 (+ $0.00005 start fee) | ✗ | ✓ |
| **Domain SEO Analyzer** | **$0.017** + usage | **$0.105** + usage (Bulk traffic) | **$2.015** + usage | page-level counts only | ✓ |

Prices from public Apify Store listings, checked September 2026.

Where others are cheaper, honestly:

- **A single domain summary on a paid Apify plan**: the all-in-one Actor charges $0.004–0.005 per result vs our $0.017 (about 3–4× cheaper), and its summary also includes domain rating and backlink counts. If you only need a quick authority + traffic snapshot of a few domains on a paid plan, it is the cheaper choice.
- **Traffic checks on a paid plan**: the low-cost website-traffic Actor (~3,000 users) charges $0.001 per domain plus a $0.0001 start fee on Bronze and above, so 100 domains cost about $0.10 there vs $0.105 (+ usage) with our Bulk traffic — about equal. On paid plans it stays cheaper up to about 150 domains per run (our $0.015 report fee is spread over more domains above that, e.g. 1,000 domains: $1.00 there vs $0.915 here). At 100 domains our Bulk traffic is cheaper than it only on the Apify Free plan ($0.30 there). The ~1,500-user website-traffic Actor charges $0.0015–0.002 per domain on paid plans, so against it we become cheaper from about 14–25 domains per run; the most-used ~4,100-user traffic Actor ($0.013 per domain + $0.001 start) is slightly cheaper for a single domain ($0.014 vs our $0.0159 Bulk traffic) and costs more from 2 domains per run.
- **Full ranked-keyword lists**: the per-keyword Actor is about $0.015 cheaper per 1,000 keywords (no per-report fee), plus our platform usage on top.

What you get here in exchange: traffic checks for 100 domains at $0.105, the full ranked-keyword list (up to 10,000 rows per domain), keyword gap, SERP competitors, top pages, subdomains and monthly rank history back to October 2020 in the same Actor and output format, a small monthly allowance on the Apify Free plan, a stable `id` for comparing scheduled runs, and filters that run before rows are charged.

### What data you get

| Report | One row per | Main fields |
|---|---|---|
| **Domain overview** (default) | domain (or market with "All countries") | organic keywords, estimated monthly organic traffic, traffic value (USD), keywords at position 1, 2–3, 4–10 … 91–100, new / improved / declined / lost keywords, paid keywords & traffic |
| **Ranked keywords** | keyword | keyword, position, previous position, change (new/up/down/same), ranking URL, title, search volume, 12-month history, CPC, competition, difficulty, intent, estimated traffic, SERP features |
| **Competitors** | competitor domain | shared keywords, average position, competitor's total keywords & traffic, your vs their traffic on the shared keywords |
| **Top pages** | page URL | keywords and traffic per page, position distribution, paid data |
| **Subdomains** | subdomain | keywords and traffic per subdomain |
| **Keyword gap** | keyword | keywords a competitor ranks for and you don't (or shared / only yours), both positions and URLs, volume, CPC, difficulty, intent |
| **SERP competitors** | domain | who ranks for YOUR keyword list: average & median position, visibility, estimated traffic, positions per keyword |
| **Rank history** | month | keywords, traffic and position distribution month by month |
| **Bulk traffic** | domain or URL | organic, paid, featured snippet and local pack traffic and keyword counts |

Main output fields:

| Field | Type | Example |
|---|---|---|
| `id` | string | `"01a59e3bf8e1a0dcbc47"` — stable hash of report + domain + row key (keyword, competitor, page, month …) + country + language |
| `report` | string | `"rankedKeywords"` |
| `domain` | string | `"apify.com"` |
| `keyword` | string | `"1000 keywords"` |
| `position` / `previousPosition` | integer | `1` / `1` |
| `positionChange` | string | `"down"` (`new`, `up`, `down`, `same`) |
| `url` / `title` | string | `"https://example.com/free-seo-stats/top-1000-keywords"` |
| `searchVolume` | integer | `40` |
| `monthlySearches` | array | `[{ "year": 2026, "month": 8, "searchVolume": 10 }]` |
| `cpc` | number (USD) or null | `30.87` |
| `competition` / `competitionLevel` | number / string | `0.1` / `"LOW"` |
| `keywordDifficulty` | integer 0–100 | `15` |
| `searchIntent` | string | `"informational"` |
| `estimatedTraffic` / `trafficValueUsd` | number | `12.16` |
| `serpFeatures` | array | `["ai_overview", "organic", "people_also_ask"]` |
| `competitorDomain` | string | `"dentaly.org"` |
| `sharedKeywords` / `avgPosition` | integer / number | `152` / `57.43` |
| `gapType` | string | `"competitorOnly"`, `"shared"` or `"yoursOnly"` |
| `yourPosition` / `competitorPosition` | integer | `9` / `44` |
| `pageUrl` / `subdomain` | string | `"https://apify.com/store"` |
| `period` | string | `"2026-09"` (Rank history) |
| `organicKeywords` / `organicTraffic` / `organicTrafficValueUsd` | number | `11625` / `32394.41` / `197346.88` |
| `organicPos1` … `organicPos91to100` | integer | `42` |
| `organicNewKeywords` / `organicLostKeywords` | integer | `9698` / `1948` |
| `paidKeywords` / `paidTraffic` | number | `0` |
| `country` / `language` | string | `"US"` / `"en"` |
| `scrapedAt` | string (ISO time) | `"2026-09-29T05:05:42.761Z"` |

### How to use

1. Open the Actor in Apify Console and go to the **Input** tab.
2. Pick a **Report** (default: Domain overview).
3. Enter one or more **Domains** (e.g. `apify.com`; `https://` and `www.` are removed). For Keyword gap put **your domain first**, then competitors. For SERP competitors enter **Keywords** instead.
4. Choose a **Country** (default: United States).
5. Optional: open the **Advanced** sections to set max results, filters (search volume, position, difficulty, CPC …), sorting or rank history dates.
6. Optional: set **Maximum cost per run** in the run options to cap spending.
7. Click **Start**. When the run finishes, open the **Output** tab and use the *Domain & traffic overview*, *Keywords*, *Competitors*, *Top pages*, *Rank history* or *Bulk traffic* view, or export to CSV, Excel, JSON or HTML.

Ready-to-paste input (default):

```json
{
    "report": "domainOverview",
    "domains": ["apify.com"],
    "country": "US"
}
```

More inputs:

```json
{ "report": "rankedKeywords", "domains": ["example.com"], "country": "GB", "maxResults": 500, "maxPosition": 10, "minSearchVolume": 100 }
```

```json
{ "report": "keywordGap", "domains": ["my-shop.com", "competitor-a.com", "competitor-b.com"], "country": "DE", "maxKeywordDifficulty": 30 }
```

```json
{ "report": "serpCompetitors", "keywords": ["crm software", "best crm for small business"], "country": "US" }
```

```json
{ "report": "rankHistory", "domains": ["apify.com"], "country": "US", "historyFrom": "2025-10", "historyTo": "2026-09" }
```

#### API: curl

```bash
curl -X POST "https://api.apify.com/v2/acts/cheapapi~domain-seo-analyzer/run-sync-get-dataset-items?token=<YOUR_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"report": "rankedKeywords", "domains": ["apify.com"], "country": "US", "maxResults": 100}'
```

#### API: JavaScript (`apify-client`)

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: '<YOUR_TOKEN>' });
const run = await client.actor('cheapapi/domain-seo-analyzer').call({
    report: 'keywordGap',
    domains: ['my-shop.com', 'competitor.com'],
    country: 'US',
    maxResults: 300,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.length, items[0]);
```

#### API: Python (`apify-client`)

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_TOKEN>")
run = client.actor("cheapapi/domain-seo-analyzer").call(run_input={
    "report": "domainOverview",
    "domains": ["apify.com", "example.com"],
    "country": "US",
})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["id"], item["domain"], item["organicTraffic"])
```

### Use cases

- **Competitor research** — find who competes with you in Google and how much traffic they get.
- **Keyword gap / content gap** — list keywords competitors rank for and you don't, filtered to easy (low difficulty) keywords.
- **SEO monitoring** — schedule a weekly Domain overview or Ranked keywords run and compare rows by `id` to spot new, lost and moved keywords.
- **Agency prospecting & reporting** — check the traffic of hundreds of prospects at once with Bulk traffic, or add rank history charts to client reports.
- **Market sizing & due diligence** — estimate the organic traffic of a list of companies and what it would cost as ads.
- **SERP landscape** — see which domains dominate a keyword cluster with SERP competitors.

### Advanced options

| Input | Default | What it does |
|---|---|---|
| `maxResults` | 100 | Max rows per report (per domain, compared pair or keyword group), 1–10,000, paged automatically |
| `startOffset` | 0 | Skip the first N rows (continue a previous run) |
| `language` | main language of the country | 2-letter search database language, e.g. `en`, `de` |
| `includeOrganic` / `includePaid` | true / true | Count organic results / Google Ads |
| `includeFeaturedSnippets` / `includeLocalPack` | false / false | Also count featured snippet / local pack positions |
| `includeAiOverviews` | false | Ranked keywords: include keywords where the domain is cited in an AI Overview |
| `rankingStatus` | `live` | Ranked keywords, Top pages, Subdomains: `live`, `lost` or `all` rankings |
| `includeAbsolutePositions` | false | Ranked keywords: position counts on absolute positions |
| `ignoreSynonyms` | false | Skip near-duplicate keyword variants |
| `includeAudienceInsights` | false | Real-user click traffic plus gender and age split (every event of the report is charged twice) |
| `includeSerpDetails` | false | Keyword gap: add SERP features and total results |
| `minSearchVolume` / `maxSearchVolume` | none | Monthly search volume range (Ranked keywords, Keyword gap) |
| `minPosition` / `maxPosition` | none | Position range 1–100, e.g. 1–10 = page one |
| `minKeywordDifficulty` / `maxKeywordDifficulty` | none | Difficulty range 0–100 |
| `minCpc` / `maxCpc` | none | CPC range in USD (numbers, 0–1,000) |
| `keywordIncludes` / `keywordExcludes` | none | Keyword contains / does not contain text (e.g. exclude your brand) |
| `urlIncludes` | none | Only rows whose URL / page / subdomain contains text, e.g. `/blog/` |
| `searchIntent` | `any` | `informational`, `navigational`, `commercial` or `transactional` |
| `minTraffic` | none | Minimum estimated monthly traffic per row |
| `minKeywordsCount` | none | Competitors, Top pages, Subdomains: minimum ranking keywords |
| `sortBy` / `sortOrder` | `default` / `desc` | Sort by volume, position, traffic, traffic value, difficulty, CPC, keywords, shared keywords, avg. position, visibility or keyword |
| `customFilters` | none | Expert: extra filters (max 8 filters in total), e.g. `[["searchVolume", ">", 1000], ["keyword", "not_ilike", "%free%"]]` |
| `customSort` | none | Expert: up to 3 rules, e.g. `"keywordDifficulty,asc"` |
| `gapMode` | `missing` | Keyword gap view: `missing`, `shared`, `unique` or `all` (3 reports per competitor) |
| `excludeMajorPlatforms` | false | Competitors: leave out YouTube, Wikipedia, Amazon, Reddit … |
| `excludeDomains` | none | Competitors: domains to leave out (up to 1,000) |
| `focusDomains` | none | Competitors: up to 20 domains that sharpen the niche |
| `maxCompetitorPosition` | 100 | Competitors: only rankings up to this position count |
| `includeSubdomains` | true | SERP competitors: count subdomains as the main domain |
| `historyFrom` / `historyTo` | 12 months ago / this month | Rank history range (`YYYY-MM`, earliest 2020-10) |
| `alignWithCurrentData` | true | Rank history: recalculate past months with the current keyword database |

"All countries" works for Domain overview (one row per market), Top pages, Subdomains and Bulk traffic. If a minimum is above its maximum (e.g. min CPC 5, max CPC 1) the run stops with a clear message before anything is charged.

### Output example

Domain overview (default input):

```json
{
    "id": "f0051652b615ac13472c",
    "report": "domainOverview",
    "domain": "apify.com",
    "organicKeywords": 11625,
    "organicTraffic": 32394.41,
    "organicTrafficValueUsd": 197346.88,
    "organicPos1": 42,
    "organicPos2to3": 170,
    "organicPos4to10": 1512,
    "organicPos11to20": 2705,
    "organicPos21to30": 2344,
    "organicPos91to100": 34,
    "organicNewKeywords": 9698,
    "organicImprovedKeywords": 1117,
    "organicDeclinedKeywords": 627,
    "organicLostKeywords": 1948,
    "paidKeywords": 0,
    "paidTraffic": 0,
    "paidTrafficCostUsd": 0,
    "country": "US",
    "language": "en",
    "scrapedAt": "2026-09-29T05:05:42.761Z"
}
```

(`organicPos31to40` … `organicPos81to90` are left out here to keep the example short.)

Ranked keyword:

```json
{
    "id": "01a59e3bf8e1a0dcbc47",
    "report": "rankedKeywords",
    "domain": "example.com",
    "keyword": "1000 keywords",
    "searchVolume": 40,
    "cpc": null,
    "competition": 0.1,
    "competitionLevel": "LOW",
    "keywordDifficulty": 15,
    "searchIntent": "informational",
    "searchVolumeTrend": { "monthly": -91, "quarterly": -80, "yearly": -80 },
    "monthlySearches": [{ "year": 2026, "month": 8, "searchVolume": 10 }, { "year": 2026, "month": 7, "searchVolume": 110 }],
    "serpFeatures": ["ai_overview", "organic", "people_also_ask", "video"],
    "totalResults": 132,
    "position": 1,
    "absolutePosition": 2,
    "previousPosition": 1,
    "positionChange": "down",
    "resultType": "organic",
    "url": "https://example.com/free-seo-stats/top-1000-keywords",
    "title": "Top 1000 Keywords Searched on Google",
    "estimatedTraffic": 12.16,
    "isLost": false,
    "pageReferringDomains": 4,
    "pageBacklinks": 7,
    "serpUpdatedAt": "2026-08-13 18:11:06 +00:00",
    "country": "US",
    "language": "en",
    "scrapedAt": "2026-09-29T05:05:43.478Z"
}
```

Competitor:

```json
{
    "id": "d168fccc9f4fb860fa3c",
    "report": "competitors",
    "domain": "newmouth.com",
    "competitorDomain": "dentaly.org",
    "sharedKeywords": 152,
    "avgPosition": 57.43,
    "yourTrafficOnSharedKeywords": 246.98,
    "competitorTrafficOnSharedKeywords": 256.08,
    "competitorOrganicKeywords": 16962,
    "competitorOrganicTraffic": 40251.66,
    "competitorOrganicTrafficValueUsd": 272044.73,
    "competitorOrganicPos4to10": 61,
    "country": "US",
    "language": "en",
    "scrapedAt": "2026-09-29T05:10:00.000Z"
}
```

Keyword gap (shared view):

```json
{
    "id": "848c559cca284a628dd8",
    "report": "keywordGap",
    "domain": "seopanel.org",
    "competitorDomain": "examplesite.com",
    "gapType": "shared",
    "keyword": "seo open source",
    "searchVolume": 110,
    "cpc": 30.87,
    "keywordDifficulty": 17,
    "searchIntent": "commercial",
    "yourPosition": 9,
    "yourUrl": "https://www.seopanel.org/",
    "yourTraffic": 1.65,
    "competitorPosition": 44,
    "competitorUrl": "https://examplesite.com/",
    "competitorTraffic": 0.23,
    "country": "US",
    "language": "en",
    "scrapedAt": "2026-09-29T05:10:00.000Z"
}
```

SERP competitor rows have `keywords`, `competitorDomain`, `avgPosition`, `medianPosition`, `visibility`, `visibilityRating`, `estimatedTraffic`, `keywordsCount` and `keywordPositions`. Rank history rows have `period`, `year`, `month` plus the Domain overview metrics. Bulk traffic rows have `domain`, `organicTraffic`, `organicKeywords`, `paidTraffic`, `paidKeywords`, `featuredSnippetTraffic`, `featuredSnippetKeywords`, `localPackTraffic` and `localPackKeywords`. A `RUN_SUMMARY` record in the key-value store lists requests, rows delivered, domains without data and warnings.

### Pricing

**Typical cost:** a domain overview is **$0.017**, 1,000 ranked keywords of one domain **$2.015** (event fees; Apify platform usage billed separately).

**Apify Free plan:** Apify does not pay developers for usage on its Free plan, so on the Free plan this Actor can be used for up to **$0.25 of results per calendar month** — enough to try it on a small input. When the allowance is used up, the run ends with a clear message (not an error). Any paid Apify plan removes the limit; prices are the same.

Pay per event — no monthly fee, and **the same price on every Apify plan**. Apify platform usage is billed separately by Apify (at the default 256 MB a small run typically uses about $0.001–$0.005); the prices and examples below are event fees only. You are charged per report requested, even if nothing is found (every lookup is a paid request on our side); failed requests are free, except that a request that times out after being sent is still charged as if the full page was returned (report fee plus Report-without-results fees), because it may already have been processed.

| Apify plan | Free | Bronze | Silver | Gold | Platinum | Diamond |
|---|---|---|---|---|---|---|
| Prices below | same | same | same | same | same | same |

| Event | Price | When it is charged |
|---|---|---|
| Report | $0.015 | Once per report requested, even if nothing is found: per domain, per keyword-gap competitor and view, per group of up to 200 keywords (SERP competitors) or up to 1,000 domains (Bulk traffic) |
| Result row | $0.002 | Per row: keyword, competitor, page, subdomain, gap keyword, SERP competitor, domain overview, history month |
| Bulk traffic domain | $0.0009 | Per domain in the Bulk traffic report (instead of Result row) |
| Rank history report | $0.16 | Once per domain in Rank history, even if no history is found (plus $0.002 per month row) |
| Report without results | $0.0004 | Only when a report finds no rows (no data for the domain, or your filters removed everything): added to the $0.015 Report fee. Never charged when rows are returned or for Rank history (except for a request that timed out after being sent, see above) |

Worked examples:

- **1,000 ranked keywords of one domain**: $0.015 + 1,000 × $0.002 = **$2.015**.
- **Domain overview for 10 domains**: 10 × ($0.015 + $0.002) = **$0.17**.
- **Keyword gap, you vs 1 competitor, 300 missing keywords**: $0.015 + 300 × $0.002 = **$0.615**.
- **Bulk traffic for 1,000 domains**: $0.015 + 1,000 × $0.0009 = **$0.915**.
- **Rank history, 12 months**: $0.16 + 12 × $0.002 = **$0.184**.
- **Ranked keywords for a domain with no data (or filters that match nothing)**: $0.015 + $0.0004 = **$0.0154**; with Audience insights **$0.0308**.

With **Audience insights** enabled, every event of that report is charged twice. Set **Maximum cost per run** in the run options: the Actor limits rows up front and stops before the next report would exceed your budget (a report is only requested when the budget can pay for it even if it comes back empty; Rank history only when every month of the range fits).

### Integrations

- **Schedules** — run weekly or monthly from Apify Schedules; compare runs by `id` to track new, lost and moved keywords.
- **Webhooks** — get a call when a run finishes and fetch the dataset automatically.
- **Make, Zapier, n8n** — use the Apify app/node to start runs and send rows to Slack, email, a CRM or a database.
- **Google Sheets** — export with the Google Sheets integration, or download CSV/Excel.
- **API & MCP** — start runs and read datasets over the Apify API (examples above), or let AI agents call the Actor through the Apify MCP server.

### FAQ

**Is there a limit on the Apify Free plan, and is platform usage included?** Up to $0.25 of this Actor's results per calendar month, enough to try it. Apify pays developers nothing for Free-plan usage while our data costs are real, so this keeps the Actor sustainable. Runs that reach the allowance stop cleanly and keep everything collected so far; the allowance resets on the 1st of the month. Any paid Apify plan has no limit. Apify platform usage (compute) is not included: Apify bills it separately (about $0.001–$0.005 for a small run at the default 256 MB; shown per run in Apify Console).

**Is it legal?** The Actor returns aggregated SEO metrics (rankings, search volumes, traffic estimates) about public websites and public Google results. It does not log in anywhere and collects no personal data. You are responsible for how you use the results.

**How fresh is the data?** Search volumes are monthly averages with 12 months of history. Rankings are rechecked continuously, popular keywords more often than long-tail ones. Each Ranked keywords row has `serpUpdatedAt` showing when the result page was last checked, and every row has `scrapedAt`.

**How accurate is the traffic?** Traffic is an estimate: position × search volume × typical click-through rate, summed over all keywords. It is great for comparing domains and spotting trends; it will not match your own analytics exactly.

**Why did I get fewer results than `maxResults`?** The domain ranks for fewer keywords in that country, your filters removed rows, or your maximum cost per run was reached (see the log and `RUN_SUMMARY`). Small or new sites may have no data at all.

**Why was I charged $0.0154 for a report with no rows?** Every report is a paid lookup on our side, even when the domain has no data or your filters remove every row. An empty report costs the $0.015 Report fee plus a $0.0004 "Report without results" fee (twice with Audience insights). Failed requests (errors) are free, but a request that times out after being sent is still charged as if the full page was returned, because it may already have been processed (`unansweredRequests` in RUN_SUMMARY). Check the domain spelling and loosen filters before running large lists.

**How do I control my budget?** Set *Maximum cost per run* in the run options, lower `maxResults` and use filters (they run before rows are counted). The Actor never charges past your limit.

**Can I schedule and monitor domains?** Yes. Create an Apify Schedule (e.g. weekly Domain overview or Ranked keywords) and compare runs by the stable `id` field. Rank history gives you monthly figures back to October 2020 in one run.

**Which countries and languages are supported? Can I analyze a single page?** 249 countries. Leave language empty to use the main language of the country, or set a 2-letter code (e.g. `en` for English results in Germany). Ranked keywords and Bulk traffic also accept full page URLs (e.g. `https://example.com/blog/post`).

**Do you store the domains I analyze?** No. Domains, keywords and results are kept only in your own run's dataset and key-value store in your Apify account, under your Apify data-retention settings. We do not keep a copy, share your target lists or use them for anything else.

**Where do I get help?** Open an issue on the Actor's **Issues** tab in Apify Console with your run link, and we will look into it.

### Limitations

- Traffic and traffic value are estimates based on rankings, search volumes and click-through rates.
- Google only (no Bing/Yandex). Full backlink profiles ([Backlink Checker](https://apify.com/cheapapi/backlink-checker)) and on-page audits ([SEO Site Audit](https://apify.com/cheapapi/seo-site-audit)) are not part of this Actor.
- Rank history is monthly and starts in October 2020.
- The keyword gap compares your domain with each competitor separately (one report per competitor and view).
- "All countries" works only for Domain overview, Top pages, Subdomains and Bulk traffic.
- Options that do not apply to the chosen report are ignored with a warning in the log.

### Privacy

The output contains domain-level and keyword-level SEO metrics only — no names, emails or other personal data. Page titles and URLs come from public Google results.

# Actor input Schema

## `report` (type: `string`):

What you want to know about the domain(s). Domain overview is a one-row summary per domain (keywords, traffic, positions). All other reports return one row per keyword, competitor, page, subdomain, month or domain.

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

Domains to analyze, e.g. example.com (https:// and www. are removed automatically). Keyword gap: put YOUR domain first, then 1 or more competitors. Bulk traffic: up to thousands of domains or page URLs. Ranked keywords also accept a full page URL. Not needed for SERP competitors. Each report is charged even if a domain has no data, so check the spelling.

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

Only for the SERP competitors report: the keywords whose Google results you want to analyze. Up to 200 keywords are analyzed together per report; more are split into groups of 200.

## `country` (type: `string`):

Google country database to use, e.g. United States. "All countries" works for Domain overview, Top pages, Subdomains and Bulk traffic.

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

Maximum rows per report (per domain, per compared domain pair, or per group of keywords). Domain overview always returns 1 row per domain (except with "All countries"). Rank history returns one row per month.

## `startOffset` (type: `integer`):

Skip this many rows at the start (useful to continue a previous run, e.g. 1000 to get rows 1001+).

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

2-letter language code of the search database, e.g. en, de, es, fr, tr. Leave empty to use the main language of the selected country.

## `includeOrganic` (type: `boolean`):

Include regular (organic) Google results.

## `includePaid` (type: `boolean`):

Include Google Ads results.

## `includeFeaturedSnippets` (type: `boolean`):

Also include featured snippet positions.

## `includeLocalPack` (type: `boolean`):

Also include local pack (map) positions.

## `includeAiOverviews` (type: `boolean`):

Ranked keywords only: also return keywords where the domain is cited in a Google AI Overview.

## `rankingStatus` (type: `string`):

Ranked keywords, Top pages and Subdomains: return current rankings, rankings the domain has lost, or both.

## `includeAbsolutePositions` (type: `boolean`):

Ranked keywords only: position counts are also calculated on absolute positions (counting all SERP elements).

## `ignoreSynonyms` (type: `boolean`):

Skip highly similar keyword variants (e.g. plural/singular) to get a cleaner list.

## `includeAudienceInsights` (type: `boolean`):

Adds real-user click traffic estimates plus gender and age distribution. Available for Ranked keywords, Competitors, Top pages, Subdomains, Keyword gap and Rank history. Every event of the report is charged twice when enabled.

## `includeSerpDetails` (type: `boolean`):

Keyword gap only: add Google result page details (result types, total results) to each keyword.

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

Ranked keywords, Keyword gap: only keywords with at least this many monthly searches.

## `maxSearchVolume` (type: `integer`):

Ranked keywords, Keyword gap: only keywords with at most this many monthly searches.

## `minPosition` (type: `integer`):

Ranked keywords, Keyword gap: only keywords where the domain ranks at this position or lower, e.g. 4.

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

Ranked keywords, Keyword gap: only keywords where the domain ranks at this position or better, e.g. 10 for page one.

## `minKeywordDifficulty` (type: `integer`):

Ranked keywords, Keyword gap: only keywords with at least this difficulty score (0 = easiest, 100 = hardest). Leave empty for no limit.

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

Ranked keywords, Keyword gap: only keywords with at most this difficulty score (0–100), e.g. 30 for easier keywords. Leave empty for no limit.

## `minCpc` (type: `number`):

Ranked keywords, Keyword gap: only keywords with at least this cost per click in USD, e.g. 0.5. Leave empty for no limit.

## `maxCpc` (type: `number`):

Ranked keywords, Keyword gap: only keywords with at most this cost per click in USD, e.g. 5. Leave empty for no limit.

## `keywordIncludes` (type: `string`):

Ranked keywords, Keyword gap: only keywords containing this text (case-insensitive).

## `keywordExcludes` (type: `string`):

Ranked keywords, Keyword gap: drop keywords containing this text, e.g. your brand name.

## `urlIncludes` (type: `string`):

Ranked keywords, Keyword gap, Top pages, Subdomains: only rows whose ranking URL / page / subdomain contains this text, e.g. /blog/.

## `searchIntent` (type: `string`):

Ranked keywords, Keyword gap: only keywords with this main search intent.

## `minTraffic` (type: `integer`):

Only rows with at least this estimated monthly organic traffic (all reports except Domain overview, Rank history and Bulk traffic).

## `minKeywordsCount` (type: `integer`):

Competitors, Top pages, Subdomains: only rows with at least this many ranking keywords.

## `sortBy` (type: `string`):

How rows are ordered before the limit is applied. Not every option applies to every report; unavailable options fall back to the default order.

## `sortOrder` (type: `string`):

Direction for "Sort by": descending (largest first) or ascending.

## `customFilters` (type: `array`):

Extra filters as a JSON list of \[field, operator, value], all combined with AND. Example: \[\["searchVolume", ">", 1000], \["keyword", "not_ilike", "%free%"]]. Fields: keyword, searchVolume, cpc, competition, keywordDifficulty, searchIntent, position, absolutePosition, traffic, trafficValue, url, resultType, competitorPosition, competitorTraffic, competitorUrl, domain, avgPosition, sharedKeywords, keywordsCount, paidKeywordsCount, paidTraffic, newKeywords, lostKeywords, medianPosition, visibility, visibilityRating (depending on the report). Operators: <, <=, >, >=, =, <>, in, not_in, like, not_like, ilike, not_ilike, regex, not_regex, match, not_match.

## `customSort` (type: `array`):

Up to 3 sort rules as "field,direction", e.g. "keywordDifficulty,asc". Uses the same field names as custom filters. Combined with "Sort by" (max 3 rules in total).

## `gapMode` (type: `string`):

Which keywords the Keyword gap report returns for each competitor. Every row has a "gapType" field.

## `excludeMajorPlatforms` (type: `boolean`):

Leave out huge sites that rank for everything (YouTube, Wikipedia, Amazon, Reddit, …).

## `excludeDomains` (type: `array`):

Competitor domains to leave out (up to 1000).

## `focusDomains` (type: `array`):

Up to 20 extra domains; only keywords that these domains AND your domain rank for are used to find competitors — sharpens results for a niche.

## `maxCompetitorPosition` (type: `integer`):

Only rankings up to this position are used to find competitors, e.g. 10 = only first-page overlap.

## `includeSubdomains` (type: `boolean`):

SERP competitors: count subdomains (blog.example.com) as part of their main domain.

## `historyFrom` (type: `string`):

First month of the history as YYYY-MM, e.g. 2024-01 (earliest 2020-10). Leave empty to start 12 months ago.

## `historyTo` (type: `string`):

Last month of the history as YYYY-MM, e.g. 2025-12. Leave empty to end with the current month.

## `alignWithCurrentData` (type: `boolean`):

Recalculate past months with the current keyword database so months are comparable. Turn off to get the figures exactly as they were recorded.

## Actor input object example

```json
{
  "report": "domainOverview",
  "domains": [
    "apify.com"
  ],
  "country": "US",
  "maxResults": 100,
  "startOffset": 0,
  "includeOrganic": true,
  "includePaid": true,
  "includeFeaturedSnippets": false,
  "includeLocalPack": false,
  "includeAiOverviews": false,
  "rankingStatus": "live",
  "includeAbsolutePositions": false,
  "ignoreSynonyms": false,
  "includeAudienceInsights": false,
  "includeSerpDetails": false,
  "searchIntent": "any",
  "sortBy": "default",
  "sortOrder": "desc",
  "gapMode": "missing",
  "excludeMajorPlatforms": false,
  "maxCompetitorPosition": 100,
  "includeSubdomains": true,
  "alignWithCurrentData": true
}
```

# Actor output Schema

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

No description

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

No description

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

No description

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

No description

## `topPages` (type: `string`):

No description

## `rankHistory` (type: `string`):

No description

## `bulkTraffic` (type: `string`):

No description

## `summary` (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 = {
    "report": "domainOverview",
    "domains": [
        "apify.com"
    ],
    "country": "US"
};

// Run the Actor and wait for it to finish
const run = await client.actor("cheapapi/domain-seo-analyzer").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 = {
    "report": "domainOverview",
    "domains": ["apify.com"],
    "country": "US",
}

# Run the Actor and wait for it to finish
run = client.actor("cheapapi/domain-seo-analyzer").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 '{
  "report": "domainOverview",
  "domains": [
    "apify.com"
  ],
  "country": "US"
}' |
apify call cheapapi/domain-seo-analyzer --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,cheapapi/domain-seo-analyzer"
        }
    }
}
```

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/u4puhRqNveAbXuE6m/builds/8UpZheDRZQ2HuhRTM/openapi.json
