# whisper.variants() — Lookalike Generation

> whisper.variants() generates typosquat and homoglyph lookalikes of a domain and returns the registered ones: strategies, exists semantics, examples.

*Source: https://www.whisper.security/docs/whisper-graph/procedures/variants*
*Published: 2026-07-06*
*Last updated: 2026-09-27*

---
`whisper.variants()` generates lookalike domains for a name you give it and, by default, returns only the ones registered in the graph. One call replaces the usual pipeline of an offline typosquat generator plus a bulk registration check. Because the generation and the existence check happen where the rest of the investigation lives, you can chain the hits straight into DNS resolution, routing, and threat-feed context in the same query.

The procedure takes the domain as its first argument. By default it filters to variants that exist as `HOSTNAME` nodes; pass a node label as the second argument to check a different node type, or `false` as the filter argument to return every generated variant, registered or not. Each row carries `variant`, `method`, `exists`, `confidence` and `confidenceLabel`.

## Generation strategies

The domain is run through a set of generation algorithms: character omission, repetition, transposition, keyboard-adjacent replacement and insertion, homoglyph substitution, bitsquatting, TLD swap, and others. The `method` column names the strategy behind each row (`OMISSION`, `TRANSPOSITION`, `HOMOGLYPH`, `BITSQUATTING`, ...), and `confidence` ranks how plausible the variant is as a lookalike, so you can sort the shortlist before triaging it.

```cypher expect=rows>0 seed=paypal.com verified=2026-09-02
// Registered lookalikes of paypal.com, highest-confidence first
CALL whisper.variants("paypal.com")
YIELD variant, method, exists, confidence, confidenceLabel
WHERE exists
RETURN variant, method, confidence, confidenceLabel
ORDER BY confidence DESC
LIMIT 25
```

```json
[
  {"variant": "paypa1.com", "method": "HOMOGLYPH", "confidence": 1.0, "confidenceLabel": "high"},
  {"variant": "paypai.com", "method": "HOMOGLYPH", "confidence": 1.0, "confidenceLabel": "high"},
  {"variant": "paypan.com", "method": "BITSQUATTING", "confidence": 1.0, "confidenceLabel": "high"},
  {"variant": "paytal.com", "method": "BITSQUATTING", "confidence": 1.0, "confidenceLabel": "high"}
]
```

Captured 2026-09-02, abridged. The registered set moves as names are registered and dropped.

## What the exists flag means

`exists: true` means the variant is registered. It does not mean the variant is malicious. A parked typosquat and a live phishing page both show up as registered, so treat every hit as a candidate that still needs triage.

The unregistered side is useful too. Pass `false` as the filter argument and the full generated space comes back, which is the list worth reviewing for defensive registration before someone else stands one up.

```cypher expect=rows>0 seed=paypal.com verified=2026-09-02
CALL whisper.variants("paypal.com", false)
YIELD variant, method, exists
WHERE NOT exists
RETURN variant, method
LIMIT 10
```

## In expression position

`whisper.variants` is also a function, so it works inside an expression as well as after `CALL`: `size(whisper.variants("paypal.com"))` counts the generated space, and slicing the list gives you the names without a `YIELD`. The function form returns the variant names only; use `CALL` when you need `method`, `exists` or `confidence`.

```cypher expect=rows>0 seed=paypal.com verified=2026-09-02
RETURN size(whisper.variants("paypal.com")) AS generated,
       whisper.variants("paypal.com")[0..5] AS sample
```

> **Read `coverage` before `band`.** Only `known-clean` licenses the word "clean"; `no-data` means
> *unknown*, which is a different thing again; `malicious-evidenced` and `ambiguous` mean there is
> evidence, whatever the band says.
> Full contract: [Coverage — what we looked at](/docs/whisper-graph/procedures/coverage).

## Pivot the hits to a verdict

The standard triage read chains the generator into resolution and the reconciled verdict properties in one pass: which registered lookalikes are live, where they resolve, and which already carry a blocking verdict.

```cypher expect=rows>0 seed=paypal.com verified=2026-09-02
// Registered variants → where they resolve → reconciled verdict, all at once
CALL whisper.variants("paypal.com")
YIELD variant, method, exists
WHERE exists
WITH variant, method LIMIT 50
MATCH (h:HOSTNAME {name: variant})-[:RESOLVES_TO]->(ip:IPV4)
RETURN variant, method, ip.name AS resolves_to,
       ip.verdictLevel AS ip_verdict, ip.verdictBlocking AS blocking
ORDER BY blocking DESC, ip_verdict DESC
LIMIT 25
```

For any single suspect, [`explain()`](/docs/whisper-graph/procedures/explain) returns the full evidence chain: score, level, the contributing feeds with their weights, and first/last-seen timestamps you can paste into a takedown ticket.

```cypher expect=rows>0,no-null-columns seed=paypa1.com verified=2026-09-02
CALL explain("paypa1.com")
YIELD indicator, found, score, level, explanation, sources
RETURN indicator, found, score, level, explanation, sources
```

Keep the coverage caveat in mind: a `NONE` level is not proof the graph has never seen the name. Run the call above and `paypa1.com` returns `NONE` with a populated `sources` array, so read `found`, `sources` and `explanation` together, and ask [`whisper.assess`](/docs/whisper-graph/procedures/identify#whisper-assess-hosts-is-it-dangerous) the coverage question. Either way a lookalike can still be a brand-new phishing page. Fresh lookalikes routinely beat feed coverage, so a variant that resolves but has no verdict yet is often the one to look at first.

## Where it fits

- The [Brand Protection use case](/docs/workflows#brand-protection) and its [Lookalike Hunting](/docs/recipes/brand-protection) recipes build the full loop on this procedure: surface scans, cluster expansion through co-hosting and shared nameservers, and certificate-transparency monitoring.
- To run the generate-and-triage loop in the browser without writing Cypher, use the [Typosquat Scanner](/products/intelligence/use-cases/brand-protection/typosquat) workflow.
- On the AI side, the same capability is available over MCP: call the procedure through the `query` tool, or run the `typosquat` workflow with `run_workflow`; see the [MCP reference](/docs/ai/mcp/reference).
- For the other procedures and when to prefer them over a hand-written traversal, see the [procedures overview](/docs/whisper-graph/procedures).
