API for developers
An HTTPS API for your programs: DNS records (certbot and other Let’s Encrypt clients, deployment systems, your own scripts), the “Domains” section — terms, nameservers, auto-renewal, availability checks — and purchases from the balance: registering and renewing domains. Responses are JSON.
API key
Keys are created in the account under “API keys”. A key is bound to chosen domains or to every domain of the account, including future ones, and grants permissions in three sections — DNS, “Domains” and purchases. A key’s settings can be changed in the account at any time; the key itself stays the same.
- “SSL validation only” — the key sees, adds and removes only TXT records named _acme-challenge: on the domain itself (_acme-challenge or _acme-challenge.example.com — the same record) and on subdomains (_acme-challenge.www). It cannot see or change any other record in the zone. This is all certbot needs.
- “Read DNS” — read every record without changing anything.
- “Manage DNS” — read, add and delete any record.
- “Domains: read” — domain list, expiry, nameservers and auto-renew; whether a domain is free and its price.
- “Domains: manage” — the same, plus changing nameservers and switching auto-renew on and off.
- “Purchases: renewal” — renew the key’s domains from the account balance.
- “Purchases: registration and renewal” — register new domains and renew the key’s domains from the balance.
“Full access” in the account means DNS management and domain management at once. Purchases are not part of it: the right to spend money is switched on separately, with optional limits.
Send the key in the Authorization: Bearer <key> header. A key is shown once when created; if you lose it or it leaks, reissue it in the account (“Reissue”: a new value, the same permissions) or delete it.
curl -H "Authorization: Bearer rz_…" https://regzone.pro/api/v1/zones
HTTP/1.1 200 OK
{"scope":"acme","zones":[{"name":"example.com","status":"active"}]}Response: scope is the key’s DNS access (acme, dns_read, dns_write, or null when the key has none); zones lists the domains the key may use, alphabetically: name is the domain (internationalised names in punycode), status is its state (active, pending, expired, etc.). Records can be changed only on a domain with status active; others answer 409. There are no other fields.
Checking a key
curl -H "Authorization: Bearer rz_…" https://regzone.pro/api/v1/key
{"key":{"name":"certbot","scope":"acme","domainsAccess":null,"purchases":null,"allDomains":false,"domains":["example.com"],"allowedIps":["203.0.113.7"],"expiresAt":null}}The request changes nothing and needs no permission — it is how to check that a key is set up correctly and what it may do. scope, domainsAccess and purchases are the key’s permissions (null means none); domains lists the domains it covers; allDomains means it works with every domain of the account, future ones included; allowedIps lists the addresses it works from (empty means any); expiresAt is the end of its term (ISO 8601), or null for a key that never expires.
Endpoints
Base URL: https://regzone.pro/api/v1
| GET | /key | What this key may do: permissions, domains, allowed addresses, term. Changes nothing. |
| GET | /zones | Domains available to the key, and its permission. |
| GET | /zones/{zone}/records | Zone records the key may see. |
| POST | /zones/{zone}/records | Add a record. The response carries its id. |
| DELETE | /zones/{zone}/records/{id} | Delete one record by id. |
| GET | /domains | The key’s domains: expiry, auto-renew, nameservers. |
| GET | /domains/{domain} | One domain — the same fields. |
| PUT | /domains/{domain}/nameservers | Change the domain’s nameservers. |
| PUT | /domains/{domain}/auto-renew | Switch auto-renew on or off. |
| GET | /availability?domain={domain} | Whether a domain is free, and the registration and renewal price. |
| GET | /balance | The account balance and the key’s spending limits. |
| GET | /contacts | Contacts domains can be registered to. |
| POST | /domains | Register a domain from the balance. |
| POST | /domains/{domain}/renew | Renew a domain from the balance. |
| GET | /orders/{id} | An order placed with this key and its state. |
Add a record
Body: type, name, value, ttl (optional; 60 by default for _acme-challenge, 3600 otherwise), priority (required for MX and SRV).
curl -X POST https://regzone.pro/api/v1/zones/example.com/records \
-H "Authorization: Bearer rz_…" \
-H "Content-Type: application/json" \
-d '{"type":"TXT","name":"_acme-challenge","value":"<token>","ttl":60}'
HTTP/1.1 201 Created
{"record":{"id":"4Zt2…","type":"TXT","name":"_acme-challenge","value":"<token>","ttl":60,"priority":null},"created":true}- name — relative (_acme-challenge, www, @ for the domain itself) or fully qualified (_acme-challenge.example.com).
- Repeating a request with the same record creates no copy: the answer is 200 with "created": false and the same id.
- One name may hold several TXT records at once — for example, two for a certificate on example.com and *.example.com.
- The id is derived from the record itself: a changed record gets a new id.
- A certificate check (TXT _acme-challenge) added through the API and never deleted is removed automatically after 6 h — in case your program crashed before cleaning up. No other record is ever removed on its own.
List records
curl -H "Authorization: Bearer rz_…" https://regzone.pro/api/v1/zones/example.com/records
{"zone":"example.com","records":[{"id":"4Zt2…","type":"TXT","name":"_acme-challenge","value":"<token>","ttl":60,"priority":null}]}An “SSL validation only” key sees only _acme-challenge records — handy for finding and cleaning up leftover validation records.
Delete a record
curl -X DELETE -H "Authorization: Bearer rz_…" https://regzone.pro/api/v1/zones/example.com/records/4Zt2…
HTTP/1.1 204 No ContentExactly one record is deleted; other TXT records on the same name stay. An unknown id returns 404.
Domains
Requires “Domains: read” or “Domains: manage”.
curl -H "Authorization: Bearer rz_…" https://regzone.pro/api/v1/domains
{"domains":[{"name":"example.com","status":"active","expiresAt":"2027-10-02T00:00:00.000Z","autoRenew":true,"nameservers":["ns1.regzone.pro","ns2.regzone.pro"],"dns":"platform"}]}expiresAt is the end of the term (ISO 8601), or null while the domain is being registered; dns is platform when the default nameservers serve the zone, otherwise other.
Nameservers
curl -X PUT https://regzone.pro/api/v1/domains/example.com/nameservers \
-H "Authorization: Bearer rz_…" \
-H "Content-Type: application/json" \
-d '{"nameservers":["ns1.example.net","ns2.example.net"]}'
{"domain":{"name":"example.com",…,"nameservers":["ns1.example.net","ns2.example.net"],"dns":"other"}}Requires “Domains: manage”. From 2 to 13 names. The default nameservers are not set this way (409 platform_nameservers): going back to them is a button in the account, and it carries the zone’s records over. Your own servers for such a domain are fine: the domain leaves the default DNS just as it would from the account. The response is the domain after the change.
Auto-renew
curl -X PUT https://regzone.pro/api/v1/domains/example.com/auto-renew \
-H "Authorization: Bearer rz_…" \
-H "Content-Type: application/json" \
-d '{"enabled":false}'Requires “Domains: manage”. Auto-renew charges the balance 14 days before the term ends; the API only switches it on and off.
Is a domain free
curl -H "Authorization: Bearer rz_…" "https://regzone.pro/api/v1/availability?domain=example.org"
{"domain":"example.org","available":true,"premium":false,"sold":true,"price":{"minor":113480,"currency":"RUB"},"renewPrice":{"minor":113480,"currency":"RUB"}}Requires “Domains: read”. One full name per request. Prices are in minor units (minor) and in the balance currency; premium is a premium domain with its own price; sold: false means we do not sell this zone. Up to 40 checks a minute.
Purchases
Needs “Purchases: renewal” or “Purchases: registration and renewal”. Money is taken from the account balance — top it up in the account beforehand. The account e-mail address must be confirmed.
- Every purchase carries an Idempotency-Key header: a unique string of 8 to 100 characters (a UUID, say). Repeating the request with the same value — after a dropped connection or a timeout — never charges twice; it returns the first response with "replayed": true. Without the header the answer is 400.
- A key may have limits: how much it spends in any 24 hours and how much one purchase may cost. There are none by default; they are set and changed in the account. A purchase over a limit gets 403, and nothing is charged.
- Every purchase made with a key e-mails the account owner: what was bought, for how much and by which key. The letters can be switched off in the key’s settings.
- Prices, zone terms and checks are the same as when buying in the account. Amounts are in kopecks (minor), for the whole term.
Balance and limits
curl -H "Authorization: Bearer rz_…" https://regzone.pro/api/v1/balance
{"balance":{"minor":500000,"currency":"RUB"},"key":{"purchases":"register","dailyLimit":{"minor":300000,"currency":"RUB"},"purchaseLimit":null,"spentLast24h":{"minor":113480,"currency":"RUB"},"remainingLast24h":{"minor":186520,"currency":"RUB"}}}dailyLimit and purchaseLimit are the key’s limits (null — no limit); spentLast24h is what the key spent in the last 24 hours; remainingLast24h is what it may still spend (null — no limit).
Contacts
curl -H "Authorization: Bearer rz_…" https://regzone.pro/api/v1/contacts
{"contacts":[{"id":"8f0c…","label":"Основной","name":"Иван Петров","country":"RU","default":true}]}Needs “Purchases: registration and renewal”. Contacts are created in the account; the API returns only their id, label, name and country.
Register a domain
curl -X POST https://regzone.pro/api/v1/domains \
-H "Authorization: Bearer rz_…" \
-H "Idempotency-Key: 3b1f6c2e-7a4d-4f0e-9c1a-5d2b8e6f0a11" \
-H "Content-Type: application/json" \
-d '{"domain":"example.org","years":1,"contact":"8f0c…","autoRenew":true}'
HTTP/1.1 202 Accepted
{"order":{"id":"c41e…","status":"processing"},"domain":"example.org","years":1,"charged":{"minor":113480,"currency":"RUB"},"replayed":false}Body: domain — one full name; years — the term in years (1 by default; .ru and .рф take 1 only); contact — the owner contact’s id (the default contact if omitted); autoRenew — renew the domain automatically from the balance (false by default). 202 means the order is paid and the registration is under way — usually a minute. Follow it with GET /orders/{id}; the domain then appears in GET /domains if the key works with all of the account’s domains. Zones that need extra data (.ru, .рф, .us, .it) take it from the contact, filled in in the account.
Renew a domain
curl -X POST https://regzone.pro/api/v1/domains/example.com/renew \
-H "Authorization: Bearer rz_…" \
-H "Idempotency-Key: renew-example.com-2027" \
-H "Content-Type: application/json" \
-d '{"years":1}'
{"domain":{"name":"example.com","status":"active","expiresAt":"2028-10-02T00:00:00.000Z",…},"years":1,"charged":{"minor":113480,"currency":"RUB"},"replayed":false}Body: years — the term in years (1 by default; the body may be omitted). Only the key’s domains can be renewed. The response carries the domain with its new term. If the zone’s registry does not accept a renewal yet (.ru and .рф — two months before expiry), the answer is 409 renewal_not_open with the date.
Order
curl -H "Authorization: Bearer rz_…" https://regzone.pro/api/v1/orders/c41e…
{"order":{"id":"c41e…","status":"completed","total":{"minor":113480,"currency":"RUB"},"placedAt":"2026-10-03T09:15:00.000Z","items":[{"domain":"example.org","years":1}]}}A key sees only its own orders. status: processing — paid, registration under way; completed — the domain is registered; failed or refunded — it did not work out and the money went back to the balance.
Limits
- Up to 120 requests per minute per key; beyond that — 429.
- Responses carry X-RateLimit-Limit and X-RateLimit-Remaining headers; a 429 carries Retry-After with the number of seconds to wait.
- TTL from 60 seconds to 7 days.
- A new record reaches the public nameservers within 1–2 minutes (for domains on the default nameservers).
- At most 200 records per zone.
- A key works only with the domains it is bound to (or every domain of the account), and only while they are in your account. A domain moved to another customer leaves the key at once.
Errors
An error is an HTTP status and a body with a machine code (code), an explanation (message) and the request number (request_id):
HTTP/1.1 409 Conflict
X-Request-Id: req_5b1c0e7a9d2f4c68b3a1e0d97f246c15
{"error":{"code":"zone_not_active","message":"The domain is not active: its status is \"pending\". Records can be managed once it is \"active\".","details":{"status":"pending"},"request_id":"req_5b1c0e7a9d2f4c68b3a1e0d97f246c15"}}Every response carries an X-Request-Id header, and an error repeats it in request_id. Send it to support: it is how we find the request in the log.
Some errors carry a details field saying what exactly is wrong: zone_not_active has status (the domain’s current status); zone_not_served has nameservers (the nameservers recorded for the domain) and expected_nameservers (the ones the API manages records for); ip_not_allowed has ip (the address the request came from).
| 400 | idempotency_key_required | No Idempotency-Key header — without it a purchase could be repeated by accident. |
| 400 | invalid_json | The request body is not JSON. |
| 401 | invalid_token | No key, or the key is invalid, revoked or expired. |
| 402 | insufficient_balance | Not enough money on the balance — top it up in the account. |
| 403 | insufficient_scope | The key has no permission for this section or action. |
| 403 | ip_not_allowed | The key may not be used from this address: it is not in the key’s allowed list. |
| 403 | email_not_verified | Confirm the account e-mail address first. |
| 403 | purchase_limit_exceeded | The purchase costs more than this key’s per-purchase limit. |
| 403 | daily_limit_exceeded | The purchase does not fit this key’s 24-hour limit. |
| 403 | forbidden_record | The key’s permission does not allow this record. |
| 403 | zone_suspended | The domain is suspended — its zone cannot be changed. |
| 403 | domain_suspended | The domain is suspended — it cannot be changed. |
| 404 | zone_not_found | The key has no such zone. |
| 404 | record_not_found | No such record. |
| 404 | domain_not_found | The key has no such domain. |
| 404 | order_not_found | No such order for this key. |
| 409 | zone_not_active | The domain is not active; its status is in details.status. Records can be managed once the status is active. |
| 409 | zone_not_served | The domain is delegated to other nameservers — manage records there. details says which nameservers are recorded for the domain and which are needed. |
| 409 | zone_full | The zone already holds the maximum number of records. |
| 409 | platform_nameservers | The default nameservers are set with a button in the account. |
| 409 | not_supported | This change is not available for this domain. |
| 409 | domain_not_available | The domain is taken and cannot be registered. |
| 409 | price_unavailable | The price cannot be determined right now — retry later. |
| 409 | renewal_not_open | The zone’s registry does not accept the renewal yet — the response names the date. |
| 409 | renewal_in_progress | This domain is being renewed right now. |
| 409 | late_fee_required | Renewing now costs an extra fee — nothing was charged; contact support. |
| 409 | request_in_progress | A request with this Idempotency-Key is still running — retry in a minute. |
| 409 | previous_order_failed | The purchase with this Idempotency-Key already failed and the money went back. Use a new value to try again. |
| 413 | body_too_large | The request is too large. |
| 422 | invalid_body | The body is not JSON of the expected shape. |
| 422 | invalid_value | Invalid value, name, TTL or record type. |
| 422 | invalid_type | Unknown record type, or one this domain does not support. |
| 422 | invalid_name | Invalid record name, or a name from another zone. |
| 422 | invalid_ttl | TTL out of range. |
| 422 | priority_required | This record type needs a priority. |
| 422 | invalid_nameservers | Give 2 to 13 names like ns1.example.net. |
| 422 | invalid_domain | Give one full domain name. |
| 422 | invalid_years | The term is outside what the zone allows. |
| 422 | contact_required | The account has no contacts — add one in the account. |
| 422 | contact_not_found | No such contact — see GET /contacts. |
| 422 | contact_incomplete | The contact is incomplete — complete it in the account. |
| 422 | contact_missing_zone_data | The contact lacks data this zone needs — fill in its “Documents” section. |
| 422 | contact_not_eligible | The zone’s registry does not accept an owner from the contact’s country. |
| 429 | rate_limited | Too many requests — try again later. |
| 500 | internal_error | Unexpected error — try again shortly; if it keeps happening, send the request_id to support. |
| 502 | dns_unavailable | The zone could not be read — try again shortly. |
| 502 | dns_write_failed | The record could not be saved — try again in a minute. |
| 502 | registry_unavailable | The change did not go through — try again shortly. |
| 502 | check_failed | The check did not complete — try again shortly. |
| 502 | purchase_failed | The purchase did not go through and nothing was charged — retry shortly. |
| 502 | renewal_failed | The renewal did not go through; the money went back to the balance — retry shortly. |
| 502 | charge_failed | The balance could not be charged — retry shortly. |
| 503 | check_unavailable | The registry did not answer — try again shortly. |
| 503 | temporarily_unavailable | Changes are temporarily unavailable. |
| 503 | api_disabled | The API is temporarily disabled. |
Wildcard certificate with certbot
Two certbot scripts: the first adds the validation record, the second removes it after validation. Replace example.com with your domain.
#!/bin/sh
# /etc/letsencrypt/regzone-auth.sh — certbot --manual-auth-hook
set -eu
KEY=$(cat /etc/letsencrypt/regzone.key)
ZONE="example.com"
ID_FILE="/var/tmp/regzone-$CERTBOT_DOMAIN-$CERTBOT_VALIDATION.id"
curl -fsS -X POST "https://regzone.pro/api/v1/zones/$ZONE/records" \
-H "Authorization: Bearer $KEY" \
-H "Content-Type: application/json" \
-d "{\"type\":\"TXT\",\"name\":\"_acme-challenge.$CERTBOT_DOMAIN\",\"value\":\"$CERTBOT_VALIDATION\",\"ttl\":60}" \
| sed -n 's/.*"id":"\([^"]*\)".*/\1/p' > "$ID_FILE"
# The record reaches the public nameservers within 1–2 minutes.
sleep 90#!/bin/sh
# /etc/letsencrypt/regzone-cleanup.sh — certbot --manual-cleanup-hook
set -eu
KEY=$(cat /etc/letsencrypt/regzone.key)
ZONE="example.com"
ID_FILE="/var/tmp/regzone-$CERTBOT_DOMAIN-$CERTBOT_VALIDATION.id"
if [ -s "$ID_FILE" ]; then
curl -fsS -X DELETE "https://regzone.pro/api/v1/zones/$ZONE/records/$(cat "$ID_FILE")" \
-H "Authorization: Bearer $KEY"
rm -f "$ID_FILE"
fiSave the key to a file and issue the certificate — renewal uses the same scripts:
printf '%s' 'rz_…' > /etc/letsencrypt/regzone.key
chmod 600 /etc/letsencrypt/regzone.key
chmod 700 /etc/letsencrypt/regzone-auth.sh /etc/letsencrypt/regzone-cleanup.sh
certbot certonly --manual --preferred-challenges dns \
--manual-auth-hook /etc/letsencrypt/regzone-auth.sh \
--manual-cleanup-hook /etc/letsencrypt/regzone-cleanup.sh \
-d example.com -d '*.example.com'
# Renewal re-uses the same hooks:
certbot renewThe “SSL validation only” permission is enough. A certificate for example.com and *.example.com needs two TXT records on one name — the API supports that.
Security
- Treat the key like a password: keep it in a file with mode 600 and never put it in a URL.
- Give a key the narrowest permission and only the domains it needs.
- Every new key and every widening of a key’s access e-mails the account owner; a key is deleted in the account with one click and stops working at once.
- Give a key that buys a spending limit — then even a leaked key cannot spend more than that.
- Give the key the addresses of the server it runs on: from any other address even a leaked key is refused.
- Seven days before a key expires the account owner gets an email. A term cannot be extended — create a new key.