Skip to main content

API Reference

Base URL: https://api.nexdns.tech/v1

Authentication

All API requests require authentication using an API key. Pass the key in the Authorization header as a Bearer token. The API key must start with the nxd_ prefix.

Plan requirement: the REST API, the ISPmanager-compatible API and webhooks are available on Pro plans and above, as are the two capabilities this reference also covers: DNSSEC and secondary (slave) zones. A key on a plan without them gets a 403.

Authorization: Bearer nxd_your_api_key_here

The API authentication layer is stateless – each request is authenticated independently. There are no sessions or cookies.

Keep your API key secret. Do not share it in client-side code, public repositories, or URLs. If a key is compromised, revoke it immediately and create a new one.

Authentication Errors

Status Cause
401 Missing or invalid API key, expired key, or cancelled account
403 The key lacks the scope the endpoint needs, or the account's plan does not include API access - the plan gate answers 403, not 401, on every path.

Response Format

All responses are JSON. Successful responses have the following structure:

Single Resource

{
    "status": "success",
    "data": {
        "id": "xK9mQ2",
        "name": "example.com",
        ...
    }
}

Paginated List

{
    "status": "success",
    "data": [ ... ],
    "meta": {
        "total": 150,
        "page": 1,
        "per_page": 25,
        "last_page": 6
    }
}

Error Response

{
    "status": "error",
    "error": {
        "code": "validation_error",
        "message": "Validation failed.",
        "details": {
            "name": ["Domain name is required."]
        }
    }
}

Errors raised deeper in the platform - a quota, a blocked domain, a nameserver failure - carry the same error object but no status field. Branch on error.code, which is stable, rather than on the presence of status. Messages are English on every instance and in every locale by contract; error.code is the machine-readable part.

Public IDs

Every resource is identified by an opaque id (for example xK9mQ2), used in URL paths. Raw numeric database IDs are never exposed or accepted.

Pagination

List endpoints that return paginated results accept the following query parameters:

Parameter Type Default Description
page integer 1 Page number (minimum 1)
per_page integer 25 Items per page (1–100)

Rate Limits

Requests are counted per account, not per key, in a sliding one-minute window, and the budget comes from your plan - see the plan comparison on the pricing page for the figure. Unauthenticated requests are counted per IP address. Every response carries the current state of your budget, so you never have to guess:

Header Meaning
X-RateLimit-LimitRequests allowed in the window.
X-RateLimit-RemainingRequests left in the current window.
X-RateLimit-ResetUnix timestamp at which the window rolls over.
Retry-AfterSeconds to wait, sent on a 429.

For bulk work - importing a large zone, reconciling hundreds of records - read X-RateLimit-Remaining and pause before it reaches zero rather than retrying after a 429. The CLI does this for you.

A few operations carry their own, longer window on top of the request budget: one zone may be moved to another nameserver group three times a day. The response says which limit was hit.

Zones

Manage DNS zones. Requires zones.read for read operations and zones.write for write operations.

GET /v1/zones

List all zones for the authenticated user.

Query parameters

  • search – filter zones by name
  • page, per_page – pagination

Response fields

id, name, type (master/slave), status, ns_group, created_at, updated_at

GET /v1/zones/{id}

Get detailed information about a specific zone, including SOA data, nameservers, and record count.

Additional response fields

records_count, soa (primary_ns, admin_email, serial, refresh, retry, expire, minimum), nameservers (array), ns_group (id, slug, name)

POST /v1/zones

Create a new DNS zone. Refused with 409 when the domain already exists or overlaps another account's zone, and with 422 when the plan's zone limit is reached or the domain is blocked.

Request body (JSON)

{
    "name": "example.com",
    "type": "master",
    "ns_group": "eu"
}

A secondary (slave) zone instead:

{
    "name": "example.com",
    "type": "slave",
    "master_ip": "203.0.113.10"
}
  • name (required) – domain name
  • type"master" (default) or "slave"
  • ns_group – NS group to assign the zone to (optional, uses default if omitted)
  • master_ip - required for secondary zones; must be a valid public IP address

Returns 201 Created with the zone object.

