# DNSTwister — Typosquatting & Phishing Detector (`khadinakbar/dnstwister-typosquatting-detector`) Actor

Find registered typo, homoglyph, combo-squat, subdomain, and TLD variants with DNS, MX, HTTP, TLS, and heuristic phishing evidence for authorized brand monitoring.

- **URL**: https://apify.com/khadinakbar/dnstwister-typosquatting-detector.md
- **Developed by:** [Khadin Akbar](https://apify.com/khadinakbar) (community)
- **Categories:** Developer tools, Automation, MCP servers
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $40.00 / 1,000 registered candidate domains

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## DNSTwister Typosquatting & Phishing Detector

DNSTwister helps brand-protection teams, SOC analysts, registrars, and security researchers inventory registered lookalike domains. Give it an authorized registrable domain; it generates bounded typo, keyboard, homoglyph/IDN, combosquatting, subdomain, and TLD variants, checks DNS/MX registration, and returns ranked dataset rows with optional HTTP/TLS and page-similarity evidence.

### Best fit and scope

Choose this Actor when the decision is **which registered domain variants deserve human review first**. It works well for periodic brand monitoring, a newly reported lookalike domain, defensive phishing triage, and a pre-takedown evidence pass. It is designed as a focused standalone workflow: the dataset is the handoff to an analyst, ticketing system, registrar process, or a separate website-health workflow.

The network layer makes bounded GET requests and reads certificate metadata. It never submits forms, logs in, follows private-address targets, or stores page bodies. The phishing flag is a heuristic triage signal; analysts should confirm ownership, content, redirects, certificate details, and applicable policy before taking action.

Candidate families are informed by the open-source [`dnstwist` project](https://github.com/elceef/dnstwist), which documents typo, homograph, DNS, HTTP, MX, and certificate-oriented discovery techniques. This Actor is an independent Node.js implementation with explicit bounds and Apify dataset/PPE contracts; it does not bundle the Python project.

### Workflow: from an authorized seed to a review queue

1. A brand owner supplies `example.com` and selects the breadth and probe caps.
2. The Actor generates and deduplicates variants, then resolves A, AAAA, MX, NS, and CNAME records.
3. Registered candidates are ranked using label similarity, variant family, DNS/MX evidence, optional page signals, and TLS state.
4. The overview, DNS, and phishing dataset views give the analyst a short queue with provenance and collection time.
5. The analyst confirms the evidence and passes selected rows into the organization’s incident, registrar, or monitoring workflow.

### Quick start

The required input is one public registrable domain that you are authorized to monitor. A URL is accepted and reduced to its registrable domain.

```json
{
  "domain": "example.com",
  "maxCandidates": 300,
  "maxResults": 100,
  "maxHttpProbes": 100,
  "fuzzers": ["omission", "insertion", "homoglyph", "combosquatting", "tld-swap"],
  "checkHttp": true,
  "checkTls": true,
  "concurrency": 20
}
```

| Input | Meaning | Default |
| --- | --- | --- |
| `domain` | Authorized registrable seed domain or URL | required |
| `maxCandidates` | Generated variants sent to DNS resolution, from 1 to 2,000 | `300` |
| `maxResults` | Registered rows persisted and billed, from 1 to 500 | `100` |
| `maxHttpProbes` | Registered candidates receiving network inspection; `0` gives DNS/MX-only output | `100` |
| `fuzzers` | Selected variant families; duplicates are removed | all families |
| `checkHttp` | Enable bounded GET page inspection | `true` |
| `checkTls` | Capture certificate metadata for selected network targets | `true` |
| `concurrency` | Parallel DNS/network work, from 1 to 50 | `20` |

For a fast inventory, start with `maxCandidates: 100`, `maxHttpProbes: 25`, and the typo-oriented families. For a broad defensive review, raise the candidate cap while keeping the network cap aligned with analyst capacity.

### Data you receive

One dataset row represents one registered candidate. Unregistered variants are counted in `RUN_SUMMARY` and are not emitted or billed.

| Field | Description |
| --- | --- |
| `domain`, `unicodeDomain`, `url` | ASCII candidate, optional Unicode rendering, and review URL |
| `fuzzer`, `editDistance`, `similarityScore` | Generation provenance and label similarity |
| `dnsStatus`, `registered`, `aRecords`, `aaaaRecords`, `mxRecords`, `nsRecords`, `cnameRecords` | Registration and DNS evidence |
| `httpStatus`, `httpFinalUrl`, `httpResponded`, `redirectHosts` | Bounded GET response and public redirect chain |
| `pageTitle`, `brandMentioned`, `credentialFormDetected`, `loginKeywordDetected` | Page-level triage signals |
| `tlsAuthorized`, `tlsIssuer`, `tlsSubject`, `tlsValidFrom`, `tlsValidTo`, `tlsDaysUntilExpiry` | Certificate metadata |
| `htmlSimilarityScore` | Visible-text similarity to the seed page when both pages are readable |
| `phishingSignals`, `potentialPhishing`, `riskScore`, `riskLevel` | Explainable heuristic triage output |
| `checkedAt`, `warnings` | Collection timestamp and row-specific caveats |

Example persisted row:

```json
{
  "seedDomain": "example.com",
  "domain": "examp1e-login.com",
  "unicodeDomain": null,
  "url": "https://examp1e-login.com/",
  "fuzzer": "combosquatting",
  "editDistance": 7,
  "similarityScore": 58,
  "dnsStatus": "registered",
  "registered": true,
  "aRecords": ["203.0.113.10"],
  "aaaaRecords": [],
  "mxRecords": ["10 mail.examp1e-login.com"],
  "nsRecords": ["ns1.example.net"],
  "cnameRecords": [],
  "hasMx": true,
  "httpStatus": 200,
  "httpFinalUrl": "https://examp1e-login.com/",
  "httpResponded": true,
  "httpError": null,
  "redirectHosts": [],
  "pageTitle": "Account verification",
  "brandMentioned": true,
  "credentialFormDetected": true,
  "loginKeywordDetected": true,
  "tlsAuthorized": true,
  "tlsAuthorizationError": null,
  "tlsIssuer": "Example Certificate Authority",
  "tlsSubject": "examp1e-login.com",
  "tlsValidFrom": "2026-08-01T00:00:00.000Z",
  "tlsValidTo": "2026-11-01T00:00:00.000Z",
  "tlsDaysUntilExpiry": 63,
  "htmlSimilarityScore": 78,
  "phishingSignals": ["registered-domain", "mail-exchange", "credential-form", "seed-brand-text", "similar-page", "potential-phishing"],
  "potentialPhishing": true,
  "riskScore": 91,
  "riskLevel": "critical",
  "checkedAt": "2026-08-30T00:00:00.000Z",
  "warnings": []
}
```

`OUTPUT` contains the terminal outcome and dataset pointer. `RUN_SUMMARY` contains candidate counts, effective bounds, warnings, validation diagnostics, and the billed event count. Terminal outcomes are `COMPLETE`, `PARTIAL`, `VALID_EMPTY`, `INVALID_INPUT`, `UPSTREAM_FAILED`, and `CONFIG_ERROR`, so an agent can distinguish a useful empty inventory from an interrupted run.

### Pricing and platform usage

This Actor uses Pay per event: `$0.04` for each fully validated registered-domain row persisted to the dataset, plus the Actor Start event. A run that persists 50 rows therefore has an Actor-level estimate of `$2.00` plus the start event. DNS-only and unregistered candidates remain outside result-event billing. Apify platform usage is paid by the user and is separate from these PPE events; the live Pricing tab is the current source of truth for the Actor’s configured prices and usage presentation.

The `maxResults` setting is the result-event cost cap. The Actor reports its effective event count and expected PPE amount in `RUN_SUMMARY`, while Apify remains the billing source of truth.

### AI-agent and MCP prompt card

Hosted Apify MCP can call the Actor as a bounded read-only investigation. A useful request is:

> For the authorized domain `example.com`, generate up to 150 candidates, inspect up to 30 registered candidates, return the phishing dataset view, and summarize outcome, dataset readback, candidate provenance, collection time, cost, scope, pagination, warnings, and any rows needing human review.

Agents should pass the exact seed domain, keep candidate and network caps explicit, read `OUTPUT` and `RUN_SUMMARY`, then retrieve the dataset for row-level provenance. Treat `potentialPhishing` as triage, preserve `fuzzer`, DNS, HTTP/TLS, and timestamp fields, and request another bounded page or run when a result set is intentionally capped.

### Scope and best results

- Keep the seed to a public registrable domain and written monitoring authorization.
- Use typo and homoglyph families for brand inventory; add combosquatting and TLD families when the review includes landing-page impersonation.
- Use `maxHttpProbes` as the analyst’s network-inspection budget. Rows outside that cap retain DNS evidence and clearly state the inspection scope.
- Treat DNS observations, certificate state, redirect destinations, and page text as time-sensitive evidence; rerun before a consequential decision.
- Compare results with the organization’s own domain inventory and incident records before contacting a registrar, host, or site owner.

### Focused standalone workflow

This Actor is designed as a focused standalone workflow for a bounded domain-evidence handoff.

### Builder's note

I designed this as a provider-free Actor because registration and first-pass phishing triage are strongest when the evidence stays close to DNS, public HTTP responses, and certificate metadata. The implementation keeps candidate generation local, validates the complete row before the coupled dataset/PPE write, and stores only bounded page signals rather than response bodies. That makes the output useful to both an analyst and an agent while keeping network scope visible.

### API example

Replace `YOUR_APIFY_TOKEN` with an Apify token that has permission to run the Actor. The endpoint returns a run; use the returned run ID and dataset link for readback.

```bash
curl -X POST \
  'https://api.apify.com/v2/acts/khadinakbar~dnstwister-typosquatting-detector/runs?token=YOUR_APIFY_TOKEN' \
  -H 'Content-Type: application/json' \
  --data '{
    "domain": "example.com",
    "maxCandidates": 100,
    "maxResults": 25,
    "maxHttpProbes": 10,
    "checkHttp": true,
    "checkTls": true,
    "concurrency": 10
  }'
```

### Responsible use

Run this Actor only against domains and public services you are authorized to monitor, and follow applicable law, site terms, registrar policies, and internal security procedures. The Actor performs discovery and evidence collection; human review remains the decision boundary for attribution, takedown, notification, or enforcement.

# Actor input Schema

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

Public registrable domain you are authorized to monitor, such as example.com. A URL is accepted, but its path is ignored and the registrable domain is used as the seed.

## `maxCandidates` (type: `integer`):

Maximum number of generated variants to DNS-check. Lower this for a quick triage; increase it for broader brand monitoring. Bounded to 1–2,000.

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

Maximum number of registered candidate rows to persist and bill. Results are ranked by heuristic risk before this cap is applied. Bounded to 1–500.

## `maxHttpProbes` (type: `integer`):

Maximum number of registered candidates that receive HTTP and optional TLS inspection. Set 0 for DNS/MX-only results. This cap prevents an unexpectedly broad network scan.

## `fuzzers` (type: `array`):

Select the variant families to generate. The default covers common typos, keyboard mistakes, homoglyph/IDN substitutions, combosquatting, subdomains, and TLD swaps. Duplicate candidates are deduplicated before DNS checks.

## `checkHttp` (type: `boolean`):

When true, make a bounded GET request to each selected candidate and inspect only response metadata plus a truncated HTML body for title, login-language, credential-form, brand-text, and similarity signals. No forms are submitted.

## `checkTls` (type: `boolean`):

When true, capture safe TLS certificate metadata for selected candidates: authorization state, issuer, subject, validity dates, and expiry interval. Certificate bodies and private keys are never stored.

## `concurrency` (type: `integer`):

Number of bounded DNS or network checks allowed in parallel. Keep this moderate for respectful monitoring and shared environments. Bounded to 1–50.

## Actor input object example

```json
{
  "domain": "example.com",
  "maxCandidates": 300,
  "maxResults": 100,
  "maxHttpProbes": 100,
  "fuzzers": [
    "addition",
    "bitsquatting",
    "cyrillic",
    "homoglyph",
    "hyphenation",
    "insertion",
    "omission",
    "plural",
    "repetition",
    "replacement",
    "subdomain",
    "transposition",
    "vowel-swap",
    "tld-swap",
    "combosquatting",
    "various"
  ],
  "checkHttp": true,
  "checkTls": true,
  "concurrency": 20
}
```

# Actor output Schema

## `resultsDataset` (type: `string`):

No description

## `downloadCsv` (type: `string`):

No description

## `outputJson` (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 = {
    "domain": "example.com"
};

// Run the Actor and wait for it to finish
const run = await client.actor("khadinakbar/dnstwister-typosquatting-detector").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 = { "domain": "example.com" }

# Run the Actor and wait for it to finish
run = client.actor("khadinakbar/dnstwister-typosquatting-detector").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 '{
  "domain": "example.com"
}' |
apify call khadinakbar/dnstwister-typosquatting-detector --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,khadinakbar/dnstwister-typosquatting-detector"
        }
    }
}

```

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/MC4jhHqHDENyPrLfn/builds/8eHGT5y5CoEKRwAzy/openapi.json
