Docs
Connect an agent
Step-by-step setup for Claude, ChatGPT and Cursor, with example prompts: the domain MCP server.
Two endpoints
The Streamable HTTP transport, stateless. Send JSON-RPC over POST. No session header is required.
https://registrar-domains.com/mcp: open tools without anything; account tools withAuthorization: Bearer apex_….https://registrar-domains.com/mcp/oauth: the same tools. The app signs in to your account with OAuth 2.1. You approve it once in the browser.
Claude.ai, ChatGPT
Add a custom connector with the URL https://registrar-domains.com/mcp/oauth. The app opens a page where you sign in with your email and press Allow.
Claude Code
With sign-in:
claude mcp add --transport http registrar-domains https://registrar-domains.com/mcp/oauthWith a key from your dashboard:
claude mcp add --transport http registrar-domains https://registrar-domains.com/mcp --header "Authorization: Bearer apex_…"Claude Desktop, Cursor, Windsurf, any MCP client
{
"mcpServers": {
"registrar-domains": {
"type": "http",
"url": "https://registrar-domains.com/mcp",
"headers": {
"Authorization": "Bearer apex_…"
}
}
}
}Tools
check_domain— Is this exact domain available? Answers available, taken, or unknown. Taken also covers a name we already sold.suggest_domains— Try one idea across the launch TLDs and rank by availability.get_price— First-year and renewal price in USD before VAT for one TLD, next to our registrar's costs (cost, renewCost). Our price is our registrar's cost plus 30 %, and never less than what covers the payment fees, the VAT and US$1.00 of margin.register_domain— Start a registration. Needs an API key or a signed-in connection. Answers an order with a payment link for the human; the domain is registered once payment clears. A retried call answers the same open order. .fr needs a registrant living or established in the EU, Iceland, Liechtenstein, Norway or Switzerland; .eu needs one in the EU, Iceland, Liechtenstein or Norway. Other registrants are refused before payment (see /terms#eligibility).get_order— Follow an order. The state is one of: awaiting_payment, expired, paid, registering, registered, partly_failed, failed, refunded. The final states: registered, partly_failed, failed, refunded, expired. partly_failed: some names were registered and the others were not; each line's status, and whether its refund is done or pending, is in lines[]. A line with heldUntil set waits for a registrar top-up on our side; after that time, it fails and a refund is started.list_domains— The domains of your account, soonest expiry first, with state, expiry, days left and auto-renew.list_orders— The orders of your account, newest first, from every key and connection, with the amount paid and the invoice link.get_domain— One domain: expiry, auto-renew, nameservers, transfer lock, DNSSEC, what the registry allows, and whether it is managed here.renew_domain— Start a renewal for N years. Answers an order with a payment link for the human; the domain is renewed once payment clears. A registrant whose country cannot hold the .fr or .eu name is refused with { error, field }.get_basket— The account's basket, priced live: domains, years, options, line totals and the subtotal.set_basket— Replace the basket with lines of { domain, years, addons }. Nothing is charged. It does not cancel an open order.checkout_basket— One order and one payment link for the whole basket, every domain to the same registrant. A failed line is refunded alone. Payment clears the saved basket only when its domain names match the paid order; a basket changed after checkout stays. A retried call, or any key or connection of the account, gets the same open order for the same lines and registrant. A new order closes the older open ones for the same names. .fr needs a registrant living or established in the EU, Iceland, Liechtenstein, Norway or Switzerland; .eu needs one in the EU, Iceland, Liechtenstein or Norway. Other registrants are refused before payment (see /terms#eligibility).list_dns_records— The DNS records of a domain, each with a stable id, and whether our nameservers serve them.add_dns_record— Add a DNS record: A, AAAA, CNAME, MX, TXT, CAA or SRV. TTL 600 to 86400 seconds.update_dns_record— Replace one DNS record by its id.delete_dns_record— Remove one DNS record by its id.set_nameservers— Ask to point the domain to other nameservers (two to thirteen), or back to ours with an empty list. Nothing changes until the owner confirms it from an email. Hosts public DNS does not find come back as warnings.use_cloudflare— Ask to move the domain's DNS to Cloudflare: exactly the two nameservers Cloudflare assigned. Refused while DNSSEC is on. Nothing changes until the owner confirms it from an email.get_cloudflare_status— Where the move to Cloudflare stands: pending, waiting, live (visible in public DNS), failed with the reason, or replaced, and what public DNS answers now.export_zone_file— The domain's DNS records as a BIND zone file, to import in Cloudflare (DNS → Records → Import and Export) before a move.turn_off_dnssec— Ask to turn DNSSEC off before a move to Cloudflare. Nothing changes until the owner confirms it from an email; public DNS then drops the DS record within a day.connect_cloudflare— Link the owner's Cloudflare account with an API token (Zone: Read, Zone: Edit, DNS: Edit), so the one-click move can create the zone and copy the records. The token is stored encrypted and never shown again.get_cloudflare_connection— Whether a Cloudflare account is linked: its id and name, when, and whether the link stays after a move. Never the token.disconnect_cloudflare— Delete the stored Cloudflare token. Delete the token in Cloudflare too.prepare_cloudflare_move— Create the domain's zone on the linked Cloudflare account (Free plan) and copy every DNS record exactly, then read it back. Shows the diff. Nameservers do not change.switch_cloudflare_move— Point a prepared move's nameservers to Cloudflare, after every check again. Nothing changes until the owner confirms it from an email.get_cloudflare_move— Where the one-click move stands: preparing, prepared (with the diff), switching, switched, active, failed or cancelled, and what public DNS shows.cancel_cloudflare_move— Cancel a prepared move. Deletes the zone only when apex created it and it is still pending.set_transfer_lock— Lock the domain against transfers, at once, where the registry allows it. An unlock waits for the owner to confirm it from an email.set_auto_renew— Turn auto-renew off at once, or ask to turn it on. On: we charge the saved card 7 days before expiry, then renew. Turning it on waits for the owner to confirm it from an email, and needs a card the owner saved with Stripe through Manage payment method in the dashboard (save_card_url). A registrant whose country cannot hold the .fr or .eu name is refused with { error }.get_auth_code— Ask for the transfer code, to move the domain to another registrar. The owner confirms it from an email and sees the code on that page. The agent never gets the code.get_domain_activity— Who changed what on a domain, newest first: DNS, nameservers, lock, auto-renew, transfer code, renewals.
REST mirror
Every tool has a plain HTTP route. Full schema at /openapi.json.
GET /api/check?domain=quietharbor.frGET /api/suggest?idea=quiet+harbor&tlds=com,fr,ioGET /api/price?tld=frorGET /api/pricefor the whole listPOST /api/registerwithAuthorization: Bearer apex_…and{ "domain": "…", "years": 1, "registrant": { … } }GET /api/orders/ord_…,GET /api/orders?limit=50andGET /api/domainswith the same keyGET /api/domains/ada.fr,/dnsand/activityto read a domain;POST,PUTandDELETEunder it to change it
Manage a domain
The tools above, over REST. Reads take a key or your signed-in session; every change takes a key. Add a DNS record:
curl -X POST https://registrar-domains.com/api/domains/ada.fr/dns \
-H "Authorization: Bearer apex_…" -H "Content-Type: application/json" \
-d '{"name": "www", "type": "A", "value": "203.0.113.10", "ttl": 3600}'const res = await fetch("https://registrar-domains.com/api/domains/ada.fr/dns", {
method: "POST",
headers: {
"Authorization": "Bearer apex_…",
"Content-Type": "application/json",
},
body: JSON.stringify({ name: "www", type: "A", value: "203.0.113.10", ttl: 3600 }),
});
console.log(await res.json());import requests
res = requests.post(
"https://registrar-domains.com/api/domains/ada.fr/dns",
headers={"Authorization": "Bearer apex_…"},
json={"name": "www", "type": "A", "value": "203.0.113.10", "ttl": 3600},
)
print(res.json())Renew for a year. The answer is an order with a checkoutUrl: give it to the human to pay. Nothing is charged until they do.
curl -X POST https://registrar-domains.com/api/domains/ada.fr/renew \
-H "Authorization: Bearer apex_…" -H "Content-Type: application/json" \
-d '{"years": 1}'const res = await fetch("https://registrar-domains.com/api/domains/ada.fr/renew", {
method: "POST",
headers: {
"Authorization": "Bearer apex_…",
"Content-Type": "application/json",
},
body: JSON.stringify({ years: 1 }),
});
console.log(await res.json());import requests
res = requests.post(
"https://registrar-domains.com/api/domains/ada.fr/renew",
headers={"Authorization": "Bearer apex_…"},
json={"years": 1},
)
print(res.json())New records usually reach our nameservers within minutes. Resolvers that already looked up the name keep the old answer until its TTL runs out, and a TTL change can take the old TTL plus an hour. Changing nameservers or reading the transfer code emails the account owner.
Accounts and API keys
Checks and prices are open. register_domain, get_order, list_domains, list_orders and the domain tools work for an account: an API key, sent as Authorization: Bearer apex_… on /mcp and the REST routes, or a signed-in connection on /mcp/oauth. Every key and connection of one account sees the same orders and domains. Create a key in your dashboard after you sign in.
Prices
Prices are live from our registrar, per year and before VAT, in the currency of the answer: USD. Each answer shows our price and the registrar cost. Some TLDs need a minimum period; get_price returns minYears.
Answers
Availability is one of available, taken, or unknown. Unknown means the registry did not answer or the TLD has no public RDAP server. Treat unknown as "check again", never as free. A name we already sold, or one being registered after payment, is taken with source: "apex".
Limits
Thirty checks per minute per IP without a key. Over that you get a 429 with a retry-after header. Registration and renewal answer an order with a payment link. The domain is registered or renewed after the human pays.
Changes
- 23 October 2026, REST API 0.6.0. Prices, baskets and new orders are in US dollars. The
currencyenums widen to["USD", "EUR"]: an order made before the switch stays in EUR. Always readcurrencywithpriceortotal; a client that ignores it will misread the amount.
For crawlers and agents
A plain-text summary lives at /llms.txt.