# Expiring Domains Finder — Expired Domain List & SEO (`cheapapi/expiring-domains-finder`) Actor

Find expiring and expired domains by date, TLD and keyword, with registrar, WHOIS dates, backlinks, referring domains and organic traffic. No login.

- **URL**: https://apify.com/cheapapi/expiring-domains-finder.md
- **Developed by:** [CheapAPI](https://apify.com/cheapapi) (community)
- **Categories:** SEO tools, Lead generation, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 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

## Expiring Domains Finder — Expired Domain List & SEO

Get ready-to-use lists of expiring and recently expired domains, filtered by expiration date, TLD, keywords, backlinks, referring domains and organic traffic, with WHOIS dates and registrar for every domain, as a one-off list or a daily feed of only new domains.

**Why this Actor**

- **$2.15 per 1,000 domains**, including the list request fee. No subscription, no login, no API key. Apify platform usage is billed separately by Apify (usually $0.001–$0.005 for a small run).
- **Large lists**: up to 1,000 domains per request and up to 100,000 domains per run.
- **29 fields per domain**: stable `id`, expiration, creation and update dates, days left, domain age, registrar, status codes, backlinks, dofollow links, referring domains, organic keywords, organic traffic and its dollar value, paid search metrics, and a transparent **`domainScore` (0–100)** (plus 24 optional ranking-distribution columns).
- **Filters built for domain investors and SEOs**: expiring within N days or any date range, TLDs, several keywords at once (any may match; contains / starts / ends / exact / regex), no hyphens, maximum name length, minimum age, minimum referring domains, backlinks and traffic, and a minimum domain score. Sort by domain score too.
- **Daily monitoring built in**: turn on *Only new domains since the last run* and schedule it. Each domain is delivered once per search; repeats are skipped and cost only $0.0015 each (less than a delivered domain).
- **Full expert control**: raw filters with `and` / `or` nesting on every database field, up to 3 sort rules, and a start offset.
- **Not included**: registrant contact details, auction or backorder prices, live availability checks and third-party authority scores (see [Not included](#not-included)).

### Compared with alternatives

Typical run: 1,000 domains. Prices from public Apify Store listings, checked September 2026 (Free plan; our price is the same on every plan). For this Actor, Apify platform usage is billed separately by Apify on top of the price shown (at the default 256 MB a small run typically uses about $0.001–$0.005).

| Option | Price for 1,000 domains | Still-registered domains expiring in a date window | Organic traffic and keywords per domain | Only-new-domains mode |
|---|---|---|---|---|
| Most-used expired-domain list scraper on the Store (~300 users) | $3.08 ($0.09 start + $0.00299 per row) **plus platform usage** you pay separately | Partly (whatever lists the scraped listing site shows) | ✗ (backlinks, popularity, rank) | ✗ |
| Free community expired-domain finder (~76 users) | $0 Actor fee, you pay platform usage and proxies | ✗ (days since expiry only) | ✗ (backlinks, referring domains, archive history) | ✗ |
| Expired-domain marketplace aggregator API (~13 users) | **$0.17**, cheaper than us: its only listed charge is a $0.017 start fee per run, and one run returns one page of up to 100 domains (max 5,000 per search), so 1,000 domains = 10 runs | Partly (names already listed at auction or backorder) | Third-party visit estimates | Not documented |
| Dropped-names daily feed (~12 users) | **$1.20** ($0.0012 per row; $0.50 on Gold), cheaper than us | ✗ (dropped names) | ✗ (not documented) | ✓ (daily deduplicated feed) |
| **This Actor** | **$2.15** ($0.16 per list request + $0.00199 per domain) **plus platform usage** billed separately by Apify | ✓ any past or future window, still registered or expired | ✓ organic and paid keywords, traffic and traffic value | ✓ repeats skipped ($0.0015 each) |

**Small runs:** our $0.16 list-request fee is charged once per request, so runs of fewer than about 160 domains cost more per domain than the most-used scraper's $0.00299 per row (100 domains: $0.36 here, i.e. $0.0036 per domain), and below about 70 domains we are more expensive even after its $0.09 start fee. From about 160 domains up we are cheaper per domain.

The two cheaper options cover different inventory. The aggregator lists names that are already on auction, backorder or dropped, with third-party authority scores and archive history, which we do not provide. The daily feed gives dropped domain names without link or traffic metrics. What you get here for the extra cost is the list of domains **before** they expire, filtered and sorted by our own link and Google traffic data, with WHOIS dates and a scheduled feed of only new domains.

### What data you get

One row per domain.

| Field | Type | Example |
|---|---|---|
| `id` | string | `d8ecef93d64672f5` (stable: hash of domain + expiration date) |
| `position` | integer | `1` (position in the sorted list) |
| `domain` | string | `harborviewbakery.com` |
| `tld` | string | `com` |
| `name` | string | `harborviewbakery` |
| `expiresAt` | ISO date | `2026-10-12T17:22:41.000Z` |
| `daysUntilExpiration` | integer | `14` (negative = already expired) |
| `isRegistered` | boolean | `true` (`false` = registration expired / dropped) |
| `createdAt` | ISO date | `2011-10-12T17:22:41.000Z` |
| `domainAgeYears` | number | `15` |
| `domainScore` | integer | `73` (0–100, calculated by the Actor, see [Domain score](#domain-score)) |
| `lastChangedAt` | ISO date | `2025-10-13T08:14:02.000Z` |
| `whoisUpdatedAt` | ISO date | `2026-09-20T04:31:10.000Z` |
| `firstSeenAt` | ISO date | `2020-10-07T00:00:00.000Z` |
| `registrar` | string | `GoDaddy.com, LLC` |
| `statusCodes` | array | `["client_transfer_prohibited"]` |
| `referringDomains` | integer | `214` |
| `referringMainDomains` | integer | `187` |
| `referringPages` | integer | `2911` |
| `backlinks` | integer | `3870` |
| `dofollowBacklinks` | integer | `2645` |
| `backlinksUpdatedAt` | ISO date | `2026-09-04T18:57:50.000Z` |
| `organicKeywords` | integer | `1180` (keywords ranking in Google) |
| `organicTraffic` | number | `420` (estimated monthly visits) |
| `organicTrafficValue` | number | `612.88` (USD, what that traffic would cost as ads) |
| `paidKeywords` / `paidTraffic` / `paidTrafficValue` | number | `0` |
| `organicPos1` … `organicPos91to100`, `paidPos1` … `paidPos91to100` | integer | only with `includeRankingDistribution` |
| `scrapedAt` | ISO date | `2026-09-28T15:51:53.371Z` |

A `RUN_SUMMARY` record in the key-value store lists the filters and sort rules applied, the number of domains matching the filters in total, pages charged, domains checked and saved, how many domains were skipped for a low domain score (`domainsBelowScore`), and (with *Only new domains*) how many domains were already known and skipped (`repeatsSkipped`, `repeatChecksCharged`).

### Domain score

`domainScore` is a simple, fully documented 0–100 score calculated by the Actor from four columns of the same row. It is not a third-party authority metric; use it to rank a list quickly, then check the raw columns.

```
domainScore = round( 40 × part(referringDomains, 1,000)
                   + 20 × part(backlinks, 100,000)
                   + 25 × part(organicTraffic, 10,000)
                   + 15 × min(domainAgeYears / 20, 1) )

part(value, cap) = min( log10(1 + value) / log10(1 + cap), 1 )      missing values count as 0
```

Links and traffic grow over several orders of magnitude, so each tenfold increase adds the same number of points until the cap (1,000 referring domains, 100,000 backlinks, 10,000 monthly visits, 20 years) gives the full share.

| Referring domains | Backlinks | Organic traffic | Age (years) | domainScore |
|---|---|---|---|---|
| 5 | 20 | 0 | 3 | 18 |
| 30 | 300 | 50 | 12 | 49 |
| 100 | 1,000 | 100 | 10 | 59 |
| 1,000+ | 100,000+ | 10,000+ | 20+ | 100 |

- **Filter**: *Minimum domain score* (`minDomainScore`, 0–100). The score is calculated after the data is fetched, so it does not use one of the 8 filter conditions. Domains below it are skipped, and because they were fetched and checked for you, each one costs **$0.0018** (`score-check` event); every list request pays the $0.16 request fee, even if no domain passes. Without a minimum score this fee is never charged. To keep checks cheap, combine it with a database minimum such as *Minimum referring domains* so fewer weak domains have to be checked, and keep the default sort by referring domains. `RUN_SUMMARY.domainsBelowScore` shows how many domains were skipped.
- **Sort**: *Sort by* = *Domain score*. The database picks the domains by referring domains (the largest part of the score), then the Actor re-orders the whole list by score in the chosen direction and numbers `position` in that order. Up to 10,000 domains per run with this sort.
- In raw filters and additional sort rules, use the underlying columns (`referringDomains`, `backlinks`, `organicTraffic`, `createdAt`).

### How to use

1. Open the Actor in Apify Console and click **Try for free**.
2. Set **Expiring within (days)** (default 30), the **TLDs** you want and optionally one or more **Keywords in domain** (a domain matches if it contains any of them; leave empty for all names).
3. Set **Maximum domains** (default 100).
4. Optional: open **Advanced: SEO quality filters** and set e.g. *Minimum referring domains* = 20.
5. For a daily feed: open **Scheduled monitoring**, tick **Only new domains since the last run**, then save the task and add a schedule (see [Daily monitoring](#daily-monitoring-only-new-domains)).
6. Click **Start**. Download the list as CSV, Excel or JSON, or use the **Expiring domains** table view.

Ready-to-paste input:

```json
{
    "expiringWithinDays": 30,
    "tlds": ["com", "net", "org"],
    "keywords": ["shop", "store"],
    "maxResults": 1000,
    "minReferringDomains": 20,
    "excludeHyphens": true,
    "sortBy": "referringDomains",
    "sortDirection": "desc"
}
```

API (`curl`):

```bash
curl -X POST "https://api.apify.com/v2/acts/cheapapi~expiring-domains-finder/run-sync-get-dataset-items?token=<YOUR_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"expiringWithinDays": 30, "tlds": ["com"], "maxResults": 1000, "minReferringDomains": 20}'
```

JavaScript (`apify-client`):

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

const client = new ApifyClient({ token: '<YOUR_TOKEN>' });
const run = await client.actor('cheapapi/expiring-domains-finder').call({
    expiringWithinDays: 30,
    tlds: ['com'],
    maxResults: 1000,
    minReferringDomains: 20,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

Python (`apify-client`):

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_TOKEN>")
run = client.actor("cheapapi/expiring-domains-finder").call(run_input={
    "expiringWithinDays": 30,
    "tlds": ["com"],
    "maxResults": 1000,
    "minReferringDomains": 20,
})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["domain"], item["expiresAt"], item["referringDomains"])
```

### Use cases

- **Domain investing**: shortlist aged, short, hyphen-free .com names that expire in the next weeks, before they reach auction or drop.
- **Expired domains for SEO**: find domains with many referring domains and remaining organic traffic for 301 redirects or site rebuilds.
- **Backorder planning**: export daily lists of domains expiring tomorrow in your target TLDs and feed them to your backorder workflow.
- **Brand monitoring**: find expiring domains that contain any of your brand or product names (list them all in *Keywords in domain*).
- **Daily "new expiring domains" feed**: schedule the Actor with *Only new domains* on and get only the domains you have not seen yet, straight into Google Sheets or Slack.
- **Lead generation**: reach out to owners of valuable domains about to expire (e.g. for agencies and registrars).
- **Market research**: analyse registrars, TLDs and domain ages of expiring inventory.

### Advanced options

| Input | Default | Meaning |
|---|---|---|
| `minReferringDomains` | – | Only domains with at least this many referring domains (0–1,000,000). |
| `minBacklinks` | – | Minimum total backlinks (0–1,000,000,000). |
| `minDofollowBacklinks` | – | Minimum dofollow backlinks (0–1,000,000,000). |
| `minReferringMainDomains` | – | Minimum referring root domains (0–1,000,000). |
| `minOrganicTraffic` | – | Minimum estimated monthly organic visits (0–1,000,000,000). |
| `minOrganicKeywords` | – | Minimum number of ranking keywords (0–100,000,000). |
| `minOrganicTrafficValue` | – | Minimum monthly traffic value in USD (0–1,000,000,000). |
| `minDomainScore` | – | Minimum `domainScore` (0–100). Applied by the Actor after fetching; each skipped domain costs $0.0018; does not count toward the 8 conditions. |
| `expiresFrom` / `expiresTo` | – | Custom expiration range (`2026-10-31`, `-30 days`, `+90 days`, `today`). Replaces *Expiring within*. A past range finds recently expired domains. |
| `registrationStatus` | `any` | `registered` (still active) or `expired` (registration ended, recently dropped). |
| `minDomainAgeYears` | – | Only domains created at least N years ago. |
| `keywords` | `[]` | Simple section. Empty = all domain names. Otherwise the domain must contain at least one of these texts (OR). Letters, digits, hyphens, dots; up to 50. |
| `keywordMatch` | `contains` | How `keywords` match: `contains`, `startsWith`, `endsWith` (name ends with it), `exact` (name equals it, any TLD), `regex` (each keyword is a pattern). |
| `excludeKeywords` | `[]` | Skip domains containing any of these texts. |
| `excludeHyphens` | `false` | Skip domains with a hyphen. |
| `maxNameLength` | – | Maximum length of the name before the first dot. |
| `registrar` | – | Registrar name contains this text (2–100 characters). |
| `onlyNewDomains` | `false` | Skip domains this search already delivered in an earlier run. Each skipped repeat costs $0.0015; every list request costs $0.16 even if nothing new is found. |
| `monitoringName` | – | Optional name for the delivered-domains history (letters, digits, spaces, `.`, `-`, `_`). Empty = one history per filter set. |
| `maxScannedDomains` | auto: with `onlyNewDomains` 200 (or `maxResults` if higher, a protective default); with only `minDomainScore` 2 × `maxResults`, min 1,000 | With `onlyNewDomains` or `minDomainScore`: how many domains of the sorted list are checked at most to find ones to deliver (100–100,000). With a minimum score it also caps the score-check fees. |
| `sortBy` / `sortDirection` | `referringDomains` / `desc` | Main sort: referring domains, domain score (re-ordered by the Actor, max 10,000 domains), backlinks, dofollow backlinks, referring root domains, referring pages, organic traffic, organic keywords, traffic value, expiration date, creation date, first seen, last changed, domain name, or none. |
| `additionalSortRules` | `[]` | Up to 2 tie-breakers as `"field,direction"`, e.g. `"expiresAt,asc"`. |
| `startOffset` | `0` | Skip the first N domains of the sorted list (max 10,000). |
| `rawFilters` | – | Expert conditions on any output field, e.g. `[["organicTraffic", ">", 100], "or", ["referringDomains", ">=", 50]]`. Operators: `=`, `<>`, `<`, `<=`, `>`, `>=`, `in`, `not_in`, `like`, `not_like` (`%` = any text), `regex`. Groups can be nested. |
| `includeRankingDistribution` | `false` | Adds 24 columns: keywords ranking in positions 1, 2–3, 4–10 … 91–100 (organic and paid). |

A run can combine at most **8 filter conditions** (the date window counts as 2; the TLD list, each keyword, each exclusion, each minimum and each raw condition count as 1). Keywords are combined with OR; when they do not all fit, the Actor automatically merges them into one pattern (1 condition) and says so in the log. For anything else over the limit it tells you clearly which filters to remove.

### Daily monitoring (only new domains)

1. Set your filters, tick **Only new domains since the last run** and save the input as a task.
2. In the task, click **Schedule** and choose e.g. *daily at 06:00*.
3. Every run returns only domains that this search has not delivered before. The first run returns the full list.

How it works:

- **Repeats are not fetched at all.** The Actor remembers how far earlier complete runs got and only asks for the part that is new:
  - **Expiring windows** (for example *Expiring within 30 days*, run daily): only domains whose expiration date falls after the end of the window already covered, which is the newly added day. The first run (no history yet) fetches the full window.
  - **Recently expired windows** (a past date range or *Registration status: expired*): only domains whose registration record changed since the last complete run. This uses one of the 8 filter conditions; if none is free, the full window is fetched and repeats are skipped instead.
  - A one-hour overlap catches boundary cases. The few domains in that overlap are removed by the list of domains already delivered, so they are never delivered twice (each costs only the $0.0015 repeat check).
- **The list of domains already delivered** is saved after every page in a named key-value store, `expiring-domains-monitor`, in your own Apify account. The history is kept per filter set as you wrote it, so a relative window keeps its history even though its dates move every day. Changing a filter starts a new history. Set `monitoringName` to share one list of delivered domains across filter changes or to run several independent feeds; the "already covered" point is still kept separately for each filter set.
- **Request size:** when many domains are repeats, the Actor asks for more domains per request, but never more than the domains still needed plus the few extra that request's own fees cover (about 6 + a third of the domains still needed). A generous *Maximum domains* therefore means fewer list requests.
- **What counts as a complete run:** the list ended, or the run stopped at *Maximum domains*. If a run stops at *Maximum domains*, the rest of that window is not revisited; raise *Maximum domains* or sort by expiration date ascending (then the next run continues from the last expiration date checked). Runs stopped by the maximum cost or by *Maximum domains to check* (a safety cap) do not move the covered point, so nothing is lost.
- A domain that has not appeared in any run for 120 days is forgotten, so a domain that is renewed and expires again a year later is reported again.
- `RUN_SUMMARY.onlyNewDomains` shows the window actually fetched (`fetchedWindow`), the covered point after the run (`coveredAfterRun`), and the new and skipped counts.

A one-day slice also works well for large feeds, for example:

```json
{
    "expiresFrom": "+30 days",
    "expiresTo": "+30 days",
    "tlds": ["com", "net"],
    "minReferringDomains": 10,
    "maxResults": 5000,
    "onlyNewDomains": true
}
```

### Output example

Example output: one row of a list of `.com` domains expiring within 30 days (the same row as in the field table above).

```json
{
    "id": "d8ecef93d64672f5",
    "position": 1,
    "domain": "harborviewbakery.com",
    "tld": "com",
    "name": "harborviewbakery",
    "expiresAt": "2026-10-12T17:22:41.000Z",
    "daysUntilExpiration": 14,
    "isRegistered": true,
    "createdAt": "2011-10-12T17:22:41.000Z",
    "domainAgeYears": 15,
    "domainScore": 73,
    "lastChangedAt": "2025-10-13T08:14:02.000Z",
    "whoisUpdatedAt": "2026-09-20T04:31:10.000Z",
    "firstSeenAt": "2020-10-07T00:00:00.000Z",
    "registrar": "GoDaddy.com, LLC",
    "statusCodes": ["client_transfer_prohibited"],
    "referringDomains": 214,
    "referringMainDomains": 187,
    "referringPages": 2911,
    "backlinks": 3870,
    "dofollowBacklinks": 2645,
    "backlinksUpdatedAt": "2026-09-04T18:57:50.000Z",
    "organicKeywords": 1180,
    "organicTraffic": 420,
    "organicTrafficValue": 612.88,
    "paidKeywords": 0,
    "paidTraffic": 0,
    "paidTrafficValue": 0,
    "scrapedAt": "2026-09-28T15:51:53.371Z"
}
```

### Pricing

**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. You are **charged per list request made, even if nothing (new) is found**, because the domain database is queried either way; plus one fee per domain you receive. With *Only new domains*, domains delivered in an earlier run are skipped and cost $0.0015 each (they were fetched and checked for you), which is less than a delivered domain.

| Event | Price (every plan) | When |
|---|---|---|
| Domain list request | $0.16 | Once per list request made (up to 1,000 domains per request), even if it returns no domain or nothing new |
| Domain | $0.00199 | Per delivered domain |
| Domain checked for minimum score | $0.0018 | Only with *Minimum domain score*: per fetched domain skipped because it is below your minimum. With default settings only in the rare case described below (a request that got no answer). |
| Already-delivered domain skipped | $0.0015 | Only with *Only new domains* (or a repeat inside a run sorted by domain score): per fetched domain skipped because it was delivered before. Never charged with default settings. |

**1,000 domains = $0.16 + 1,000 × $0.00199 = $2.15.**

The price is the same on every Apify plan (Free, Bronze, Silver, Gold, Platinum and Diamond). There is no start fee and no subscription. 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 on this page are event fees only. The score-check and repeat-check fees exist because those domains are fetched and checked before they are skipped; with default settings they are never charged. A search that matches nothing still costs one list request ($0.16).

Worked examples:

- **100 domains** expiring this month: $0.16 + 100 × $0.00199 = **$0.36**.
- **1,000 domains**: $0.16 + 1,000 × $0.00199 = **$2.15**.
- **10,000 domains** (10 list requests): 10 × $0.16 + 10,000 × $0.00199 = **$21.50**.
- **Top 500 by domain score** (`sortBy: "domainScore"`, 500 domains, one list request): $0.16 + 500 × $0.00199 = **$1.16**.
- **500 domains with `minDomainScore: 50`**, when about half of the checked domains pass (about 1,000 checked in 3 list requests): 3 × $0.16 + 500 × $0.0018 + 500 × $0.00199 = **$2.38**. Without the minimum score, 500 domains cost $1.16.
- **Daily monitoring**, 150 new domains per day on average and 20 overlapping repeats skipped (one list request per run): $0.16 + 150 × $0.00199 + 20 × $0.0015 = **$0.49 per day**, about **$14.66 per month** (30 days) (plus platform usage).
- **Daily monitoring on a day with nothing new** (one list request returns 40 domains, all delivered before): $0.16 + 40 × $0.0015 = **$0.22**.
- **A search that matches nothing**: one list request = **$0.16**.

Set **Maximum cost per run** on the run to cap spending. The Actor only requests as many domains as your budget covers and stops cleanly.

### Integrations

- **Scheduling / monitoring**: run it every morning with *Only new domains* on and get only the domains you have not seen yet (Apify Schedules).
- **API and MCP**: start runs and read results from your own code with the Apify API or `apify-client`, or let an AI agent call the Actor through the Apify MCP server.
- **Google Sheets, Make, Zapier, n8n**: send the dataset to a sheet or your CRM with the ready-made Apify integrations.
- **Webhooks**: get notified when a run finishes and fetch the dataset via API.
- **Exports**: CSV, Excel, JSON, XML, RSS from the dataset.

### FAQ

**Is there a limit on the Apify Free plan?** Yes: 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.

**Is it legal?** The Actor returns public domain registration data (WHOIS dates, registrar, status) and aggregated SEO metrics. It contains no personal registrant contact data. Always follow the rules of the registries and marketplaces you buy from.

**Is Apify platform usage included?** No. Apify platform usage (compute) is billed separately by Apify, on top of the event fees. At the default 256 MB memory a small run typically uses about $0.001–$0.005; very large runs use more. Each run's usage is shown in Apify Console.

**How fresh is the data?** The domain database is updated continuously. Registration data shows its last update in `whoisUpdatedAt`, backlink metrics in `backlinksUpdatedAt`. Expiration dates can change when an owner renews, so check availability at your registrar before buying.

**Why did I get fewer results than "Maximum domains"?** Fewer domains match your filters (see `totalMatchingDomains` in RUN_SUMMARY), your maximum cost per run was reached, or, with *Only new domains*, most matching domains were already delivered earlier (see `repeatsSkipped` in RUN_SUMMARY). Widen the date window, add TLDs or keywords, or lower the minimums.

**How do I get only new domains every day?** Tick *Only new domains since the last run*, save the input as a task and add a daily schedule. See [Daily monitoring](#daily-monitoring-only-new-domains). Repeats are skipped and cost $0.0015 each; each list request costs $0.16 even if nothing new is found.

**How do I control my budget?** Set **Maximum cost per run** when starting the run or in the task. The Actor only requests as many domains as the budget covers, stops cleanly and records `stoppedByBudget: true` in RUN_SUMMARY.

**What is the difference between "expiring" and "expired"?** Expiring domains are still registered and have an expiration date in your window. Expired domains (`registrationStatus: "expired"`) had their registration end; they stay in the database only for a short time after dropping.

**Does an expiring domain become available?** Not necessarily. Many owners renew at the last minute, and many registrars keep domains in a grace or auction period (see `statusCodes`). Treat the list as candidates to monitor or backorder.

**Which export formats are available?** JSON, CSV, Excel, XML, RSS and HTML from the dataset, in the Console or via the API. The table views *Expiring domains*, *SEO metrics* and *Registration details* export only their columns.

**How do I get more than 10,000 domains, and which fields can I sort and filter on?** Set `maxResults` up to 100,000; the Actor pages through the list automatically (`startOffset`, max 10,000, is only for skipping the start). Every database field can be used in sorting and raw filters, with the same names as in the output. Columns the Actor calculates itself (`domainScore`, `id`, `position`, `name`, `daysUntilExpiration`, `domainAgeYears`) are not database fields; filter and sort by `domainScore` with *Minimum domain score* and *Sort by: Domain score*.

**What does a scheduled "only new" run cost when nothing is new?** With the defaults (*Maximum domains* 100, *Maximum domains to check* left empty = 200), at most 200 domains are checked: 2 list requests × $0.16 + 200 × $0.0015 = **$0.62** on every plan. Set *Maximum domains to check* yourself to allow more (each extra 1,000 checked domains adds about $1.50 plus list requests), or set **Maximum cost per run** as a hard cap.

**Why was I charged when a request failed with "Network problem"?** Very rarely a list request gets no answer although the database may already have processed (and billed) it, including every requested domain. It is never resent (that could bill it twice); instead it is charged like an answered request that delivered nothing: $0.16 plus $0.0015 per requested domain (at most $1.66 for a 1,000-domain request, always within your maximum cost per run). `RUN_SUMMARY` shows `unansweredRequestsCharged`.

**Why was I charged $0.16 when I got no domains (or no new ones)?** Every list request queries the domain database, which costs us money whether or not something matches, so each list request made is charged ($0.16), even if nothing (new) is found. With *Only new domains*, each fetched domain that was already delivered costs $0.0015. Example: a scheduled run whose request returns 40 domains, all delivered before, costs $0.16 + 40 × $0.0015 = $0.22. `RUN_SUMMARY` shows `pagesCharged`, `repeatsSkipped` and `repeatChecksCharged`. To keep this low, schedule less often or use a narrower window.

**Why does "Minimum domain score" cost extra?** The score is calculated from the fetched data, so domains below your minimum have to be fetched and checked before they can be skipped. Each of them costs $0.0018, less than a delivered domain ($0.00199). Database minimums such as *Minimum referring domains* are free and filter before fetching, so use them first and add the score on top. Set **Maximum cost per run** or *Maximum domains to check* to cap spending.

**How is the domain score calculated, and can I trust it?** It is a fixed formula of four output columns, shown in [Domain score](#domain-score), so you can recompute it in a spreadsheet. It ranks link, traffic and age signals together; it does not check spam, topic relevance or link quality, so review the top domains before buying.

**Do I need an API key, and where do I get help?** No key or account elsewhere, only your Apify account. For questions, bugs or feature requests, open an issue on the Actor's **Issues** tab.

### Not included

- **Registrant contact details** (names, emails, phones): not collected, for privacy reasons.
- **Auction, backorder or buy-now prices and live availability checks at a registrar**: the list shows registration data and status codes; check availability at your registrar before buying.
- **Third-party authority scores** (for example domain rating or trust flow style metrics) and archive history: `domainScore` is our own documented formula instead.
- **The full backlink list of a domain**: use [Backlink Checker](https://apify.com/cheapapi/backlink-checker).
- **A detailed SEO profile of one domain** (ranked keywords, competitors, traffic history): use [Domain SEO Analyzer](https://apify.com/cheapapi/domain-seo-analyzer).
- **Keyword ideas and search volumes** for naming a domain: use [Keyword Research Tool](https://apify.com/cheapapi/keyword-research-tool).
- **Who ranks in Google for a term** (to judge a niche): use [Google SERP Scraper](https://apify.com/cheapapi/google-serp-scraper).

### Limitations

- `domainScore` is calculated after fetching: *Minimum domain score* charges $0.0018 per skipped domain and can reach *Maximum domains to check* before the list is full, and *Sort by: Domain score* allows at most 10,000 domains per run.
- At most 8 filter conditions and 3 sort rules per run; `startOffset` max 10,000; `maxResults` max 100,000 per run.
- The database does not contain every registered domain; coverage is best for domains that have some web presence (links or rankings). Brand-new domains without any footprint may be missing.
- Expired domains stay in the database only briefly after they drop, so date ranges far in the past return few rows.
- Traffic and keyword numbers are estimates for Google; backlink metrics are from our own link index and differ from other SEO tools.
- Expiration dates reflect the last registration update and may already be renewed at the registry.
- No single-domain WHOIS lookups: the Actor is built for filtered lists of many domains.
- *Only new domains* remembers domains per Apify account and per filter set (or monitoring name). Deleting the `expiring-domains-monitor` store resets it. Only domains that newly enter the window (or, for recently expired windows, whose registration record changes) are fetched; a domain added to the database later with an expiration date in an already covered range is not picked up. `maxScannedDomains` caps how many domains one run checks (default 200 with *Only new domains*, so a run with nothing new costs at most about $0.62 with default settings).
- The list request fee ($0.16) applies to every list request made, even one that returns nothing (new), so very small lists cost more per domain.

### Privacy and personal data

The output contains domain-level data only: registration dates, registrar, status codes and SEO metrics. It contains no registrant names, emails, phone numbers or addresses. If you combine the list with other sources to contact domain owners, you are responsible for complying with GDPR, CAN-SPAM and similar laws.

# Actor input Schema

## `expiringWithinDays` (type: `integer`):

List domains whose registration expires between today and this many days from now. Use 0 together with the date range in the advanced section (for example to find domains that expired recently).

## `tlds` (type: `array`):

Domain extensions to include, without the dot (com, net, org, io, co.uk …). Leave empty for all TLDs.

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

Leave empty to include all domain names. Only domains that contain at least one of these texts, e.g. "shop", "store", "crypto" (any keyword may match). Letters, digits, hyphens and dots. Each keyword counts as one filter condition; if they do not fit into the 8-condition limit, they are combined into one pattern automatically. Change how they match under Advanced → Keyword match.

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

How many domains to return (1–100,000). Each 1,000 domains is one list request.

## `onlyNewDomains` (type: `boolean`):

For scheduled runs: skip every domain this search already delivered in an earlier run. Skipped repeats are not saved again but cost $0.0015 each (they had to be fetched and checked), and every list request costs $0.16 even if nothing new is found. Request sizes adapt to the share of new domains, so repeats stay cheap. Unless you set "Maximum domains to check", at most 200 domains (or "Maximum domains" if higher) are checked per run, so a run where nothing is new costs at most about $0.62. The history is kept per filter set (same filters = same history) in your account's key-value store "expiring-domains-monitor".

## `monitoringName` (type: `string`):

Optional. Give the history a name to keep it when you change filters, or to run several independent feeds. Leave empty to keep one history per filter set.

## `maxScannedDomains` (type: `integer`):

Only with "Only new domains" or "Minimum domain score": how many domains of the sorted list are checked at most to find ones to deliver (100–100,000). Leave empty for automatic: with "Only new domains" 200 (or "Maximum domains" if higher), a protective default so a run where nothing is new costs at most about $0.62; with only a minimum score, twice "Maximum domains", at least 1,000.

## `minReferringDomains` (type: `integer`):

Only domains linked from at least this many different websites. The best single quality signal for expired domains. Leave empty for no minimum.

## `minBacklinks` (type: `integer`):

Only domains with at least this many backlinks in total. Leave empty for no minimum.

## `minDofollowBacklinks` (type: `integer`):

Only domains with at least this many dofollow backlinks. Leave empty for no minimum.

## `minReferringMainDomains` (type: `integer`):

Only domains linked from at least this many different root domains (subdomains counted once). Leave empty for no minimum.

## `minOrganicTraffic` (type: `integer`):

Only domains with at least this estimated monthly organic search traffic (visits from Google). Leave empty for no minimum.

## `minOrganicKeywords` (type: `integer`):

Only domains that still rank in Google for at least this many keywords. Leave empty for no minimum.

## `minOrganicTrafficValue` (type: `number`):

Only domains whose organic traffic would cost at least this much per month as ads. Leave empty for no minimum.

## `minDomainScore` (type: `integer`):

Only domains whose domainScore (0–100, calculated by the Actor from referring domains, backlinks, organic traffic and domain age; formula in the README) is at least this value. Applied after fetching, so it does not use one of the 8 filter conditions. Each fetched domain skipped for a lower score costs $0.0018 (it had to be fetched and checked); every list request pays the $0.16 request fee, even if no domain passes. Use free database minimums such as "Minimum referring domains" first. Leave empty for no minimum and no extra fee.

## `expiresFrom` (type: `string`):

Start of a custom expiration date range (YYYY-MM-DD or relative like "-30 days"). When set (with or without "Expires to"), it replaces "Expiring within (days)". Use a past date to find domains that already expired.

## `expiresTo` (type: `string`):

End of the custom expiration date range (inclusive). YYYY-MM-DD or relative like "+90 days".

## `registrationStatus` (type: `string`):

Filter by whether the registration is still active. Expired domains stay in the database only for a short time after they drop.

## `minDomainAgeYears` (type: `number`):

Only domains first registered at least this many years ago (by creation date). Leave empty for any age.

## `keywordMatch` (type: `string`):

How the keywords are matched against the domain name. With "Regular expression", each keyword is a pattern and any of them may match.

## `excludeKeywords` (type: `array`):

Skip domains containing any of these texts. Each entry counts as one filter condition.

## `excludeHyphens` (type: `boolean`):

Skip domains that contain a hyphen.

## `maxNameLength` (type: `integer`):

Only domains whose name (the part before the first dot) has at most this many characters. Leave empty for any length.

## `registrar` (type: `string`):

Only domains whose registrar name contains this text (2–100 characters, no % or \_), e.g. "GoDaddy" or "NameCheap".

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

Main sort order of the list. "Domain score" is calculated by the Actor: the database picks the domains by referring domains, then the Actor re-orders them by score (the direction below applies), which allows at most 10,000 domains per run.

## `sortDirection` (type: `string`):

Direction of the main sort.

## `additionalSortRules` (type: `array`):

Up to 2 tie-breaker rules as "field,direction", e.g. "expiresAt,asc" or "organicTraffic,desc". Any numeric or date output field can be used.

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

Skip this many domains of the sorted list first (0–10,000). Use it to continue a previous run with the same filters.

## `rawFilters` (type: `array`):

Extra conditions in the form \["field", "operator", value], joined with "and" / "or" and nestable, e.g. \[\["organicTraffic", ">", 100], "or", \["referringDomains", ">=", 50]]. Fields: any output field (domain, tld, registrar, isRegistered, statusCodes, createdAt, lastChangedAt, expiresAt, whoisUpdatedAt, firstSeenAt, backlinks, dofollowBacklinks, referringDomains, referringMainDomains, referringPages, backlinksUpdatedAt, organicKeywords, organicTraffic, organicTrafficValue, paidKeywords, paidTraffic, paidTrafficValue, organicPos1 … organicPos91to100, paidPos1 … paidPos91to100). Operators: =, <>, <, <=, >, >=, in, not_in, like, not_like (% = any text), regex. Dates accept YYYY-MM-DD. Combined with all other filters using "and"; max 8 conditions per run in total.

## `includeRankingDistribution` (type: `boolean`):

Add 24 columns with the number of keywords ranking in positions 1, 2–3, 4–10 … 91–100 (organic and paid).

## Actor input object example

```json
{
  "expiringWithinDays": 30,
  "tlds": [
    "com",
    "net",
    "org"
  ],
  "keywords": [],
  "maxResults": 100,
  "onlyNewDomains": false,
  "registrationStatus": "any",
  "keywordMatch": "contains",
  "excludeKeywords": [],
  "excludeHyphens": false,
  "sortBy": "referringDomains",
  "sortDirection": "desc",
  "additionalSortRules": [],
  "startOffset": 0,
  "includeRankingDistribution": false
}
```

# Actor output Schema

## `domains` (type: `string`):

No description

## `allFields` (type: `string`):

No description

## `runSummary` (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 = {
    "expiringWithinDays": 30,
    "tlds": [
        "com",
        "net",
        "org"
    ],
    "maxResults": 100
};

// Run the Actor and wait for it to finish
const run = await client.actor("cheapapi/expiring-domains-finder").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 = {
    "expiringWithinDays": 30,
    "tlds": [
        "com",
        "net",
        "org",
    ],
    "maxResults": 100,
}

# Run the Actor and wait for it to finish
run = client.actor("cheapapi/expiring-domains-finder").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 '{
  "expiringWithinDays": 30,
  "tlds": [
    "com",
    "net",
    "org"
  ],
  "maxResults": 100
}' |
apify call cheapapi/expiring-domains-finder --silent --output-dataset

```

## MCP server setup

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

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/F6TLkrCwLNGvrmRdf/builds/47fmcRfQmV1ocMF7t/openapi.json
