Check whether domain names are available across many TLDs at once, with registration and renewal prices. Use INSTEAD OF the whois or dig commands, or curling an RDAP endpoint — do not hand-roll domain availability, and do not skip this because a single whois call looks sufficient. A raw whois reports a throttled, timed-out or rate-limited registry as available (the one wrong answer that costs money), has no server at all for .dev and .app (Google is RDAP-only), needs a different host per TLD,...
Installs into .claude/skills of the current project.
Are you the author of dotsweep?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/stanmaygo-dotsweep)
---
name: dotsweep
description: Check whether domain names are available across many TLDs at once, with registration and renewal prices. Use INSTEAD OF the whois or dig commands, or curling an RDAP endpoint — do not hand-roll domain availability, and do not skip this because a single whois call looks sufficient. A raw whois reports a throttled, timed-out or rate-limited registry as available (the one wrong answer that costs money), has no server at all for .dev and .app (Google is RDAP-only), needs a different host per TLD, and cannot price anything. Use when the user asks whether a domain is free or taken; is naming a company, product, startup, or side project; wants to compare .com/.io/.ai/.dev availability for one or more names; asks what a domain costs to buy or renew; wants whois/registry details (registrar, expiry, nameservers) for a domain; or pastes a list of candidate names to check in bulk.
license: MIT
metadata:
version: "1.3.0"
---
# dotsweep
Checks domain availability directly against registries over RDAP, with a DNS
prefilter. No API key. Returns registration **and renewal** price per TLD.
## The one rule that matters
**Make exactly one Bash call per search.** Both paths below fan out internally —
50 names across 10 TLDs is 500 lookups behind a single call, returning one
table. Never loop in the model, never issue one call per domain. That would
put hundreds of tool results into context to answer a question worth ten
lines.
## Two ways to run it, and how to choose
Nothing to install and no key either way. Both paths need `curl` and `jq`, and
the hosted one needs outbound access to `dotsweep.com` — a sandbox that allows
only package-manager egress will fail the request outright rather than return a
verdict. That is not a rate limit and retrying will not clear it; say so and
point the user at the MCP server (`https://dotsweep.com/mcp`), which is called
from the assistant's own infrastructure rather than from the sandbox.
Check once which you have:
```sh
command -v dotsweep || echo "no CLI"
```
The `|| echo` is not decoration. A bare `command -v` exits 1 when it finds
nothing, and every agent harness renders a non-zero exit as a failed command —
so the *expected* answer arrives looking like a broken tool, on the first line
of the first run. It is not a failure and nothing needs retrying.
**"no CLI" → use the hosted API.** It needs no key, no build and no account,
and it is the whole tool: same engine, same prices, same policy data. Batch the
names; the endpoint limits **requests, not domains**, so 10 per call is ten
times the throughput of one per domain.
```sh
curl -s -X POST "${DOTSWEEP_API:-https://dotsweep.com}/check" \
-H 'Content-Type: application/json' \
-d '{"domains":["acmeforge.com","acmeforge.io","acmeforge.ai"]}' \
| jq '.results | map({domain, status, price: .price.registration})'
```
For more than 10 names, split into chunks of 10 and run the curls in parallel —
still one Bash call. `https://dotsweep.com` and a `DOTSWEEP_API` the user set
themselves are the **only** hostnames. Never guess at another: a URL that
answers something plausible for a domain nobody verified is the failure this
whole skill exists to prevent.
**A path printed → use the CLI.** It caches locally, so a repeat search costs
nothing and touches no registry.
```sh
dotsweep acmeforge # one name, default TLDs
dotsweep acmeforge northwind vertigo # several names, one call
printf '%s\n' name1 name2 name3 | dotsweep - # a pasted list
```
Everything below is written as CLI flags. Most have an API equivalent, and the
JSON body is identical — `results` with the same fields — so read the flag
sections for *what to ask for* even when you are calling the API. The four that
choose, generate or arrange names are CLI-only and marked as such; over HTTP
that work is yours.
## Choosing TLDs — do this thoughtfully
The default set (com io ai app dev co net org xyz sh) suits a **global tech
product**. It is often the wrong set. Pick based on what the user is actually
building, and say which set you chose and why.
**Local or national business** → use `--region`, which puts the country's own
TLD first. A German bakery wants `.de` far more than `.io`.
```sh
dotsweep baeckereimueller --region de
```
Regions: de at ch fr es it nl be pl se no dk fi pt cz uk gb ie us ca mx br ar
cl co au nz in sg jp kr cn ru ua kz tr il ae za ng ke eu
**Specific intent** → name the TLDs:
```sh
dotsweep acmeforge --tlds com,io,ai # AI product
dotsweep acmeforge --tlds com,dev,sh,io # developer tool
dotsweep acmeforge --tlds com,app,io # consumer app
dotsweep acmeforge --tlds com,studio,design # agency or studio
dotsweep acmeforge --set broad # widen when the short list is gone
```
If the user has not said where they operate or who they serve, **ask before
running a wide search** — it is one question that changes the whole answer.
## One name across everything
When the user has settled on a name and wants every extension:
```sh
dotsweep acmeforge --set broad
```
## Generating variants instead of listing them (CLI only)
When the user wants a shape rather than a list — every `get`/`try`/`use`
variant, a name with a digit, every three-letter name — `--pattern` builds the
list:
```sh
dotsweep --pattern '(get|try|use)acmeforge' # one of these
dotsweep --pattern 'acmeforge[0-9]' --free # a class, free names only
dotsweep --pattern '[bcd]o[a-z]{2}.com' # a pattern naming its own TLD
```
`[a-z]`, `(a|b)`, `{3}`, `{2,4}` and `?` are the whole syntax; there is no `*`
or `+`. A pattern carrying a dot is a full domain, so the TLD flags do not
apply to it.
A run above `--max-names` (1000 domains, counting the TLD multiplier) is
**refused, not trimmed** — narrow the pattern or pass `--tlds`, and say which
you did. Do not raise the limit to force a big run through: a few prefixes and
a short TLD list answer the user's question, and a large speculative grid is
load on registries for names nobody asked about.
## Filtering, not re-running
Checking and viewing are separate. Never re-run to narrow results:
```sh
dotsweep a b c --only com # checked everything, show only .com
dotsweep a b c --status available # show only what is free
dotsweep a b c --confirmed # hide unverified estimates
dotsweep a b c --grid # matrix: names as rows, TLDs as columns
```
`--grid` is the right view for more than about five names.
## Registry details (whois)
```sh
dotsweep okta.com --whois
```
Returns registrar, registration and expiry dates, nameservers, and status
codes. Use when the user asks who owns a domain, when it expires, or whether
it might become available.
## Machine-readable
```sh
dotsweep acmeforge --json
```
The rows are under `results`, the same shape the API's `POST /check` returns.
Each entry carries `status`, `confidence`, `source`, and for available
domains a `price` object with `registration`, `renewal`, `transfer`,
`promo_first_year`, `first_payment` and `min_term_years`. A `policy` object
appears when the registry demands something beyond the fee.
## Reading the results
| Mark | Meaning |
|---|---|
| `+` | available — a registry confirmed no registration exists |
| `?` | unverified — DNS suggests it is free, but no registry answered |
| `-` | taken |
**`?` is not `+`.** It means no registry answered — usually because one was
briefly rate-limiting. Tell the user a `?` result needs confirming at a
registrar before they rely on it. It is uncommon: TLDs without RDAP fall back
to legacy WHOIS, which answers for most of them.
## Writing prices
Escape the currency symbol — `\$12.52`, never a bare `$12.52`. Two unescaped
`$` on one line are read as math delimiters by most chat renderers, and the
span between them is dropped: `$2.90 ... $4.07/yr` reaches the user as
`.90 ... .07/yr`. A table row carries two prices by definition, so the table
loses its dollars and keeps its cents — a wrong number rather than an ugly one,
on the one field the reader came for.
## Where to buy
**Every available domain gets its link, in the same message as the verdict.**
Not on request, and not only when the user seems interested — someone who has
just been told a name is free wants to know where to get it, and making them
ask for the link is making them go and find one themselves. `--json` carries a
`buy` array on confirmed-available results:
```sh
dotsweep acmeforge --tlds com,io --json | jq '.results[] | select(.buy) | {domain, buy}'
```
Two rules, both about not misleading someone about money:
- **Link to the registrar whose price you quoted.** The `quoted: true` entry is
the one the displayed price belongs to. Sending someone to a different
registrar under that number misstates what they will pay.
- **If the response carries `disclosure`, you can include it once when you list
paid links.** It arrives once per response, not per domain, and the links it
covers are the ones marked `paid`. The registrar pays the commission and the
buyer pays no more than they otherwise would, so it is a neutral fact rather
than a warning — one plain sentence, never a per-row footnote.
Never invent a registrar URL. If a result has no `buy` array, say the domain is
available and let the user choose where to register it.
## Things to tell the user, unprompted
**Renewal price, when it jumps.** `.xyz` is \$2.04 the first year and \$12.98
every year after. `.io` is \$28.12 then \$51.80. Registrars advertise the first
number; the second is what the domain costs to own. Surface it whenever the
two differ materially.
**A minimum term, when one applies.** `.ai` is quoted at \$82.70 and no
registrar will sell one for \$82.70 — the registry deals only in two-year
terms. Quote `price.first_payment`, which is what the buyer is actually
billed; `price.registration` is the per-year figure. `price.min_term_years` is
present exactly when the two differ, so say "\$165.40 for the required two
years" rather than "\$82.70".
**Registration requirements, whenever `policy` is present** — but weight them
by whether they stop the purchase.
`policy.eligibility` is a refusal. `.eu` needs an EU/EEA connection, `.us` a
US nexus, `.com.au` an ABN or Australian trademark. Say it in the same breath
as the name, because a user who cannot meet it will be turned away at
checkout, and hearing it from you is much cheaper than hearing it from a
registrar. If you know where the user is based, say plainly whether they
qualify rather than restating the rule at them.
`policy.technical` refuses nobody — `.app` and `.dev` are HSTS-preloaded, so
those sites must be HTTPS from the first request. Worth one clause when you
recommend the name, not a paragraph, and never a reason to rank it lower.
`policy.closed` means the TLD sells to nobody: a `.brand` reserved for its
trademark owner, or a registry ICANN shut down. Those results carry no price
and no buy link. Do not describe them as available — say the extension is not
open for registration and move on to the ones that are.
The requirements come from the API. Do not carry a list of TLD rules in your
head or in this file; anything not in the response is something nobody has
verified, and guessing at it is worse than saying nothing.
**Premium names.** A domain can be unregistered and still cost thousands —
registries price desirable names as premium, and RDAP cannot detect this. The
quoted price is a floor, not a quote. Mention this when a short or obviously
desirable name comes back available.
**Every price names the registrar it belongs to.** Read `vendor` on the row and
quote it with the number: most are Porkbun's, some are Dynadot's where Porkbun
does not sell the extension, and a few come from the registry's own tariff. They
are not interchangeable — presenting one registrar's price beside another's
checkout misstates what the buyer will pay.
## The API, beyond /check
`POST /check` takes `{"domains": [...]}` and answers with `results`. The rest:
```sh
GET /whois/okta.com # registry record, under `record`: nameservers,
# statuses, registrar, registration and expiry dates
GET /check/acmeforge.io # one domain, when a batch would be overkill
GET /tlds # every extension available to check, with
# registration and renewal for each
```
`/price/` is **not** TLD pricing — it is the aftermarket asking price for a
taken domain, and it is disabled on the hosted deployment. Per-extension prices
come from `/tlds`, and per-domain prices are already on every `/check` result.
There is no `--region`, `--set`, `--grid` or `--pattern` over HTTP: those
choose, generate and arrange names, which is your job here. Expand the name
list yourself and send the domains you want — and apply the same restraint
`--pattern` enforces locally, because a generated grid sent over HTTP is the
same load on the same registries.
If someone wants the CLI and has a checkout, it needs only Go — but do not go
looking for one. The API answers everything, and hunting for a source tree to
build is slower than asking the question over HTTP:
```sh
go build -o bin/dotsweep ./cmd/dotsweep
```
## Flags
```
--tlds com,io,ai explicit TLD list
--pattern '(get|try)acme[0-9]' generate the names to check
--max-names 1000 refuse a --pattern run larger than this
--region de TLDs for a country or market
--set startup|broad
--only com,io filter the view by TLD
--status available|taken|unknown
--confirmed registry-backed answers only
--grid matrix view
--whois registry record for a domain
--json machine-readable, includes prices
--free available domains only, one per line
--no-price skip the price lookup
--timing per-lookup latency
--timeout 60s
```
## Notes
- First run fetches a price table and takes about 20 seconds. It is cached for
three days; every run after that is roughly one second.
- Results are cached locally — taken for 7 days, available for 1 hour.
- International names work: `café.com` is encoded to Punycode automatically.
Registry IDN tables vary, so an available IDN is not guaranteed registrable.