PATCH /v1/zones/{id}

Move the zone into another nameserver group. This is the only PATCH on the API. Scope: zones.write.

Request Body (JSON)

{
    "ns_group": "eu"
}

Returns the zone as it stands after the move, in the same shape as GET /v1/zones/{id}. The group is named by its slug - the values GET /v1/ns-groups lists for your account; anything else is rejected with a 400.

The zone keeps answering throughout: the new nameservers are provisioned before the response, and the previous ones keep serving while resolvers refresh. Update the delegation at your registrar afterwards. A zone may be moved three times a day; beyond that the call returns 429.

DELETE /v1/zones/{id}

Delete a zone and all its records.

Returns 204 No Content on success.

GET /v1/zones/{id}/export

Export a zone in BIND format or as structured JSON.

Query parameters

  • format"bind" (default) returns BIND zone file text; "json" returns a structured array of records with name, type, content, and ttl

Records

Manage DNS records within a zone. All record endpoints are nested under a zone. Requires records.read for read operations and records.write for write operations.

GET /v1/zones/{zoneId}/records

List all records in a zone.

Query parameters

  • type – filter by record type (e.g. A, CNAME, MX)
  • name – filter by record name (substring match)
  • search – search in both name and content

Response fields

id, name, type, content, ttl, disabled, fields (type-specific parsed fields)

GET /v1/zones/{zoneId}/records/{recordId}

Get a single record by its ID.

POST /v1/zones/{zoneId}/records

Create a new DNS record.

Request body (JSON)

{
    "type": "A",
    "name": "www",
    "ttl": 3600,
    "content": "93.184.216.34"
}
  • type (required) – record type (A, AAAA, CNAME, MX, TXT, SRV, CAA, NS, PTR, ALIAS, DNAME, DS, TLSA)
  • name – record name relative to the zone (default: @ for zone apex)
  • ttl – time to live in seconds (default: 3600)
  • content – record value (IP for A/AAAA, hostname for CNAME/NS/PTR, text for TXT, mail server for MX)

Type-specific fields

  • MX: priority (default: 10)
  • SRV: priority, weight, port
  • CAA: flags (default: 0), tag (default: "issue")
  • DS: keytag, algorithm, digest_type
  • TLSA: usage, selector, matching_type

For MX, SRV, CAA, DS and TLSA, content carries the primary value only - the mail host, the SRV target, the CA domain, the bare hex digest - and everything else goes in the fields above. Sending assembled record data (for example 0 issue "letsencrypt.org" as a CAA content) is rejected with a 400 naming the field.

TTL belongs to the record set. Omit ttl when adding another value to a name that already exists and the existing TTL is kept; send one and every value at that name is retimed. A brand-new name defaults to 3600 seconds.

Returns 201 Created with the record object.

PUT /v1/zones/{zoneId}/records/{recordId}

Update an existing record. Only include the fields you want to change; omitted fields retain their current values.

{
    "content": "93.184.216.35",
    "ttl": 7200
}

Returns 200 OK with the updated record object. Note: the record ID may change after an update since it is computed from the record’s name, type, and content.

DELETE /v1/zones/{zoneId}/records/{recordId}

Delete a record from the zone.

Returns 204 No Content on success.

DNSSEC

Manage DNSSEC for your zones. Requires zones.read to view status and zones.write to enable or disable.

GET /v1/zones/{id}/dnssec

Get DNSSEC status for a zone, including keys and DS records.

Response fields

enabled (boolean), keys (array of DNSKEY records), ds_records (array of DS records to set at registrar)

POST /v1/zones/{id}/dnssec/enable

Enable DNSSEC for a zone. Generates signing keys automatically.

Returns DNSSEC status with generated keys and DS records.

POST /v1/zones/{id}/dnssec/disable

Disable DNSSEC for a zone. Removes all signing keys.

Returns {"enabled": false, "keys": [], "ds_records": []}.

NS Groups

List the available nameserver groups. Use a group's id as ns_group when creating a zone. Any valid API key can read this endpoint.

GET /v1/ns-groups

List the active nameserver groups.

