Skip to content

Change a domain's nameservers

6 steps · Checked against the API code 3 November 2026

A guide does one task from start to finish. Every endpoint, with all its fields and cases, is in the API reference.

The nameservers decide which DNS service answers for a domain. Changing them moves the domain’s DNS to another provider, to your own servers or back to Hostup. The domain stays registered at Hostup. The API answers at once with a job. The change at the registry happens in that job.

  • An API key with the scopes read:domains and write:domains (Keys and scopes).
  • The requests read the key from $HOSTUP_API_KEY. The Quickstart shows how to set it.
  • The nameservers you are moving to. A DNS provider gives you their names when you add the domain there. Hostup’s own are primary.ns.hostup.se and secondary.ns.hostup.se.
  • jq and wait-for-job.sh for step 5.

Look the domain up by name with GET /api/v2/domains. Every domain endpoint takes its dom_… ID. IDs explains the prefix. Put your own domain in place of example.se here and in the requests below.

GET /api/v2/domains
curl "https://cloud.hostup.se/api/v2/domains?name=example.se" \
-H "Authorization: Bearer $HOSTUP_API_KEY"
Response 200
{
"data": [
{
"id": "dom_01hxa3b4c5d6e7f8g9h0j1k2m3",
"name": "example.se",
"serviceStatus": "active",
"…": "…"
}
],
"hasMore": false,
"nextCursor": null
}
export DOMAIN_ID="dom_…" # data[0].id above

Read the domain’s nameservers with GET /api/v2/domains/{id}/nameservers.

GET /api/v2/domains/{id}/nameservers
curl "https://cloud.hostup.se/api/v2/domains/$DOMAIN_ID/nameservers" \
-H "Authorization: Bearer $HOSTUP_API_KEY"
Response 200
{
"nameservers": ["primary.ns.hostup.se", "secondary.ns.hostup.se"],
"registrarLock": { "enabled": false, "…": "…" },
"actions": {
"canChangeNameservers": { "allowed": true, "reason": null }
},
"…": "…"
}

nameservers is the current list. Go on when actions.canChangeNameservers.allowed is true. Only an active domain passes. When it is false, reason says why:

  • A transfer is in progress. Wait until it has finished. Follow the transfer shows how.
  • The domain is pending, suspended or expired. An expired domain must be renewed first.
  • The domain has moved to another registrar. Change the nameservers there.
  • The registry lock is on. This only happens on .se and .nu domains. See below.

Then registrarLock.enabled is true. This is the .se and .nu registry lock, not the transfer lock at another registrar. Only registrarLock.enabled says whether the lock is on. unlockAction is set on every .se and .nu domain, locked or not.

The unlock request needs a BankID login. An API key cannot provide one. Sign in to the client area and send the request from the domain’s Settings tab. Hostup checks the request and removes the lock by hand. This takes a few working days. Read the nameservers again later. actions.canChangeNameservers.allowed turns true once the lock is off.

The change itself does not test the nameservers. Test them first with POST /api/v2/domains/nameservers/validate. This call needs no API key. One IP address can make 30 calls a minute. Send domain too. For a .se or .nu domain it adds a check at the registry.

POST /api/v2/domains/nameservers/validate
curl -X POST "https://cloud.hostup.se/api/v2/domains/nameservers/validate" \
-H "Content-Type: application/json" \
-d '{"nameservers": ["ns1.example.net", "ns2.example.net"], "domain": "example.se"}'
Response 200
{
"results": [
{ "ns": "ns1.example.net", "isValid": true, "errorCode": null, "errorMessage": null },
{ "ns": "ns2.example.net", "isValid": true, "errorCode": null, "errorMessage": null }
]
}

An entry whose isValid is false has an errorCode:

  • NOT_FOUND: the name has no A or AAAA record in public DNS. Check the spelling with your DNS provider.
  • DNS_RESOLUTION_FAILED: the lookup got no answer. Try again shortly.
  • INVALID_FORMAT or EMPTY_VALUE: the entry is not a hostname.
  • GLUE_HOST_NOT_REGISTERED: the .se or .nu registry has no record of this nameserver. For a nameserver under your own domain, register it as shown below. For one under another domain, the owner of that domain has to register it.

