# Typosquat & Lookalike Domain Detector with Threat Signals (`cottony_brittlegill/domain-typosquat-detector`) Actor

Find typosquatted and lookalike domains impersonating your brand: typo, homoglyph, bitsquat, TLD-swap and combosquat variants, checked against live registries and enriched with registrar, DNS, MX and TLS data to separate real threats from parked domains. Unregistered variants are never charged.

- **URL**: https://apify.com/cottony\_brittlegill/domain-typosquat-detector.md
- **Developed by:** [cottony\_brittlegill](https://apify.com/cottony_brittlegill) (community)
- **Categories:** Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $25.00 / 1,000 registered lookalike founds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#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

## Typosquat & Lookalike Domain Detector

**Find the domains impersonating your brand — and tell which ones are actually dangerous.**

> This is a defensive tool. It is built to monitor for lookalike domains that
> impersonate a brand you own, or that you are explicitly authorized to protect —
> not to explore third-party infrastructure for its own sake. It reads only public
> data: domain registry records (RDAP/WHOIS), DNS answers, and — for a domain that
> already resolves — the TLS certificate it presents on a standard handshake. It
> never sends email, submits a form, logs in, scrapes page content, or otherwise
> interacts with a lookalike site. If a domain doesn't answer, it's recorded as
> unknown, not probed harder.

### What it does

You give it one or more domains you own (a list, or newline/comma-separated text —
`brandDomains` accepts bare domains or full URLs). **A single run scans at most
the first 50 valid brand domains supplied**, in the order you listed them; this
limit is fixed and not exposed as an input, and anything past the 50th is
dropped — with a warning in the run log naming how many were submitted, how
many were scanned, and that the rest were dropped, so it never reads as full
coverage — see [Honest limits](#honest-limits). If you need to cover more than
50 domains, split them across multiple runs. For each one it:

1. Generates lookalike candidates using 13 techniques.
2. Checks which candidates are actually registered (DNS first, then the domain
   registry — see [Pricing](#pricing) for why this order matters for cost).
3. Enriches every registered lookalike with DNS, MX, TLS and registration data,
   and scores how dangerous it currently looks.

| Technique | Example (`yourbrand.com` / `paypal.com`) | What it captures |
|---|---|---|
| `omission` | `paypal.com` → `paypl.com` | a dropped letter |
| `insertion` | `paypal.com` → `playpal.com` | an extra, keyboard-adjacent letter |
| `repetition` | `paypal.com` → `paypall.com` | a doubled letter (double-tapped key) |
| `replacement` | `yourbrand.com` → `tourbrand.com` | one letter swapped for an adjacent key |
| `transposition` | `paypal.com` → `paypla.com` | two adjacent letters swapped |
| `hyphenation` | `paypal.com` → `pay-pal.com` | a hyphen inserted between letters |
| `subdomain` | `yourbrand.com` → `your.brand.com` | the brand split so it reads as a subdomain of an unrelated domain |
| `addition` | `paypal.com` → `paypals.com` | an extra character appended at the end |
| `vowelswap` | `paypal.com` → `peypal.com` | one vowel swapped for another |
| `bitsquatting` | `paypal.com` → `paypan.com` | a single-bit memory/transmission error in one character |
| `homoglyph` | `paypal.com` → `paypa1.com` | a visually similar character or IDN/Unicode lookalike |
| `tldswap` | `yourbrand.com` → `yourbrand.io` | the same brand label under a different TLD |
| `combosquat` | `yourbrand.com` → `yourbrand-login.com` | a common keyword combined with the brand (`login`, `secure`, `account`, `support`, `verify`, `billing`, `help`, `mail` by default) |

`tldswap` runs against a default list of 20 commonly abused TLDs (`com`, `net`,
`org`, `info`, `biz`, `co`, `io`, `app`, `online`, `site`, `shop`, `store`, `xyz`,
`top`, `live`, `club`, `eu`, `de`, `uk`, `it`) unless you supply your own. All 13
techniques run by default; you can restrict to a subset via `variantKinds`. Output
is capped at `maxVariantsPerDomain` (default 400), and the cap is spread evenly
across every enabled technique — so a low cap still gives you homoglyph and
TLD-swap coverage instead of being consumed entirely by, say, `insertion`.

### Why the enrichment matters

A list of "this lookalike is registered" is not actionable on its own — most
registered lookalikes are dormant or parked, not phishing infrastructure. This
actor tells you, per lookalike:

- Does it have **MX records** — can it send or receive mail *as your brand*? This
  is the business-email-compromise vector, and it doesn't require a website at all.
- Does it **resolve to a live host** (or a CNAME)?
- Does it present a **TLS certificate** — a lookalike with a valid cert was built
  to look convincing in a browser, not just squatted and forgotten.
- **How old** is the registration — newly registered lookalikes correlate with an
  active campaign, not background noise.
- Is it merely **parked** with a domain reseller — i.e. inventory for resale, not
  live infrastructure?

Each finding gets a `riskLevel`:

| `riskLevel` | Meaning |
|---|---|
| `active_threat` | Has MX records **and** (resolves live **or** presents a TLS cert) — mail and web infrastructure both active. The highest-concern tier. |
| `mail_capable` | Has MX records but no live web presence or certificate — can still send/receive mail as the lookalike with no website at all. |
| `live_site` | Resolves (or has a CNAME) but has no MX records — a site exists; it can't send mail as the brand. |
| `parked` | Nameservers point at a known parking/resale provider. This is a de-escalating signal and overrides the others — a parked domain's own landing page can otherwise look like a live site. |
| `registered_inactive` | Registered, but no active DNS records were observed at all. Lowest concern; likely dormant or reserved. |
| `dns_incomplete` | The DNS lookup for this domain partially failed (e.g. a timeout on one record type while others answered), and none of the levels above applied from confirmed evidence. **This is not a clean bill of health** — it means we could not fully verify mail/web capability, not that we checked and found nothing. Any positively-observed evidence (a resolved host, a certificate) is still reflected in the score and `riskReasons`; only the *absence* of a record is treated as unverified rather than confirmed. Re-run the scan if you see this. |

Each record also carries a `riskScore` (0–100): it starts at 10 for simply being
registered, then adds 25 for a live resolution or CNAME, 30 for MX records, 20 for
a TLS certificate, and 15 if registered in the last 90 days. A `parked`
classification subtracts 45 (floored at 5) — parking is the single strongest
signal that a listing is inventory, not infrastructure. Every record's
`riskReasons` array spells out exactly which of these applied, so you never have
to reverse-engineer the score.

### Output sample

A real, unedited finding from a run against `apify.com` — `aapify.com`, a
repetition-squat registered about four months earlier via NameCheap, actively
resolving through Cloudflare with MX records and a live Google Trust Services
certificate:

```json
{
  "brandDomain": "apify.com",
  "domain": "aapify.com",
  "registrableDomain": "aapify.com",
  "unicodeDomain": null,
  "variantKind": "repetition",
  "status": "registered",
  "riskLevel": "active_threat",
  "riskScore": 85,
  "riskReasons": [
    "resolves to a live host",
    "has MX records — can send or receive mail as the lookalike",
    "presents a TLS certificate (Google Trust Services)"
  ],
  "registration": {
    "source": "rdap",
    "registrar": "NameCheap, Inc.",
    "createdAt": "2026-03-22T18:44:18Z",
    "expiresAt": "2027-03-22T18:44:18Z",
    "ageDays": 128,
    "domainStatus": [
      "client transfer prohibited"
    ],
    "nameservers": [
      "asa.ns.cloudflare.com",
      "reese.ns.cloudflare.com"
    ]
  },
  "dns": {
    "ns": [
      "reese.ns.cloudflare.com",
      "asa.ns.cloudflare.com"
    ],
    "a": [
      "188.114.96.7",
      "188.114.97.7"
    ],
    "aaaa": [
      "2a06:98c1:3120::7",
      "2a06:98c1:3121::7"
    ],
    "mx": [
      { "priority": 17, "exchange": "route1.mx.cloudflare.net" },
      { "priority": 30, "exchange": "route3.mx.cloudflare.net" },
      { "priority": 42, "exchange": "route2.mx.cloudflare.net" }
    ],
    "txt": [
      "v=spf1 include:_spf.mx.cloudflare.net ~all",
      "google-site-verification=1EpDqhlLgU5l6XrJC71HEn0wEHzmxOKqG7wOGhsyH5M"
    ],
    "cname": [],
    "error": null
  },
  "ssl": {
    "issuer": "Google Trust Services",
    "subjectCN": "aapify.com",
    "validFrom": "2026-07-19T01:52:20.000Z",
    "validTo": "2026-10-17T02:52:11.000Z",
    "daysToExpiry": 79,
    "san": ["aapify.com"],
    "error": null
  },
  "errors": [],
  "checkedAt": "2026-07-29T09:57:14.868Z",
  "chargeable": true
}
```

By default (`onlyRegistered: true`) only records with `status: "registered"` are
written to the dataset — the unregistered majority is not, so you see the
findings you came for. Turn `onlyRegistered` off to get every candidate checked,
registered or not, as a full audit trail; it is never billed differently either
way.

Every record also carries `chargeable` — whether this finding qualifies to be
billed (`status: "registered"`, always). This actor writes each finding to your
dataset *before* charging for it, specifically so a storage failure can never
bill you for a finding you didn't receive (every failure mode falls on us, not
you) — so by the time a record is in your dataset at all, charging for it (if
`chargeable`) has either already happened or is about to. For the authoritative
record of what a run actually charged, use the run's own Apify billing/usage
panel — that is the real ledger; nothing in this dataset needs to duplicate it.

### Pricing

Two pay-per-event charges, and nothing else:

| Event | Price | When it fires |
|---|---|---|
| `actor-start` | $0.005 | Once per run. |
| `registered-lookalike-found` | $0.025 | Once per candidate confirmed **registered** — never for a candidate that isn't, and never for one whose status could not be determined. |

Candidates that turn out **not registered are never charged**, and lookups that
**fail or time out** (`status: "unknown"`) are never charged either — you only pay
for a finding, never for our inability to get an answer.

**How many findings a scan produces depends heavily on how squatted the brand
already is**, and that's a real cost variable worth planning around before you
run at scale. Two things to know before reading the table below: first, the
default `maxVariantsPerDomain: 400` is an *upper bound* on generated variants,
not a target — the generator round-robins across all 13 techniques and simply
stops once it runs out of distinct candidates to produce, which for a short
label like `apify` or `google` happens well before 400 (reaching 400 actually
generated variants needs a brand label around 13+ characters long). Second, the
"registered lookalikes" figures below count every variant with `status:
"registered"`, which (in the default `dns-first` mode) is exactly every variant
that is DNS-delegated — an RDAP/WHOIS hit is not required to bill a finding, DNS
delegation alone is sufficient evidence of registration. Both figures are taken
directly from a live run of the actual shipped generator and pipeline, recorded
in [`docs/fanout-probe-report.md`](docs/fanout-probe-report.md):

| Brand profile | Variants actually generated (`maxVariantsPerDomain: 400`) | Registered lookalikes | Cost |
|---|---|---|---|
| An ordinary brand — about 32% of generated variants come back already registered (measured on `apify.com`) | 224 (the generator exhausts before 400) | 72 | $0.005 + 72 × $0.025 = **$1.805** |
| A heavily-squatted, very well-known brand — about 87% of generated variants come back already registered (measured on `google.com`), because most of its typo-variants are already taken by someone | 256 (the generator exhausts before 400) | 223 | $0.005 + 223 × $0.025 = **$5.580** |

The gap between those two numbers is real, not a rounding error: for a famous
brand, most lookalikes aren't hypothetical — they're already registered by
someone else. Treat both figures as illustrative, not a quote for your brand —
the only hard ceiling on spend is `maxFindings`, described next.

**`maxFindings` (default 500, hard maximum 5000) is an upper bound on spend, not
an estimate of what a run will actually cost.** The scan stops the moment it
reaches `maxFindings`, so a run can never cost more than
`$0.005 + maxFindings × $0.025` — at the default, that ceiling is **$12.505** —
regardless of how many variants you generate or how squatted the brand turns out
to be; most runs, including both measured above, cost substantially less than
their `maxFindings` ceiling. At `maxFindings: 20`, for example, the same formula
caps a run at `$0.005 + 20 × $0.025 = $0.505`. Set it low (e.g. 20–50) for a
first, bounded test run before scaling up.

**If you scan several brand domains in one run, `maxFindings` is one shared
budget across all of them, not a per-brand allowance.** Brands are queued and
checked in the order you listed them; if an early brand in the list is heavily
squatted, it can consume the entire `maxFindings` cap by itself, and the run
will stop before later brands in the same list are examined at all — the log
line at the end reports a total, not a per-brand breakdown, so this is easy to
miss. If you're monitoring one brand you expect to be heavily squatted alongside
others you don't, run the heavily-squatted one in its own run with its own
`maxFindings`, rather than mixing it into a multi-brand list.

`verifyMode: dns-first` (the default) is also a cost control, not just a speed
one: it only spends a registry query on a candidate that is already DNS-delegated,
instead of querying the registry for every single generated variant. Registry
query concurrency is deliberately conservative — `perRegistryConcurrency`
defaults to 2 and maxes out at 3 (`globalConcurrency` defaults to 10, maxing at
30\) — tuned against a real rate limit we hit during live testing at higher
concurrency against a heavily-delegated brand.

### Honest limits

- **A run scans at most 50 brand domains.** If you supply more than 50 valid
  domains (via `brandDomains` and/or `brandDomainsText` combined), only the
  first 50, in the order you listed them, are actually scanned — the rest are
  dropped, with no dataset entry marking them as skipped. The run log does carry
  a warning naming how many were submitted, how many were scanned, and that the
  rest were dropped, so this is visible if you check the log — but there is
  still no per-domain dataset marker, so a dataset-only integration (skipping
  the log) would miss it. This is a fixed internal limit, not an input you can
  raise. If you're monitoring on behalf of multiple clients (MSSP use), keep
  each client's domain list under 50 per run, or run one client per run if any
  single list might exceed it.
- **`dns-first` mode (the default) can miss a lookalike that is registered but
  has no nameservers delegated yet** — it only queries the registry for
  candidates that already resolve or have DNS delegation. `rdap-all` queries the
  registry for every generated variant instead, catching registered-but-not-yet-
  delegated squats, at much higher latency and a much larger registry query
  volume.
- **No registrant contact data is returned.** It is redacted at most registries
  post-GDPR, and this actor doesn't attempt to collect it even where it might
  still be exposed — only registrar, dates, status codes and nameservers.
- **Some ccTLDs have no RDAP service at all**, or (e.g. `.kg`, `.mg`) publish only
  an insecure `http://` endpoint that this actor deliberately won't use for a
  registration lookup; those fall back to WHOIS, which is far less structured
  than RDAP. A small number of ccTLDs have neither a usable RDAP endpoint nor a
  WHOIS referral — those come back as `status: "unknown"` and, per the pricing
  policy above, are never charged.
- **Homoglyph coverage is broad but not exhaustive.** It covers the common
  Latin/Cyrillic/Greek confusables and multi-character substitutions (`rn` for
  `m`, digits for letters, and similar) used in real IDN homograph attacks, but it
  is not a substitute for a registered trademark watch service, which typically
  has broader and continuously updated coverage.
- **Parking detection is pattern-based against a fixed list of about 20 known
  parking/resale providers' nameservers** (Sedo, Bodis, Afternic, HugeDomains and
  similar). It will not catch a parking provider that isn't on that list, or one
  that changes its nameserver pattern.

### Typical uses

- **Brand-protection monitoring** — run on a schedule against your own domains to
  catch new lookalikes as they're registered.
- **Pre-launch name clearance** — before committing to a new product or company
  name, see how many lookalikes of it are already registered.
- **MSSP client reporting** — run per-client and hand over the dataset (or a
  filtered, `riskLevel`-sorted view of it) as part of a recurring security report.
- **Phishing-infrastructure triage** — when a lookalike domain is reported to you,
  use the enrichment (MX, TLS, age, parking status) to decide how urgently it
  needs escalating, instead of treating every registered lookalike the same.

### Run it on a schedule

A single run is a snapshot; new lookalikes get registered continuously, so the
useful mode of operation is recurring, not one-off. In the Apify Console, open
this actor, go to **Schedules**, and create a schedule (e.g. weekly) with the
same input you'd use for a manual run. Each scheduled run only charges for the
lookalikes it actually finds registered at that point in time — there's no extra
cost for scheduling itself, and no need to de-duplicate previous findings
yourself unless you want to; the dataset for each run is independent.

### FAQ

**How can I tell if a typosquat domain is an actual threat or just parked?**
Check `riskLevel`: `active_threat` (MX records plus a live resolve or TLS
certificate) and `mail_capable` (MX only) mean real mail/web infrastructure
is behind the domain; `parked` means its nameservers match a known
parking/resale provider, which is a de-escalating signal that overrides the
others — see the full table above.

**Does this tell me who registered a lookalike domain?**
No — registrant contact data is redacted at most registries post-GDPR, and
this actor doesn't attempt to collect it even where it might still be
exposed. You get registrar, dates, status codes and nameservers instead.

**How much does scanning a well-known brand cost versus a smaller one?**
It depends heavily on how squatted the brand already is: a measured run
against `apify.com` (an ordinary brand) found 72 registered lookalikes for
$1.805, while `google.com` (heavily squatted) found 223 for $5.580 — both
bounded by `maxFindings` (default 500, so $12.505 worst case at defaults),
so a run's cost can never run away on you. See [Pricing](#pricing) for the
full breakdown.

**Can I monitor for new lookalikes automatically, or is this a one-time scan?**
Set up a schedule in the Apify Console (e.g. weekly) with the same input —
each run is an independent snapshot and only charges for what it actually
finds registered at that point in time.

### Related tools

Part of the same domain-tooling family, same pay-per-event pricing, same
"never charge for a lookup that couldn't be determined" policy:

- **[Bulk Domain Intelligence](https://apify.com/cottony_brittlegill/domain-intelligence-bulk)**
  — WHOIS/RDAP registration data, DNS records, SSL certificate check and
  availability for domains you already know. Use it to check a domain, not
  to discover lookalikes of one.
- **[Domain Mail-Health Bulk](https://apify.com/cottony_brittlegill/domain-mailhealth-bulk)**
  — SPF, DMARC, DKIM and MX audit with an A-F grade. Once this actor finds a
  lookalike with `mail_capable` or `active_threat` risk, run that domain
  through Mail-Health Bulk to see exactly what mail-sending capability it
  has configured.

# Actor input Schema

## `brandDomains` (type: `array`):

Domains you own or are authorized to monitor. Each one is expanded into typosquat and lookalike candidates. Accepts bare domains or URLs.

## `brandDomainsText` (type: `string`):

Alternative to the list above: paste domains separated by newlines or commas. Merged with the list.

## `variantKinds` (type: `array`):

Which squatting techniques to generate. Leave empty to use all of them: omission, insertion, repetition, replacement, transposition, hyphenation, subdomain, addition, vowelswap, bitsquatting, homoglyph, tldswap, combosquat.

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

Suffixes to test with the same brand label (e.g. your-brand.net, your-brand.io). Leave empty for a sensible default set of the 20 most abused TLDs.

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

Words combined with your brand name (e.g. brand-login.com, secure-brand.com). Leave empty for the default set: login, secure, account, support, verify, billing, help, mail.

## `verifyMode` (type: `string`):

dns-first (recommended): resolve DNS first and only query the registry for variants that are actually delegated — fast, cheap, respectful of registry rate limits. rdap-all: query the registry for every single variant — also catches registered-but-undelegated squats, but is far slower and much more likely to hit registry rate limits.

## `includeSsl` (type: `boolean`):

For registered lookalikes that resolve, fetch the TLS certificate (issuer, subject, validity). A valid certificate on a lookalike is a strong sign it is built to be convincing in a browser.

## `onlyRegistered` (type: `boolean`):

On (recommended): the dataset contains only lookalikes that actually exist. Off: every candidate checked is written out, including the unregistered ones, as a full audit trail. Unregistered candidates are never charged either way.

## `maxVariantsPerDomain` (type: `integer`):

Upper bound on generated candidates per brand domain. The budget is spread evenly across the enabled algorithms so you always get homoglyph and TLD-swap coverage, not just hundreds of one type.

## `maxFindings` (type: `integer`):

Hard ceiling on billable findings for this run. The scan stops once it is reached, so a run can never cost more than this number times the per-finding price.

## `perRegistryConcurrency` (type: `integer`):

How many registry (RDAP/WHOIS) queries may be in flight against one registry at a time. Kept low to respect registry rate limits (RFC 7480).

## `globalConcurrency` (type: `integer`):

How many candidate domains to check in parallel across all registries. Higher is faster but uses more memory and more network sockets.

## `lookupTimeoutMs` (type: `integer`):

How long to wait for each individual DNS, RDAP, WHOIS or TLS lookup before giving up on it (milliseconds).

## Actor input object example

```json
{
  "brandDomains": [
    "example.com"
  ],
  "verifyMode": "dns-first",
  "includeSsl": true,
  "onlyRegistered": true,
  "maxVariantsPerDomain": 400,
  "maxFindings": 500,
  "perRegistryConcurrency": 2,
  "globalConcurrency": 10,
  "lookupTimeoutMs": 8000
}
```

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

// Run the Actor and wait for it to finish
const run = await client.actor("cottony_brittlegill/domain-typosquat-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 = { "brandDomains": ["example.com"] }

# Run the Actor and wait for it to finish
run = client.actor("cottony_brittlegill/domain-typosquat-detector").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "brandDomains": [
    "example.com"
  ]
}' |
apify call cottony_brittlegill/domain-typosquat-detector --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=cottony_brittlegill/domain-typosquat-detector",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/acts/z9Drh1VBlwSd7vzhD/builds/XdLkJE8L9nuhKSTWP/openapi.json
