Domain API Rate Limit, Soft Quota & Throttling Policy

Domain Name API rate limit, soft quota and throttling policy

Two numbers run everything on this page: 5 and 200. Five requests per second per API key, and a quota threshold of two hundred points per area. Learn those and you already know the policy — the rest is arithmetic and a handful of code snippets.

Domain Name API applies two independent controls to every API key. The first is a hard rate limit: exceed it and you get an HTTP 429. The second is a soft quota, and it behaves nothing like the first — it never rejects a request, it slows one down. Understanding that difference is the whole point of this article, because an integration that confuses the two will spend an afternoon chasing a bug that does not exist.

The 30-second version

Send at most 5 requests per second across all endpoints, and no more than 2 per second of them to /domains/search and /domains/bulk-search; go faster and you get HTTP 429 with a Retry-After header. Separately, every call costs points: lookups (/domains/search, /domains/info, /products/info) cost 1 point, every other endpoint costs 10. Your points decay by 20% per minute, and below 200 points nothing happens. Go above that and nothing fails — your responses simply start arriving about half a second late, climbing to a 5-second ceiling if you keep the pace. Ease off and the delay winds back down. The limits are counted per API key, and points are tracked separately for each area.

Contents

Rules at a Glance

Everything the policy says, in one table. Forwarding this table to your integration team is usually enough.

Subject Rule
API base address https://api.domainresellerapi.com/api/v1
Who is counted Your API key. Points are tracked separately per area: domains (including name servers, DNSSEC and DNS records), contacts, SSL, hosting, products, orders, balance.
Rate limit 5 requests per second across all endpoints; within those, 2 per second for /domains/search and /domains/bulk-search; a separate 20 per second for /domains/zones; no per-second limit for GET /domains/info and GET /products/tlds
Above the rate limit HTTP 429 with an empty body and a Retry-After header — the request is rejected and costs no points
Soft quota Points decay by 20% per minute; nothing happens below 200 points per area
Cost of lookups: /domains/search, /domains/info, /products/info 1 point
Cost of every other endpoint 10 points (bulk-search included); the TLD catalogue /products/tlds is free
Above the soft quota Responses are delayed, not rejected. No error code is returned.
Size of the delay Starts around 0.5 s and climbs to a 5 s ceiling if the pace continues
Recovery Drop back under the quota and the delay winds down again
Safe pace for 1-point calls About 45 lookups a minute stay at full speed indefinitely; from a standing start, about 200 before any slowdown
Safe pace for 10-point calls About 4 a minute stay at full speed indefinitely; from a standing start, about 20 before any slowdown

Two Layers: Rate Limit vs. Soft Quota

These two controls are measured differently, react differently and are fixed differently. Almost every support ticket on this topic begins with someone treating them as one thing.

Hard limit

Rate limit — 5 requests / second

This one counts requests, not value. Five per second is the ceiling for each API key, summed across every thread, worker, container and cron job that uses it — and within those five, domain searches (search and bulk-search) may use at most two.

  • Breaking it returns HTTP 429 with a Retry-After header, and the request does not run
  • It reacts instantly — a single busy second is enough
  • The fix is one shared limiter in your code
Soft limit

Soft quota — 200 points, decaying 20% a minute

This one counts value, not requests. A cheap lookup and an expensive registration are not the same load on the systems behind us, so they do not carry the same price.

  • Breaking it delays your responses — nothing is rejected
  • It is a brake, not a penalty, and it releases on its own
  • The fix is pacing, or fewer expensive calls
Both limits are counted per API key, and the points quota is tracked separately per area — domains (including name servers, DNSSEC and DNS records), contacts, SSL, hosting, products, orders and balance. Heavy domain searching does not slow your SSL calls.

What Each Request Costs

Lookups are cheap because they are read-only and they are the ones your customers trigger live on your website while they are deciding what to buy. Everything else reaches a registry, a billing record or a certificate authority, so it carries the real cost.

