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: 2 and 200. Two requests per second, two hundred quota points per minute. 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 reseller account. 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 2 requests per second; go faster and you get HTTP 429. Separately, every call costs points: /domains/search and /domains/info cost 1 point, every other endpoint costs 10. You get 200 points per 60 seconds. Fill 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. Both limits are counted per reseller, not per API key.

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 The reseller, identified by the __reseller header. Extra API keys add no allowance.
Rate limit 2 requests per second
Above the rate limit HTTP 429 Too Many Requests — the request is rejected
Soft quota 200 points per 60 seconds
Cost of /domains/search and /domains/info 1 point
Cost of every other endpoint 10 points
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 2 per second — the rate limit is what stops you, never the quota
Safe pace for 10-point calls 1 request every 3 seconds (20 per minute)

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 — 2 requests / second

This one counts requests, not value. Two per second is the ceiling for your whole account, summed across every thread, worker, container and cron job you run.

  • Breaking it returns HTTP 429 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 / 60 seconds

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 reseller account. Creating a second API key does not create a second allowance — every request carries your __reseller identifier, so all of them land in the same bucket no matter which key signed them.

What Each Request Costs

Two endpoints 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/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/*, /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. 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 200 names costs 200 points one at a time, or 40 points in four 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) 200 The rate limit, always 2 / second = 120 / minute
Anything else (10 points each) 20 The soft quota 1 every 3 seconds = 20 / minute
Mixed: one search plus one registration per domain (11 points) 18 domains The soft quota 1 domain every ~3.3 seconds

Worked example: checking 500 domains

One at a time through /domains/search, that is 500 calls and 500 points. The quota would let you spend that in two and a half minutes — but at 2 requests per second the wall clock is 250 seconds anyway, so the rate limit is what governs and the brake never engages. Through bulk-search in batches of 50, the same job is 10 calls, 100 points and about 5 seconds. Same answer, fifty times faster, and it spends 100 of your points instead of 500.

Worked example: registering 100 domains

A hundred registrations cost 1,000 points, and the quota releases 200 points a minute. That job takes at least five minutes, and no amount of parallelism changes it. Fire them off at full speed and you will fill the quota in the first ten seconds, then crawl through the remaining ninety behind a growing delay. Pace them at one every three seconds and you finish in the same five minutes with every response arriving promptly. The paced version is not slower — it is just honest about how long the work takes.

If your integration only does availability checks, the soft quota cannot reach you. Two requests per second at one point each is 120 points a minute, and the ceiling is 200. There is no pace at which single lookups fill the quota. So if you are seeing slow responses on a search-only integration, the cause is somewhere else.

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.

Your situation What the API does
Under 200 points in the last 60 seconds 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: more than two requests left your side within the same second. It has nothing to do with the quota and nothing to do with how many domains you manage. 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.
  • Honour Retry-After if the response carries it, and wait at least one second if it does not.
  • Retry once. A single 429 after a burst is normal and one retry usually clears it.
  • If it repeats, back off exponentially — 1 s, 2 s, 4 s, 8 s — with a sensible ceiling.
  • 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 “2 per second” are really sending eight.

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; if your workload is made of 10-point calls, raise the interval to 3 seconds and you will never meet the brake.

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 for 1-point calls. Use 3000 ms for 10-point calls.
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. Use 3000000 for 10-point calls.
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 for 1-point calls. Use 3.0 for 10-point calls.
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; use 3000 for 10-point calls
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 2 requests this second? The answer is HTTP 429 and the request stops here.

3. Quota check

Points are added to your 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, 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. It is the earliest signal available to you, and it climbs 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 “2 per second” on their own, so eight workers send sixteen.
  • Looping /domains/search over a 500-name list when four 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.
  • Creating extra API keys expecting extra allowance — the counter follows the reseller, not the key.
  • Assuming async, threads or separate containers get separate budgets. They do not; they all carry the same __reseller header.

Frequently Asked Questions

Question Answer
How many requests per second can I send to the Domain Name API? Two, counted across your entire reseller account. Anything above that returns HTTP 429.
What is the soft quota? A points budget of 200 per 60 seconds. /domains/search and /domains/info cost 1 point each; every other endpoint costs 10.
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.
Can I use several API keys to get a bigger allowance? No. Both limits are counted per reseller account. Every request carries your __reseller identifier, so extra keys share one budget.
Do parallel or async requests get separate budgets? No. Threads, coroutines, containers and servers all draw on the same account budget. Use one shared limiter.
Is bulk-search cheaper than looping search? Above ten names, yes, and dramatically so — it is a flat 10 points for the whole array. Below ten names, individual searches cost less.
How long does it take to register 100 domains? At least five minutes. Registration costs 10 points, 100 registrations cost 1,000 points, and the quota releases 200 points a minute.
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 2 requests per second or more than 200 points per minute, 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.