If the nameservers are under the domain itself

Section titled “If the nameservers are under the domain itself”

This is the case for ns1.example.se on example.se. The registry then needs their IP addresses (glue records) before it accepts them.

For a .se or .nu domain, register each nameserver with POST /api/v2/domains/{id}/glue-records before the change in step 4. Send one call per nameserver. The hostname must be under the domain. The addresses must be public.

POST /api/v2/domains/{id}/glue-records
curl -X POST "https://cloud.hostup.se/api/v2/domains/$DOMAIN_ID/glue-records" \
-H "Authorization: Bearer $HOSTUP_API_KEY" \
-H "Content-Type: application/json" \
-d '{"hostname": "ns1.example.se", "ips": ["192.0.2.53", "2001:db8::53"]}'
Response 201
{
"hostname": "ns1.example.se",
"ips": ["192.0.2.53", "2001:db8::53"]
}

Sending the same hostname again replaces its addresses. List glue records shows the nameservers the registry has for the domain.

Until the switch, the check above can answer NOT_FOUND for ns1.example.se. It looks the name up through the domain’s current nameservers. A glue record adds no A or AAAA record there. The change in step 4 does not depend on that check.

Other extensions take the addresses with the change itself. See With glue addresses for other extensions in step 4.

POST /api/v2/domains/{id}/nameservers replaces the whole list. Send every nameserver the domain should have. The list follows these rules:

  • Two to ten nameservers. Empty entries are dropped before the count.
  • A .se or .nu domain also accepts a single nameserver, unless it is one of Hostup’s.
  • For DNS at Hostup, send both primary.ns.hostup.se and secondary.ns.hostup.se. Hostup prepares the domain’s DNS zone only when every nameserver in the list is Hostup’s.
POST /api/v2/domains/{id}/nameservers
curl -X POST "https://cloud.hostup.se/api/v2/domains/$DOMAIN_ID/nameservers" \
-H "Authorization: Bearer $HOSTUP_API_KEY" \
-H "Content-Type: application/json" \
-d '{"nameservers": ["ns1.example.net", "ns2.example.net"]}'
Response 202
{
"operation": {
"status": "in_progress",
"jobId": "dbj_01hxa3b4c5d6e7f8g9h0j1k2m3",
"pollUrl": "/api/jobs/dbj_01hxa3b4c5d6e7f8g9h0j1k2m3"
}
}
export JOB_ID="dbj_…" # operation.jobId above

List a domain’s DNS records needs the scope read:dns as well. If the new provider signs the zone, add its DS record after the switch with Add a DNSSEC record.

A nameserver under the domain itself needs its IP address at the registry. A .se or .nu domain has it as a glue record from step 3. Any other domain takes the addresses in nsips with this change. Give one address per nameserver, in the same order as nameservers. Leave "" for a nameserver outside the domain.

POST /api/v2/domains/{id}/nameservers
curl -X POST "https://cloud.hostup.se/api/v2/domains/$DOMAIN_ID/nameservers" \
-H "Authorization: Bearer $HOSTUP_API_KEY" \
-H "Content-Type: application/json" \
-d '{"nameservers": ["ns1.example.com", "ns2.example.com"], "nsips": ["192.0.2.53", "192.0.2.54"]}'

The answer is the same 202 with a job.

Poll the job at GET /api/jobs/{jobId}. The job endpoint is older than v2. Its answer has the job inside data. Jobs explains every field.

GET /api/jobs/{jobId}
curl "https://cloud.hostup.se/api/jobs/$JOB_ID" \
-H "Authorization: Bearer $HOSTUP_API_KEY"
Response 200
{
"success": true,
"timestamp": "2026-11-02T10:15:41.000Z",
"requestId": "11111111-1111-1111-1111-111111111111",
"data": {
"jobId": "dbj_01hxa3b4c5d6e7f8g9h0j1k2m3",
"type": "nameservers_update",
"status": "completed",
"progress": 100,
"error": null,
"finishedAt": "2026-11-02T10:15:38.000Z",
"…": "…"
}
}

