# Domain Registration Lifecycle - Expiry & RDAP Changes (`maydit/domain-registration-lifecycle-monitor`) Actor

Monitor .com and .net registration expiry, registrar, status and nameserver changes from official RDAP. Clean domain records, saved comparisons, no contact harvesting or API key.

- **URL**: https://apify.com/maydit/domain-registration-lifecycle-monitor.md
- **Developed by:** [Brandt May](https://apify.com/maydit) (community)
- **Categories:** Developer tools
- **Features:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.20 / 1,000 saved observations

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

## Domain Registration Lifecycle — Expiry & RDAP Changes

Turn an important .com or .net domain watchlist into clean registration records, expiry windows and evidence of what changed. Use the official registry response to track renewals, registrar changes, nameserver changes and registration status without collecting personal contact details.

**Start with `example.com`. No API key, proxy setup or account login is required.** Each row includes its authoritative RDAP URL and observation time. The Actor checks IANA's bootstrap directory before querying the supported registry.

### Built for small domain portfolios

| Your workflow | What this Actor supplies |
| --- | --- |
| Agency client operations | Registration expiry, registrar and status in one consistent record per domain |
| Brand and owned-domain monitoring | Saved registrar, nameserver, status and DNSSEC delegation changes |
| Renewal review | Exact registry expiration date, days remaining and 7/30/90-day windows |
| AI research and automation | Bounded JSON records with source identity, previous values and explicit coverage |

This is an **official registration watchlist for .com and .net**, with up to 20 domains per run. It is not a domain availability search, a WHOIS contact database or a replacement for your registrar's billing and renewal notices. Registry expiration can differ from the date a registrar displays to its customer.

### Get a useful first result

1. Enter the domains you care about, such as `example.com` and `example.net`.
2. Keep **Maximum saved rows** at least as large as your domain list.
3. Run the Actor, then open **Domain observations** for data or **Readable run report** for a quick coverage check.

You can paste website URLs too: `https://www.example.com/about` becomes `example.com`. Other subdomains and TLDs are rejected rather than guessed. Internationalized .com/.net names are normalized to their ASCII registration form; the readable Unicode name remains in the output.

```json
{
  "domains": ["example.com"],
  "maxResults": 1
}
```

### Three ready-to-use recipes

#### 1. One registration record

Use the minimal input above to inspect a single domain. A successful observation includes source evidence and available registration fields, even when some registry values are absent.

#### 2. A repeatable portfolio watchlist

```json
{
  "domains": ["example.com", "example.net"],
  "maxResults": 2,
  "comparePrevious": true,
  "snapshotName": "portfolio-registration"
}
```

The first complete run establishes a baseline. Later complete runs identify `changed` or `unchanged` records and include `changedFields` plus the previous values. Use the same domain set and baseline label. Domain order does not matter; changing the set creates a separate baseline.

#### 3. Inspect a .net registration independently

```json
{
  "domains": ["example.net"],
  "maxResults": 1,
  "comparePrevious": false
}
```

This uses the official .net registry route and returns one observation without depending on any prior run. Use it to confirm the second supported TLD before building your own watchlist.

### Deliver changes only

```json
{
  "domains": ["example.com", "example.net"],
  "maxResults": 2,
  "onlyChanges": true,
  "snapshotName": "portfolio-registration"
}
```

This reuses recipe 2's baseline and skips unchanged domains. On the first run of a new baseline it returns all observations. A complete repeat run can correctly save zero rows; `SUMMARY.unchangedSkipped` explains that result. Scheduling a modest daily or weekly watchlist in Apify can support a renewal-review workflow. This Actor does not enable a schedule or send any messages by itself.

### The output contract

| Fields | Meaning |
| --- | --- |
| `domain`, `unicodeDomain`, `tld`, `recordId` | Normalized domain identity and stable record ID |
| `rdapStatus`, `recordMeaning`, `httpStatus` | `registered` for a matching registry record, or `not_found` for a conforming authoritative RDAP 404 |
| `registeredAt`, `expiresAt`, `lastChangedAt` | Available registration event dates, normalized to UTC; absent or ambiguous dates are `null` |
| `daysUntilExpiry`, `expiryWindow`, `expired` | Derived from the registry expiry at observation time; unknown when no unambiguous expiry is supplied |
| `registrar`, `registrarIanaId`, `registrars` | Registrar business identity and IANA ID, when supplied; no registrant or contact records |
| `statuses`, `nameservers`, `dnssecDelegationSigned` | Registry-published status, nameserver names and DNSSEC delegation flag |
| `events`, `ambiguousEventActions` | Retained registration events and date conflicts that could not be reduced to one value |
| `comparisonStatus`, `changedFields`, `previous`, `previousObservedAt` | Comparison result and evidence from the last complete baseline |
| `url`, `sourceUrl`, `registryTermsUrl`, `bootstrapPublishedAt`, `observedAt` | Official source URL, terms, IANA directory date and time of the observation |

`comparisonStatus` is `not_requested`, `new`, `changed` or `unchanged`. The comparison includes registration status, registration/expiration/change dates, registrar identities, statuses, nameservers, DNSSEC delegation and the expiry window. The daily countdown and the registry database refresh timestamp are excluded, so ordinary polling does not generate false changes. Crossing a 90-, 30- or 7-day expiry boundary does produce a change.

An abbreviated row shape is shown below. Values are illustrative; inspect your run for current evidence.

```json
{
  "domain": "example.com",
  "rdapStatus": "registered",
  "expiresAt": "2027-08-13T04:00:00.000Z",
  "expiryWindow": "beyond_90_days",
  "comparisonStatus": "not_requested",
  "changedFields": [],
  "sourceUrl": "https://rdap.verisign.com/com/v1/domain/example.com"
}
```

Nameservers and DNSSEC are reported from the registry record. The Actor does not resolve watched domains, test DNS propagation, validate certificates, or infer that a website is online.

### Coverage you can verify

Every run saves a machine-readable `SUMMARY` and a self-contained `RUN_REPORT`. Check `incomplete`, `sourceCoverage`, `failedSources`, `sourcesNotReached`, `rowsSaved` and `historyUpdated` before treating a run as a full portfolio check.

- A source refusal, malformed response, time limit, request limit or output failure makes the run incomplete. Valid observations may still be saved, with the partial coverage recorded.
- A maximum-charge or result limit can leave part of the input unreached. The old baseline remains unchanged.
- History advances only when every requested domain has a valid observation and every required output row has been acknowledged. Skipped unchanged records still count toward complete source coverage.
- A registry 404 must contain a valid RDAP error response. HTML error pages, access blocks and rate limits never become `not_found` records.
- A domain omitted from an incomplete run is never marked deleted or available. A 404 is only evidence that this registry route returned no record at that time; reserved names and other registration rules can still prevent registration.
- A concurrent baseline change detected before saving prevents the overwrite. Avoid simultaneous runs with the same watchlist and label; storage does not provide a transactional cross-run lock.

If all requested registration observations fail, the Actor fails with diagnostic evidence. If some succeed, the run reports partial coverage. A successful Apify run status alone is not proof that every input was checked.

### Pricing and practical bounds

The configured price is **$0.002 per saved domain observation, plus a $0.00005 Actor Start event per run**. A complete 20-domain run saves at most 20 paid rows, or $0.04005 in Actor events including the start. Check the current Store pricing and your platform plan before running; applicable platform charges are separate.

Each validated registry 200 or conforming registry 404 can produce one row. There are no paid error rows, separate change-event rows or hidden contact-enrichment calls. Unchanged records skipped by `onlyChanges` incur no dataset-item event. The Actor checks the remaining event allowance before its first paid row, and it does not advance history after a charge cap.

Requests are serial and spaced at least one second apart on each origin. The hard bounds are 20 input domains, 25 HTTP requests and a 30–120 second time budget. It stops on a registry rate limit without switching endpoints, proxies or identities. The default time budget is 90 seconds.

### Source scope and responsible use

Source discovery uses [IANA's RDAP DNS bootstrap directory](https://www.iana.org/assignments/rdap-dns). This version accepts only the reviewed HTTPS .com and .net routes at Verisign. If IANA changes those routes, it stops rather than contacting an unreviewed host. It does not follow registrar referrals or redirects.

The IANA directory fetch respects its robots policy. Registration queries use the explicitly supported RDAP API, with bounded response sizes and public-address validation. See [Verisign's RDAP help](https://origin.www1.verisign.com/news-insights/registration-data-access-protocol/help/) and [RDAP terms of use](https://www.verisign.com/legal-center/rdap-terms/). Use a small, legitimate domain watchlist; do not use this tool for high-volume queries or unsolicited marketing.

The output excludes registrant identities, email addresses, phone numbers and postal addresses. Only the registrar's business name and public IANA ID are retained. Source data may be incomplete or delayed, and an expiration timestamp is not a renewal guarantee.

### Use in an AI or data workflow

Give this Actor an explicit .com/.net watchlist. Read `SUMMARY` first, then use the dataset's `sourceUrl`, `observedAt`, `changedFields` and `previous` fields to explain each conclusion. Treat `null` as unavailable or ambiguous, not as a negative answer. Do not turn `not_found` into a purchase or availability recommendation.

Export JSON or CSV from the dataset, or use the Apify API with a saved task. Keep access credentials in your integration's secret settings, not in Actor input. The Actor runs with Limited permissions and requires no external service key.

For input recipes and the wider portfolio, visit [Mayd It data tools](https://mayd-it.com/data-tools/). Use this Actor's Issues tab for reproducible source failures, including the run URL and input scope without credentials.

# Actor input Schema

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

Registered second-level .com or .net names, or website URLs on those domains. Up to20 unique names. www is removed; other subdomains and TLDs are rejected. Empty input uses example.com.

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

One paid row per validated registration observation. Keep this at least as large as the domain list for full coverage and baseline updates. Error responses and skipped unchanged domains are not paid rows.

## `comparePrevious` (type: `boolean`):

Save a complete watchlist baseline, then identify new, changed and unchanged domain records. Expiry, status, registrar, nameservers, DNSSEC delegation and expiry-window transitions are compared.

## `onlyChanges` (type: `boolean`):

Enables comparison and skips unchanged records without a dataset-item charge. The first run emits all observed domains. Source failures, missing inputs and truncated runs never prove a registration change.

## `snapshotName` (type: `string`):

Use the same label and domain watchlist for repeat checks. Labels use1-80 letters, numbers, underscores or hyphens. Run only one writer for a given baseline at a time.

## `maxRunSeconds` (type: `integer`):

Bounded to30–120 seconds. Registry requests are spaced at least one second apart per origin, without retries, proxies or fallback endpoints. Partial runs retain the previous baseline.

## Actor input object example

```json
{
  "domains": [
    "example.com"
  ],
  "maxResults": 20,
  "comparePrevious": false,
  "onlyChanges": false,
  "snapshotName": "default",
  "maxRunSeconds": 90
}
```

# Actor output Schema

## `dataset` (type: `string`):

No description

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

No description

## `report` (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 = {
    "domains": [
        "example.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("maydit/domain-registration-lifecycle-monitor").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": ["example.com"] }

# Run the Actor and wait for it to finish
run = client.actor("maydit/domain-registration-lifecycle-monitor").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": [
    "example.com"
  ]
}' |
apify call maydit/domain-registration-lifecycle-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,maydit/domain-registration-lifecycle-monitor"
        }
    }
}
```

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/LJ1RBDyj0RjxAsvTW/builds/si91rtBgFnKIauy6P/openapi.json
