API reference#

domainsearch answers one question, is this domain registered?, over a plain JSON API. No account, no API key, no cookies. Base URL:

https://domainsearch24.eu/api/v1

How availability is determined#

Every check runs through layered signals, ordered by cost and freshness, and never guesses. The availability status is one of:

Status Meaning
registered The domain is taken.
free The registry itself answered "not registered" (RDAP 404).
unknown No signal was available: see the reason field.

Each answer names its source:

Source Layer Freshness
czds ICANN zone files: every delegated name per covered gTLD ≤ 24 h
commoncrawl Common Crawl web-graph domain set (~120 M domains) months
dns NS delegation probe: works for every TLD live
rdap The registry's own RDAP answer: the only source that can assert free live
policy The label violates the TLD's own documented length rules (e.g. below the registry's minimum): classed registered/taken without any network lookup n/a

Two important consequences:

GET /check#

GET /api/v1/check?domains=example.com,example.io[&fast=1]

Checks up to 30 comma-separated domains and returns all results in one response (extra domains beyond 30 are dropped). Domains must be registrable names in punycode (IDNs are not auto-encoded). Whole-batch budget: ~9 s; slow registries yield per-domain unknown and retries are cheap (availability results are cached server-side: registered 24 h, free 1 h, unknown 10 min).

fast=1 skips RDAP and answers from the local layers only: milliseconds, but it can assert registered, never free. Use it to pre-filter candidate lists before confirming the survivors with a full check (or use streaming and get both in one request).

curl 'https://domainsearch24.eu/api/v1/check?domains=example.com,zqhx.dev'
{
  "results": [
    {"domain": "example.com", "status": "registered", "source": "czds"},
    {"domain": "zqhx.dev", "status": "free", "source": "rdap"}
  ]
}

GET /check/{domain}#

The curl-friendly single-domain form. Same semantics, bare result object.

curl https://domainsearch24.eu/api/v1/check/example.com
{"domain": "example.com", "status": "registered", "source": "czds"}

GET /check/stream#

GET /api/v1/check/stream?domains=example.com,example.io[&fast=1]

Same input as /check, but the response is NDJSON (application/x-ndjson): one result object per line, written as each domain resolves. Local-layer answers arrive in milliseconds while RDAP confirmations trail in over a few seconds: ideal for progressive UIs. Line order is completion order, not input order; match on domain. The stream ends when every domain has answered (budget ~15 s).

curl -N 'https://domainsearch24.eu/api/v1/check/stream?domains=a.com,b.io,c.dev'

GET /bulk/stream#

GET /api/v1/bulk/stream?label=acme[&confirm=1][&tlds=com,io,dev][&group=<TLD group>]

The bulk search behind the landing page: ONE label checked across every registerable delegated TLD (~937 domains by default; see the TLD groups) on a single NDJSON stream.

By default only the fast local layers run (zone files, Common Crawl, DNS): seconds for the whole TLD space, no registry traffic. A clean local miss classes as free: no zone entry, no crawl entry, NXDOMAIN. Its strict status stays unknown: only a registry can formally assert free: and the registrar checkout is the final word either way.

tlds=com,io,dev restricts the sweep to a comma-separated subset (unknown TLDs are ignored; it wins over group -- naming a TLD directly is unambiguous intent). DNS probe outcomes are cached server-side for 24 hours, so repeated sweeps are effectively instant.

group= sweeps one node of the TLD group hierarchy instead of the default registerable: e.g. group=open skips every community-gated TLD, group=restricted-geographic checks just the country-gated ones, group=all includes even closed TLDs. Every line carries restricted/restriction_kind regardless -- the group only controls which TLDs are swept at all.

confirm=1 adds a second phase: RDAP for the still-unknown names on RDAP-capable TLDs, popular TLDs first, until the ~90 s budget runs out. Upgrade lines stream with the same domain; a confirm line supersedes the local one (upgrading assumed-free to registry-confirmed free, or catching the rare registered-but-undelegated name).

Each line adds a class refining the raw status for filtering:

class Meaning
taken Registered.
free No registration found (clean local miss), or registry-confirmed when status is free.
premium Registered with registry status reserved: typically premium/registry-held inventory.
aftermarket Registered, but delegated to a known parking/for-sale marketplace nameserver (heuristic).
unknown No signal either way: e.g. the DNS probe failed, or confirmation is pending.

class/status are pure availability. Separately, every result also carries restricted (bool) and, when true, restriction_kind (geographic or eligibility): a restricted TLD can be genuinely free by these fields and still require the buyer to qualify for it (a real bank for .bank, a Swiss company for .swiss, ...). Never treat restricted as a taken/free fact -- it's a separate "you may not be eligible" caveat.

The stream opens with {"label":"acme","total":937} and closes with {"done":true,"confirm":…,"queued":…,"confirmed":…,"truncated":…}: truncated: true means the confirmation budget ran out before every queued name was verified.

curl -N 'https://domainsearch24.eu/api/v1/bulk/stream?label=acme'

Bulk searches are rate limited per IP as whole searches (they already fan out server-side); API keys lift the limit.

GET /tlds#

GET /api/v1/tlds[?group=<TLD group>]

Delegated root-zone TLDs (per IANA) with capability flags; by default the registerable ones (~937).

{"tlds": [{"tld": "com", "rdap": true, "zone": true}, ...]}

Cacheable for an hour; the underlying sources change at most daily.

TLD groups#

group= on /bulk/stream and /tlds selects one node of the registrability hierarchy (a parent contains everything its children contain); the web UI's TLD selector and the MCP tools' tlds_group speak the same tokens:

group Contains
all Every delegated TLD in the DNS root, even ones no one can register.
registerable The default. Every TLD with a public registration path: open + restricted.
open Anyone can register: no eligibility requirements.
restricted Registerable, but gated to a specific eligible community.
restricted-geographic Requires residency, citizenship, or a registered company in a specific country (.swiss, .dubai, ...).
restricted-eligibility Requires a verified license, membership, or institutional status (.bank, .museum, .travel, ...).
closed No public registration path at all.
closed-brand Single-company dot-brand gTLDs (.google, .bmw, ...).
closed-government State, military, treaty and internet-infrastructure TLDs (gov, mil, int, arpa).
closed-discontinued No longer accepting new registrations (.tel).

A restricted TLD can be genuinely free and still require the buyer to qualify (a real bank for .bank, a Swiss company for .swiss); never treat restriction as a taken/free fact. An invalid group value is a 400.

GET /status#

Layer readiness and uptime: also our health check:

{
  "ok": true,
  "uptime_s": 86400,
  "layers": {
    "dns": {"enabled": true, "ready": true},
    "rdap": {"enabled": true, "ready": true},
    "czds": {"enabled": true, "ready": true, "detail": "853 zones indexed"},
    "commoncrawl": {"enabled": true, "ready": true}
  }
}

Errors#

Errors use one envelope, always JSON:

{"error": {"code": "rate_limited", "message": "anonymous rate limit exceeded; retry later or use an API key"}}
HTTP code When
400 invalid_request No domains given.
404 not_found Unknown endpoint.
429 rate_limited Anonymous allowance exhausted: honor Retry-After.
501 unavailable Checking is not configured on this deployment.

Malformed domains are not request errors: they come back as per-domain unknown results with a reason, so one bad name never fails a batch.

Rate limits & keys#

Anonymous use is limited per IP, per domain checked (a 30-domain batch costs 30 units): 120 domains/minute with burst headroom of twice that. That is plenty for interactive use.

Calling the API from a service? An API key lifts the limit: request one at liam@domainsearch24.eu and send it as Authorization: Bearer <key>.

API access is governed by the terms of service: in short, use the results in your own product freely, but don't resell access, redistribute the data, or build a competing domain-search service on it.

CORS & OpenAPI#

The API sends Access-Control-Allow-Origin: *: call it straight from the browser. A machine-readable spec is at /api/openapi.yaml; this page is also served as plain markdown at /docs.md for LLM agents (see /llms.txt).