Run wait-for-job.sh to poll until the job ends:

./wait-for-job.sh "/api/jobs/$JOB_ID"

The job is done once the registry has taken the change. The registry can take a little longer to publish the new nameservers.

data.result has no documented shape for this job. It does not say whether the registry lists the new nameservers yet. Step 6 shows what the domain and public DNS say.

A failed job has data.status set to failed. data.error is "Job failed." and data.progressText is "Operation failed". Neither says why. Poll the job once more with the request above. Then check these in order:

  1. Read the nameservers again as in step 2. The domain may have been locked or may no longer be active.
  2. Look for data.progressDetails.contactRecovery. When it is there, the registry needs more details about the domain holder. Its reason says what is missing. Fields whose target is account are changed in the account profile with Update your account profile. That call needs the scope write:account. Fields whose target is registrationDetails go in recoveryValues with the next change, keyed by their key.
  3. A nameserver under the domain itself needs its addresses at the registry. On a .se or .nu domain, List glue records must list it. Register it as in step 3 if it is missing. On other domains, send the change again with its address in nsips.
  4. If none of these apply, send the change again after a few minutes. When the job cannot read the domain’s current delegation, it stops before it changes anything at the registry.

Read the nameservers again with GET /api/v2/domains/{id}/nameservers. Once the job has completed, the list shows the new nameservers.

GET /api/v2/domains/{id}/nameservers
curl "https://cloud.hostup.se/api/v2/domains/$DOMAIN_ID/nameservers" \
-H "Authorization: Bearer $HOSTUP_API_KEY"
Response 200
{
"nameservers": ["ns1.example.net", "ns2.example.net"],
"…": "…"
}

Then ask public DNS which nameservers it sees, with POST /api/v2/domains/nameservers/lookup. This call needs no API key. One IP address can make 30 calls a minute here too.

POST /api/v2/domains/nameservers/lookup
curl -X POST "https://cloud.hostup.se/api/v2/domains/nameservers/lookup" \
-H "Content-Type: application/json" \
-d '{"domain": "example.se"}'
Response 200
{
"domain": "example.se",
"nameservers": ["ns1.example.net", "ns2.example.net"]
}

Resolvers that cached the old nameservers keep using them until the cache runs out. An empty nameservers list means DNS gave no answer.

  • Hostup sends no e-mail about a nameserver change. It costs nothing and creates no invoice.
  • Before the change, Hostup saves the records the old nameservers gave for the domain and its www name. List DNS snapshots shows them.
  • A move to Hostup’s nameservers runs as the job type nameservers_precheck. That job first makes sure the domain has a DNS zone at Hostup. It waits up to 30 seconds for Hostup’s nameservers to answer for the zone. Then it sends the change. Other nameservers run as nameservers_update. When the domain already has exactly these nameservers and you sent no nsips, the job finishes without a registry update.
  • A move away from Hostup leaves the domain’s DNS zone at Hostup in place. Its records stop answering for the domain once the registry has switched.
  • When the domain has automatic DNSSEC at Hostup, the change pauses it for a cooldown. dnssecAutoWillBeBlocked in step 2 is true in that case. Get automatic DNSSEC status shows when the pause ends in blockedUntil.

Next: Point a domain at your server when the domain is on Hostup’s nameservers.

  • 409 nameserver_update_blocked from the change: actions.canChangeNameservers.allowed from step 2 is false. detail and actions.canChangeNameservers.reason say why. Fix that first.
  • 400 invalid_request from the change: the list breaks a rule from step 4. detail says which.
  • 404 domain_not_found: the ID is wrong or the domain belongs to another account.
  • 403 forbidden: the key lacks write:domains. requiredScopes lists the scopes the call needs. See When a key is refused.
  • 500 internal_error from Request removal of a registry lock: an API key cannot send a new unlock request. Retrying gives the same answer. Send the request from the client area, as in step 2. With a key the call only reports status as already_unlocked or already_requested.