Endpoint Method Cost
/api/v1/domains/search — single availability check POST 1 point
/api/v1/domains/info — domain detail GET 1 point
/api/v1/products/info — product detail GET 1 point
/api/v1/products/tlds — TLD catalogue GET Free
/api/v1/domains/bulk-search POST 10 points
/api/v1/domains/register, /renew, /transfer, /restore POST 10 points
/api/v1/domains/dns/*, /zones, /forwards, /lock, /privacy any 10 points
/api/v1/contacts/* any 10 points
/api/v1/ssls/*, /hostings/*, /products/* (except the two above), /order/*, /deposit/* any 10 points
Anything not listed above any 10 points
The rule for bulk-search surprises people. It costs a flat 10 points no matter how many names are in the array (up to 30 per call). Ten names is the break-even point: below ten, looping /domains/search is cheaper; at exactly ten it is a tie; above ten, bulk-search wins and keeps winning. Checking 300 names costs 300 points one at a time, or 100 points in ten bulk calls.

Your Real Budget, in Numbers

The two limits interact, and which one you actually meet depends entirely on what you are calling. This is the table worth pinning above your desk.

What you are calling Calls inside 200 points Which limit stops you first Safe sustained pace
Single lookups only (1 point each) about 200 The points quota, once you pass about 45 a minute about 45 / minute
Anything else (10 points each) about 20 The soft quota about 4 / minute
Mixed: one search plus one registration per domain (11 points) about 18 domains The soft quota about 3–4 domains / minute

Worked example: checking 500 domains

One at a time through /domains/search, that is 500 calls and 500 points. At the 2-per-second search cap the wall clock is at least 250 seconds, and somewhere past the first 200 lookups the points quota starts adding delay as well. Through bulk-search in batches of 30, the same job is 17 calls and 170 points — under the 200-point threshold, so it runs at full speed in about 9 seconds. Same answer, far faster, and it spends 170 of your points instead of 500.

Worked example: registering 100 domains

A hundred registrations cost 1,000 points in the domains area. From a standing start, about the first 20 run at full speed; after that each response is held back — starting at half a second and rising to a ceiling of 5 seconds — for as long as you stay above the threshold. Nothing is rejected, so the job still completes; it simply takes minutes rather than seconds, and no amount of parallelism changes that, because every worker using the same key draws on the same points. Plan for it: run large batches off-peak, keep your client timeout well above 5 seconds, and watch X-Quota-Delay rather than retrying harder.

Search-only integrations can reach the quota too. At the 2-per-second search cap you add 120 points a minute, while near the threshold the 20% decay removes only about 40. From a standing start you get about 200 lookups at full speed; after that, a sustained pace of about 45 lookups a minute stays fast. For lists, bulk-search (up to 30 names for 10 points) is the cheaper route.

What Happens When the Quota Fills

Nothing dramatic, which is exactly the problem — the brake is quiet, and quiet symptoms are the ones that cost you an afternoon. Every response tells you where you stand: X-Quota-Scope (the area), X-Quota-Cost (1 or 10), X-Quota-Used, X-Quota-Limit (200), X-Quota-Delay (seconds of delay applied; 0 means full speed) and X-Quota-Reset (seconds until you are back under the threshold).

Your situation What the API does
Under 200 points in this area Nothing at all. Normal response times.
The quota has just filled Each further response is held back by roughly 0.5 seconds
You keep the same pace The hold grows step by step
You keep going regardless The hold reaches its ceiling of 5 seconds and stays there
You slow down under the quota The hold winds back down and normal timing returns
Set your HTTP client timeout to at least 10 seconds. The soft quota never returns an error, so a client configured to give up after 3 seconds will start throwing timeouts the moment the brake engages — and the exception message will point at the network, not at the quota. Most of the time this policy costs anyone is lost to exactly that misdiagnosis.

How to Fix an HTTP 429

A 429 only ever means one thing here: one of your API key’s per-second caps was exceeded — more than five requests in the same second, or more than two domain searches. It has nothing to do with the points quota and nothing to do with how many domains you manage. The response has an empty body and a Retry-After header, and a rejected request costs no points. Work through these in order.

  • Stop sending. Continuing to hammer the endpoint keeps you above the line and turns one 429 into a stream of them.
  • Wait the number of seconds in Retry-After (always a whole number, at least 1), then retry.
  • Retry once. A single 429 after a burst is normal and one retry usually clears it.
  • If it repeats, pace instead of retrying — send requests one after another through a single queue rather than in bursts.
  • Then find the real cause. Nine times out of ten it is parallel workers that each carry their own rate limiter, so four workers at “5 per second” are really sending twenty.

Code Examples

The shape that works is always the same: one gate for the whole process, a minimum interval between requests, and a retry that waits instead of insisting. These examples pace at two requests per second, which keeps you inside both the 5-per-second cap and the 2-per-second search cap. For long batches of 10-point calls, a gap of about 15 seconds (4 a minute) keeps you under the points threshold indefinitely.

C# (.NET)

// One gate for the whole process. Every worker passes through it.
private static readonly SemaphoreSlim Gate = new(1, 1);
private static DateTime _next = DateTime.UtcNow;

// 500 ms -> 2 req/sec: inside the 5/sec cap and the 2/sec search cap.
private static readonly TimeSpan MinInterval = TimeSpan.FromMilliseconds(500);

async Task<HttpResponseMessage> SendAsync(Func<HttpRequestMessage> build)
{
    var backoff = TimeSpan.FromSeconds(1);

    for (var attempt = 1; attempt <= 6; attempt++)
    {
        await Gate.WaitAsync();
        try
        {
            var wait = _next - DateTime.UtcNow;
            if (wait > TimeSpan.Zero) await Task.Delay(wait);
            _next = DateTime.UtcNow + MinInterval;
        }
        finally { Gate.Release(); }

        // HttpRequestMessage is single-use: build a fresh one per attempt.
        var response = await _http.SendAsync(build());
        if ((int)response.StatusCode != 429) return response;

        var after = response.Headers.RetryAfter?.Delta ?? backoff;
        response.Dispose();
        await Task.Delay(after);
        backoff = TimeSpan.FromSeconds(Math.Min(backoff.TotalSeconds * 2, 30));
    }

    throw new HttpRequestException("Rate limit did not clear after 6 attempts.");
}

PHP (WHMCS, WordPress, cPanel)

<?php
// 500000 microseconds = 0.5 s -> 2 req/sec (inside the 5/sec and 2/sec search caps).
const DNA_MIN_INTERVAL_US = 500000;

function dna_request(string $method, string $path, ?array $body = null) {
    static $next = 0;

    $backoff = 1;
    for ($attempt = 1; $attempt <= 6; $attempt++) {
        $now = (int) (microtime(true) * 1000000);
        if ($now < $next) usleep($next - $now);
        $next = (int) (microtime(true) * 1000000) + DNA_MIN_INTERVAL_US;

        $ch = curl_init('https://api.domainresellerapi.com/api/v1' . $path);
        curl_setopt_array($ch, [
            CURLOPT_CUSTOMREQUEST  => $method,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT        => 30,   // never below 10
            CURLOPT_HTTPHEADER     => [
                'Content-Type: application/json',
                '__reseller: ' . DNA_RESELLER_ID,
                'X-API-KEY: '  . DNA_API_KEY,
            ],
            CURLOPT_POSTFIELDS => $body === null ? null : json_encode($body),
        ]);

        $raw    = curl_exec($ch);
        $status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
        curl_close($ch);

        if ($status !== 429) return json_decode($raw, true);

        sleep($backoff);
        $backoff = min($backoff * 2, 30);
    }

    throw new RuntimeException('Rate limit did not clear after 6 attempts.');
}

Python (scripts and automation)

import threading, time, requests

BASE = "https://api.domainresellerapi.com/api/v1"
HEADERS = {"__reseller": RESELLER_ID, "X-API-KEY": API_KEY}

# 0.5 s -> 2 req/sec: inside the 5/sec cap and the 2/sec search cap.
MIN_INTERVAL = 0.5
_lock, _next = threading.Lock(), 0.0


def dna_request(method, path, **kwargs):
    global _next
    backoff = 1.0

    for _ in range(6):
        with _lock:                       # one gate, every thread
            wait = _next - time.monotonic()
            if wait > 0:
                time.sleep(wait)
            _next = time.monotonic() + MIN_INTERVAL

        # timeout must clear the 5 s soft-quota ceiling with room to spare
        r = requests.request(method, BASE + path, headers=HEADERS,
                             timeout=30, **kwargs)
        if r.status_code != 429:
            return r.json()

        time.sleep(float(r.headers.get("Retry-After", backoff)))
        backoff = min(backoff * 2, 30)

    raise RuntimeError("Rate limit did not clear after 6 attempts.")

Node.js (JavaScript / TypeScript)

const BASE = 'https://api.domainresellerapi.com/api/v1';
const MIN_INTERVAL = 500;          // 2 req/sec: inside the 5/sec and 2/sec search caps
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));

// A promise chain is the simplest single gate in Node: awaiting `tail`
// serialises every caller, no matter how many started in parallel.
let tail = Promise.resolve();

function pace() {
  const turn = tail.then(() => sleep(MIN_INTERVAL));
  tail = turn;
  return turn;
}

export async function dnaRequest(method, path, body) {
  let backoff = 1000;

  for (let attempt = 1; attempt <= 6; attempt++) {
    await pace();

    const res = await fetch(BASE + path, {
      method,
      headers: {
        'Content-Type': 'application/json',
        __reseller: process.env.DNA_RESELLER_ID,
        'X-API-KEY': process.env.DNA_API_KEY,
      },
      body: body ? JSON.stringify(body) : undefined,
      signal: AbortSignal.timeout(30000),   // never below 10000
    });

    if (res.status !== 429) return res.json();

    const after = Number(res.headers.get('Retry-After')) * 1000 || backoff;
    await sleep(after);
    backoff = Math.min(backoff * 2, 30000);
  }

  throw new Error('Rate limit did not clear after 6 attempts.');
}

Request Flow Diagram

Every call travels the same path. The two checkpoints are independent, and only one of them can reject you.

1. Your queue

One shared gate releases a request. Nothing else in your process is allowed to bypass it.

2. Rate check

More than 5 requests this second on your key, or more than 2 searches? The answer is HTTP 429 with Retry-After, and the request stops here.

3. Quota check

Points are added to the area’s running total, which decays 20% a minute. Over 200? The response is held back, then sent anyway.

4. Your handler

A 429 means back off and retry. A slow 200 means slow down — nothing is broken.

Best Practices That Actually Help

  • One limiter per process, not per worker. If each thread carries its own, your real rate is the limit multiplied by your thread count. This single mistake causes most 429s.
  • Reach for bulk-search above ten names. One call, up to 30 names, 10 points, one round trip. The saving compounds fast on long lists.
  • Cache aggressively, and de-duplicate before you send. A name you checked four seconds ago has not changed, and long input lists are full of repeats.
  • Keep the client timeout at 10 seconds or more. It has to clear the 5-second ceiling with room to spare, or the brake reaches you disguised as a network error.
  • Stagger your scheduled jobs. If every cron in your fleet starts at the top of the minute, you have built a burst on purpose. A random offset of a few seconds costs nothing and fixes it.
  • Watch your own 429 rate and the X-Quota-Delay header. They are the earliest signals available to you, and they climb long before a customer notices anything.
  • Split your queues by cost, not by count. Give registrations and renewals a slow lane of their own and let live availability checks run at full speed. Mixing them behind one pace penalises your website for your batch jobs.

Common Integration Mistakes

  • Running parallel workers that each enforce “5 per second” on their own, so four workers on one key send twenty.
  • Looping /domains/search over a 500-name list when seventeen bulk-search calls would answer the same question.
  • Reading the soft-quota delay as a network fault and lowering the timeout, which converts a slow success into a hard failure.
  • Retrying immediately after a 429, which guarantees another one.
  • Re-checking the same domain over and over inside a short window.
  • Sharing one API key across many unrelated jobs and wondering why they slow each other down — everything on one key shares its per-second cap, and everything in one area shares its points.
  • Assuming async, threads or separate containers get separate budgets. They do not; they all send the same API key.

Frequently Asked Questions

Question Answer
How many requests per second can I send to the Domain Name API? Five per API key, across all endpoints together. Domain searches (search and bulk-search) may use at most two of those five; /domains/zones has a separate budget of 20 per second; GET /domains/info and GET /products/tlds have no per-second limit. Anything above the cap returns HTTP 429 with a Retry-After header.
What is the soft quota? A points budget tracked separately per area. Lookups (/domains/search, /domains/info, /products/info) cost 1 point; every other endpoint costs 10; the TLD catalogue is free. Points decay by 20% per minute, and below 200 nothing happens.
What happens when I exceed the soft quota? Your responses are delayed, starting at roughly 0.5 seconds and rising to a 5-second ceiling if you keep the pace. No request is rejected and no error is returned.
Does the soft quota return an error code? No. It only adds latency, which is why your HTTP client timeout should be at least 10 seconds.
Is the limit counted per API key or per account? Per API key. The per-second cap covers every request sent with that key; the points quota is tracked separately for each area (domains, contacts, SSL, hosting, products, orders, balance).
Do parallel or async requests get separate budgets? No. Threads, coroutines, containers and servers that use the same API key all draw on the same budget. Use one shared limiter.
Is bulk-search cheaper than looping search? Above ten names, yes — it is a flat 10 points for up to 30 names. Below ten names, individual searches cost less.
How long does it take to register 100 domains? Minutes, not seconds. Registration costs 10 points, so 100 registrations cost 1,000 points in the domains area. About the first 20 run at full speed; after that responses are delayed (up to 5 seconds each) until the 20%-a-minute decay brings you back under 200. Nothing is rejected.
Will filling the quota get my account suspended? No. The soft quota is a brake, not a penalty. It slows you down while you are over the line and releases on its own when you drop back under.
How do I request a higher rate limit? Contact support with your use case, the endpoints involved and your expected volume per minute. Custom limits are available for high-volume resellers.

Why These Limits Exist

Not to slow you down — you are welcome to automate everything, and most of our largest resellers do. The constraint sits further upstream. Behind our API are registries, and several of them are considerably less forgiving than we are. When a burst reaches a registry faster than it wants to be reached, it does not throttle politely; it starts returning errors. Those errors land on your customer’s registration attempt, and the failure looks like yours, not the registry’s.

The soft quota exists so we can absorb that pressure instead of passing it on. Half a second of added latency is a much better outcome than a failed registration, and it is a far better outcome than the alternative most APIs reach for, which is to reject the request outright. We would rather your batch job take five minutes than have it take two and lose eleven domains along the way.

It also keeps the API open. A shared platform with no brake ends up with approval gates and manual reviews before anyone is allowed to automate anything. The brake is what lets us keep saying yes.

Requesting a Higher Limit

If your workload genuinely needs more than 5 requests per second or a larger points budget, get in touch. Tell us which endpoints you are calling, your expected volume per minute and whether the traffic is steady or bursty — that is everything we need to size a custom limit for your account. Bring the numbers and the conversation is usually a short one.