Response fields per group

id, name, slug

Account

View account information and manage API keys.

GET /v1/account

Get current account information, including subscription details.

Response fields

id (UUID), email, name, role, status, language, timezone, created_at

subscription – object with plan, billing_cycle, status, current_period_start, current_period_end (or null if no subscription)

GET /v1/account/api-keys

List all API keys for the authenticated user.

Response fields per key

id, name, key_prefix (first 8 chars), permissions (array), last_used_at, expires_at, created_at

POST /v1/account/api-keys

Create a new API key.

Request body (JSON)

{
    "name": "CI/CD Pipeline",
    "permissions": ["zones.read", "records.read", "records.write"],
    "expires_at": "2027-01-01"
}
  • name (required) – human-readable name (max 255 characters)
  • permissions (required) – array of permissions (at least one required): zones.read, zones.write, records.read, records.write, webhooks.read, webhooks.write
  • expires_at – optional expiration date (ISO 8601 or YYYY-MM-DD); must be in the future

The response includes the full API key in the key field. This is the only time the full key is returned. Store it securely.

Returns 201 Created with the key details including the full key value.

DELETE /v1/account/api-keys/{id}

Revoke (permanently delete) an API key.

Returns 204 No Content on success.

Billing

View subscription, plans, and invoices. These endpoints are read-only.

GET /v1/billing/subscription

Get current subscription details. Returns null if no active subscription.

Response fields

id, plan, billing_cycle, status, current_period_start, current_period_end, created_at

GET /v1/billing/plans

List all available plans with pricing and features.

Response fields per plan

id, name, slug, description, price_monthly, price_yearly, currency, max_domains, max_records, features (array)

GET /v1/billing/invoices

List the authenticated user's invoices, newest first.

Query parameters

  • status – filter by status (draft, issued, sent, void)
  • page, per_page – pagination

GET /v1/billing/invoices/{id}

Get a single invoice by its <code>id</code>.

Response fields

id, number, status, amount (gross), net_amount, tax_amount, tax_rate, currency, issued_at, created_at. Monetary values are plain decimal strings.

Webhooks

Manage outgoing webhook subscriptions to receive real-time notifications about zone and record changes. Requires webhooks.read for read operations and webhooks.write to create, update, or delete.

GET /v1/webhooks

List all webhook subscriptions for the authenticated user.

Response fields per webhook

id, url, events (array), description, is_active, failure_count, last_triggered_at, created_at

POST /v1/webhooks

Create a webhook subscription.

Request body (JSON)

{
    "url": "https://example.com/webhook",
    "events": ["zone.created", "record.created"],
    "description": "Production webhook"
}
  • url (required) – HTTPS endpoint that will receive the event payloads
  • events (required) – array of event types to subscribe to
  • description – optional human-readable label

The response includes a secret used to verify webhook signatures (HMAC). This is the only time the secret is returned. Store it securely.

Returns 201 Created with the webhook id and secret.

GET /v1/webhooks/{id}

Get a single webhook subscription together with its most recent deliveries.

PUT /v1/webhooks/{id}

Update a webhook subscription. Include only the fields you want to change.

Returns 200 OK with the updated webhook object.

DELETE /v1/webhooks/{id}

Delete a webhook subscription.

Returns 204 No Content on success.

POST /v1/webhooks/{id}/test

Send a test event to the webhook endpoint to verify it is reachable.

Queues a test delivery with a "type": "test" payload.

Verifying a Delivery

Every delivery is signed with the secret returned when the webhook was created, and carries four headers:

X-NexDNS-Signature: sha256=<hmac>
X-NexDNS-Timestamp: 1785370265
X-NexDNS-Event: record.created
X-NexDNS-Delivery: 42

Recompute HMAC-SHA256 over the exact raw request body using your secret, and compare it with the hex digest after sha256= in a constant-time comparison. A mismatch means the request did not come from us. X-NexDNS-Delivery identifies the attempt, so retries of the same event share an event id but not a delivery number.

Delivery Payload

