A domain registration API lets software register and manage domains. Only accredited registrars can talk to registries directly over EPP; everyone else uses a registrar's REST API with an account, a token and a payment method. Read-only tasks such as availability or expiry lookups don't need registration access: RDAP or a public check API is enough.
“We need a domain API” is a sentence that hides three different projects: checking whether names are free, reading data about existing domains, or registering and managing domains from code. They use different interfaces with very different access rules. Picking the wrong one early is how teams end up with a lookup integration when they needed checkout, or the other way round.
Four kinds of domain API
The layers stack on top of each other. Registries speak EPP to registrars, registrars expose REST APIs to customers, and public lookups go through RDAP or whois.
| Interface | Who can use it | Read | Register | Format |
|---|---|---|---|---|
| RDAP (RFC 9082/9083) | anyone | yes | no | HTTPS, JSON |
| Whois (RFC 3912) | anyone | yes | no | TCP 43, text |
| EPP (RFC 5730 to 5734) | accredited registrars | yes | yes | XML over TLS |
| Registrar REST API | the registrar’s customers | yes | yes, depending on provider | HTTPS, usually JSON |
| Public check API | anyone, fair-use limits | availability, price | no | HTTPS, JSON |
The PHP side of RDAP and whois is covered in RDAP in PHP and whois with fsockopen. This guide is about choosing.
EPP, the protocol under every registration
Whatever API you call, the registration ends as an EPP command at the registry. EPP, the Extensible Provisioning Protocol, is specified in RFC 5730 to 5734. It sends XML over a persistent TLS session and has commands such as <check>, <create>, <renew> and <transfer>. An EPP <domain:check> is the authoritative availability answer, reserved names included.
Access is the catch. You need ICANN accreditation and a separate agreement with each gTLD registry, and ccTLDs run their own programmes. That’s a business decision with deposits, audits and technical certification, not something you sign up for on a Friday afternoon. Agencies and SaaS products use a registrar that handles EPP for them.
What to check before integrating a registrar API
Registrar APIs differ more than their marketing pages suggest. Questions I’d want answered before writing code:
- Auth: API key, OAuth or personal token? Can a single token be revoked without breaking everything else?
- Price data: Are registration and renewal returned separately? A cheap first year with an expensive renewal only shows up twelve months later otherwise.
- Sandbox: Is there a test environment where orders cost nothing?
- Rate limits: What are they, and do you get HTTP 429 with
Retry-After? - Coverage: Which TLDs work via API, and how are ccTLDs with residency rules handled?
- Payment: Does the API debit a prepaid balance, or does each order need a checkout?
That last one is architecture, not accounting. Prepaid balances allow fully unattended registration. Checkout links mean a human is always in the loop, which is either a limitation or a safety feature depending on what you’re building.
betadomain’s API from PHP
betadomain offers two layers, both documented on the developer page. The first is a public REST API with two read-only endpoints and no key: POST /api/domain/check for one domain and POST /api/domain/search for a name across eight popular extensions. Limits are 30 requests per minute and 300 per six hours per IP, and there’s an OpenAPI 3.1 file at /openapi.json.
The second layer is an MCP server at /api/mcp. Its read tools are open; with a personal access token it can list your domains and prepare orders. The MCP guide covers it in detail.
A small client for the REST endpoints:
<?php
declare(strict_types=1);
final class BetadomainClient
{
public function __construct(private string $base = 'https://betadomain.de') {}
public function check(string $domain): array
{
return $this->post('/api/domain/check', ['domain' => $domain]);
}
/** @return list<array{domain:string, tld:string, available:?bool, priceEur:?float}> */
public function search(string $name): array
{
return $this->post('/api/domain/search', ['query' => $name])['results'];
}
private function post(string $path, array $payload): array
{
$ch = curl_init($this->base . $path);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode($payload, JSON_THROW_ON_ERROR),
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Accept: application/json'],
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 15,
CURLOPT_HEADER => true,
]);
$raw = curl_exec($ch);
if ($raw === false) {
throw new RuntimeException(curl_error($ch));
}
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
$headers = substr($raw, 0, curl_getinfo($ch, CURLINFO_HEADER_SIZE));
$body = substr($raw, curl_getinfo($ch, CURLINFO_HEADER_SIZE));
if ($status === 429) {
$wait = preg_match('/^Retry-After:\s*(\d+)/mi', $headers, $m) ? (int) $m[1] : 60;
throw new RuntimeException("Rate limited, retry in {$wait}s");
}
$data = json_decode($body, true, 512, JSON_THROW_ON_ERROR);
if ($status >= 400) {
throw new RuntimeException($data['error'] ?? "HTTP $status");
}
return $data;
}
}
$api = new BetadomainClient();
foreach ($api->search('my-demo-project-4711') as $r) {
$state = match ($r['available']) {
true => 'available',
false => 'taken',
default => 'unknown',
};
printf("%-30s %-10s %s\n", $r['domain'], $state, $r['priceEur'] ?? '-');
}
Run against the live API, the search returns eight rows from .de to .dev, each with available and priceEur. A .com costs €14.99 a year and an .io €61.99, with renewals at the same price.
Edge cases your client has to handle
The happy path is easy. These are the cases that cause bugs:
- HTTP 400 for an invalid name. The search endpoint accepts only a to z, digits and hyphens in the name part; send IDNs to the check endpoint or convert them first.
- HTTP 429 when you exceed the limit, with
Retry-Afterin seconds. "supported": falsewhen the extension isn’t offered. No registry query was made."available": nullwith"error": truefor a single row when that one lookup failed. The other rows are still valid.
The null case is why the example uses a three-arm match. A plain ternary treats null as false and reports a timeout as “taken”.
Which API fits which project?
My rule of thumb after a few of these integrations:
| Goal | Interface |
|---|---|
| Name suggestions during signup | public check API, cached |
| Watching expiry dates of third-party domains | RDAP, whois for ccTLDs without RDAP |
| Managing an agency’s client domains | registrar API with tokens |
| Running your own registrar | EPP, after accreditation |
| Letting an AI assistant find domains | MCP server |
And if all you need is to know whether a string looks like a domain, skip the network. A regex does that in microseconds; see validating domain names with regex.
For the full list of extensions you can query, see the price list covering every TLD, or try a name in the betadomain search box first.
Found your domain? You can check and register it directly at compare domain prices – yearly price from €2.99.
FAQ
Can I register domains through EPP myself?
Only as an accredited registrar. For gTLDs that means ICANN accreditation plus an agreement with each registry; many ccTLDs have their own membership schemes. For agencies and SaaS products a registrar API is the practical route.
Is there a free domain registration API?
API access can be free, the domains never are, because each registration pays a registry fee. Read-only availability APIs often are free within fair-use limits, such as betadomain's keyless check endpoint at 30 requests per minute per IP.
Can an AI agent buy a domain through betadomain?
It can prepare the order. The start_order MCP tool checks availability and price server-side and returns a Stripe checkout link; a person completes payment and any identity check. Nothing is charged automatically.
What's the difference between RDAP and a registrar API?
RDAP reports what the registry knows: whether a domain exists, its status, dates and nameservers. A registrar API acts: it quotes prices, places orders and manages your own domains.