{
    "id": "evt_szpj9u04z8u0",
    "type": "record.created",
    "created_at": "2026-07-30 00:31:05",
    "data": {
        "zone": { "name": "example.com" },
        "record": { "name": "www.example.com.", "type": "A", "content": "203.0.113.10", "ttl": 3600 }
    }
}

Available event types

Subscribe to any combination of these event types:

zone.created, zone.updated, zone.deleted, record.created, record.updated, record.deleted, dnssec.enabled, dnssec.disabled, zone.health.problem, zone.health.resolved

Error Codes

All errors follow a consistent format with an error code string and a human-readable message.

HTTP Status Error Code Description
400 validation_error Request body failed validation. Check details for field-specific errors.
401 unauthorized Missing, invalid, or expired API key.
403 forbidden The API key lacks the required scope, the account's plan does not include API access, or the plan does not include the capability being used (for example secondary zones or DNSSEC).
404 not_found The requested resource does not exist or is not accessible by the authenticated user.
409 conflict Resource already exists (e.g. duplicate zone name).
422 quota_exceeded Your plan's zone or record limit is reached.
422 domain_blacklisted The domain is on the blocked list and cannot be added.
429 rate_limit_exceeded Too many requests. Check Retry-After header.
500 server_error An unexpected internal error occurred. Please try again, or contact support if it persists.
502 dns_server_error The nameservers are temporarily unavailable. The request was not applied; retry it.

Code Examples

All examples use curl. Replace nxd_your_api_key with your actual API key.

List your zones

curl -s "https://api.nexdns.tech/v1/zones" \
    -H "Authorization: Bearer nxd_your_api_key"

Create a zone

curl -s -X POST "https://api.nexdns.tech/v1/zones" \
    -H "Authorization: Bearer nxd_your_api_key" \
    -H "Content-Type: application/json" \
    -d '{"name": "example.com"}'

Add an A record

curl -s -X POST "https://api.nexdns.tech/v1/zones/{zoneId}/records" \
    -H "Authorization: Bearer nxd_your_api_key" \
    -H "Content-Type: application/json" \
    -d '{
        "type": "A",
        "name": "www",
        "ttl": 3600,
        "content": "93.184.216.34"
    }'

Add an MX record

curl -s -X POST "https://api.nexdns.tech/v1/zones/{zoneId}/records" \
    -H "Authorization: Bearer nxd_your_api_key" \
    -H "Content-Type: application/json" \
    -d '{
        "type": "MX",
        "name": "@",
        "ttl": 3600,
        "content": "mail.example.com",
        "priority": 10
    }'

Update a record

curl -s -X PUT "https://api.nexdns.tech/v1/zones/{zoneId}/records/{recordId}" \
    -H "Authorization: Bearer nxd_your_api_key" \
    -H "Content-Type: application/json" \
    -d '{"content": "93.184.216.35", "ttl": 7200}'

Delete a record

curl -s -X DELETE "https://api.nexdns.tech/v1/zones/{zoneId}/records/{recordId}" \
    -H "Authorization: Bearer nxd_your_api_key"

Export a zone (BIND format)

curl -s "https://api.nexdns.tech/v1/zones/{zoneId}/export" \
    -H "Authorization: Bearer nxd_your_api_key"

Enable DNSSEC

curl -s -X POST "https://api.nexdns.tech/v1/zones/{zoneId}/dnssec/enable" \
    -H "Authorization: Bearer nxd_your_api_key"

Create an API key

curl -s -X POST "https://api.nexdns.tech/v1/account/api-keys" \
    -H "Authorization: Bearer nxd_your_api_key" \
    -H "Content-Type: application/json" \
    -d '{
        "name": "Read-only key",
        "permissions": ["zones.read", "records.read"],
        "expires_at": "2027-12-31"
    }'

Get account info

curl -s "https://api.nexdns.tech/v1/account" \
    -H "Authorization: Bearer nxd_your_api_key"

We use cookies to ensure the proper functioning of this website and to improve your experience. Some cookies are strictly necessary for the site to operate, while others are optional.

You can accept all cookies or limit your choice to strictly necessary ones. For details, see our Privacy Policy and Cookie Policy.