Registrar peculiarities
Every registrar in Most comes with its own set of quirks — undocumented rate limits, missing endpoints, oddly-formatted payloads, reseller program differences. This page collects the ones that actually affect how you should build against them, so you can plan around them before they bite you in production.
The page grows over time. If you hit something new, tell us and we'll add it here.
Spaceship
Short version: Spaceship's reseller program has essentially no commercial safeguards — no balance endpoint, no public reseller tier definition, no usage dashboard — and ships with rate limits that feel designed for a hobby tool rather than a bulk registration pipeline. Functional, but rough around the edges for serious operators.
No programmatic balance
Spaceship's API does not expose an account-balance endpoint. The
panel's "Balance" card falls back to the approximate balance
computed from manual bookkeeping
(sum(deposits) − sum(completed orders), see
provider deposits) whenever you
have logged at least one deposit against the provider. Without a
deposit history the card shows a plain dash.
This also means low-balance webhooks fire against the approximate
value and are labelled source: "approx" in the payload — see
balance.low for payload shape.
30 search requests per 30 seconds — search-only
Spaceship enforces a rolling cap of 30 search requests per 30 seconds per reseller account, applied server-side. The limit is scoped to availability / pricing lookups only — registrations, NS updates, contact management, and other write operations don't count against it.
Practical implications:
- A single bulk check of 30 domains against
POST /v1/domains/searchconsumes the entire 30-second window on Spaceship. A second call immediately after will fail until the window slides forward. - There is no "burst" — the server counts the rolling 30-second window, not a per-second bucket.
- The limit is per reseller account, so the only way to scale search throughput is to split load across multiple Spaceship accounts. That means creating extra reseller subaccounts on their side and adding each as a separate Provider in Most.
- You cannot negotiate this limit up through the reseller program — it's a global product limit, not a per-customer tuning.
Mitigation patterns we've seen work:
- Cache availability results aggressively if your use case allows (most users don't need fresh-every-second availability data for the same domain).
- Round-robin searches across multiple Spaceship providers in Most.
- Fall back to another registrar for bulk searches and only use Spaceship once the user has picked a candidate they want to register.
No reseller pricing overrides
Spaceship applies a single reseller tier and does not expose a way to negotiate or query per-account pricing. Prices in Most's registrar catalogue reflect the public reseller price list at the time Most was last synced — if Spaceship changes prices, the catalogue needs to be refreshed manually.
Contact auto-creation on first order
Unlike Webnic/Dynadot which expect you to create and manage contact
rows explicitly, Spaceship's first order on a new reseller account
implicitly creates a registrant contact from the local client's
contact data. Most handles this transparently in
orderProcessor.ts — the autogenerated
contact ID is stored in provider_contacts and reused for subsequent
orders, so you don't need to do anything special.
Webnic
Short version: Solid reseller program and inline NS-during- registration are nice, but two things hurt bulk workflows: availability checks are slow for a lot of TLDs, and Webnic's in-house abuse team occasionally takes aggressive, account-wide actions that are hard to appeal.
Slow availability checks on many zones
Webnic's checkAvailability endpoint is noticeably slow on a large
subset of TLDs — think hundreds of milliseconds to multiple seconds
per domain, depending on the extension. This is a server-side
limitation on Webnic's end and applies to all Webnic resellers, not
something Most can cache around.
Practical implications:
- A bulk search of 30–35 domains against Webnic via
POST /v1/domains/searchmay visibly take longer than the same call against Dynadot or Spaceship. - It's not a rate limit — the response just takes longer. No 429s, no retries needed, just wall-clock latency.
- If you need low-latency search UX, use Webnic as a secondary source and surface results progressively, or fall back to another registrar for the interactive typing step and only query Webnic once the user has settled on a candidate.
Abuse team may freeze an entire account
Webnic's abuse / security team is known to act on accounts broadly rather than per-domain. If they flag a single domain for a policy violation, they occasionally lock the entire reseller account, including domains that were never involved in the complaint. Appeals are possible but slow, and there's no programmatic warning — the first signal you get is failed API calls or suddenly non-functional domains.
Mitigation:
- Split high-risk domains across separate Webnic reseller accounts so a single hit doesn't take the whole pipeline down.
- Monitor the
domain.blockedwebhook closely on Webnic providers; abuse-driven blocks typically surface there first. - Keep a duplicate registration path on another registrar for domains where availability-on-Webnic isn't a hard requirement, so a freeze doesn't take the domain permanently offline.
Other items to document (stubs)
- Reseller tenant (
registrantUserId) model and how it differs from the other registrars. - Inline NS setup during registration vs. the 2-stage flow other providers use.
- Pending order workflow and
pendingOrderIdsemantics.
Dynadot
Short version: API works, pricing is competitive, but the support experience during incidents is the worst of the registrars we've integrated — expect zero visibility into outages.
Support gives no timelines during outages
When Dynadot has a service-side incident (API returning errors, registrar backend down, specific TLDs unreachable), their tech support's answer is consistently "please wait" with no estimated time to resolution, no incident status page, and no post-mortem. You can open a ticket, follow up repeatedly, and still get nothing beyond "we're looking into it" — and that's the exact wording they use even when the issue has been ongoing for hours.
Practical implications:
- Plan for Dynadot outages without expecting an ETA from the registrar — treat every incident as "unknown duration" from the start.
- For time-sensitive operations (renewal near expiry, critical registration), keep a parallel Dynadot-independent path ready: another registrar with the TLD available, or at minimum a playbook for transferring out.
- Hook the
task.completedandorders.completedwebhooks withstatus: "FAILED"as an early alarm — Dynadot failures surface in Most well before their support acknowledges anything.
Other items to document (stubs)
- XML-ish response wrappers (
RegisterResponse,SetNsResponse) and how error codes map. - Two-step registration → set-nameservers flow.
- Bulk vs. per-domain endpoints.
NiceNIC
Coming soon.
DomainNameAPI
Coming soon.
NicNames
Coming soon.
GName
Short version: GName.com is a Chinese reseller registrar with a signed-request HTTP API (no OAuth, no Bearer tokens — every call carries a per-request MD5 signature). Three things will bite you on day one: the "templates" model that replaces per-role contacts, an asynchronous register/renew flow that returns success the moment the order is queued (not when the registry actually finalises it), and a strict "reason for transfer-out" requirement that breaks the standard auth-info pull until 60 days after registration.
One contact-template per domain (no per-role WHOIS)
GName binds a single contact template (mbid) to all four ICANN
roles at once (registrant / admin / tech / billing). There is no
mechanism to set a different registrant from administrator — every
operation that wants to change the WHOIS goes through
/api/template/add to create a fresh template and
/api/domain/contact to rebind the domain to that template id.
In Most, this is hidden behind the standard four-slot ContactIdSet
contract — the GName adapter just mirrors the registrant id across all
four slots. The first registration for a client auto-creates a template
from the local REGISTRANT contact; subsequent registrations reuse the
saved binding from provider_contacts.
There is no template-edit endpoint — you can add, del,
list, info, default, cn_audit, but not modify in place. To
change a contact you have to create a new template and rebind any
domains pointing at the old one. syncDomainContacts knows this and
falls back to the create+rebind path automatically on GName instead of
the usual in-place push.
Signature is over form-URL-encoded values, not raw
The official docs say "sort the params, join with &, append the
secret, MD5, uppercase". What they don't say is that GName signs the
form-URL-encoded representation of each value, not the raw string
— so any field containing @, spaces, +, or other reserved
characters fails the signature check with Sorry, Signature error if
you sign over raw values. Most's SDK encodes both the wire body AND
the signing string identically (URLSearchParams-style: space→+,
@→%40, etc.).
Documented endpoint paths in the menu are aliases, not real paths
The /domain/api/... URLs the API index page lists use shortened
Chinese-pinyin segments (xgdns for "modify DNS", tpl for
"template", etc.). The actual API endpoints use the full English
names:
| Menu shows | Real endpoint |
|---|---|
/api/domain/xgdns | /api/domain/dns |
/api/tpl/add | /api/template/add |
/api/tpl/list | /api/template/list |
/api/tpl/info | /api/template/info |
/api/tpl/default | /api/template/default |
/api/tpl/del | /api/template/del |
/api/tpl/cn_audit | /api/template/cn_audit |
Hitting the menu paths returns Parameter error (sometimes
Signature error for write endpoints), which is hard to debug if
you're going by the docs.
Register / renew responses lie about completion
Both /api/domain/reg and /api/renew/add return code: 1 with a
"submit successfully, please wait for the system to process" message
as soon as the order is queued, not when the registry has actually
finalised it. The actual outcome can be:
- registration completed (
domain.info.zt = 0, status "OK") - registration failed (
zt = 9,ztstr = "Registration failed") — most commonly because the supplied nameservers can't be resolved by the registry (e.g. placeholderns1.example.comhosts); - still in flight (
zt = 8) - held for non-payment (
zt = 10)
Most's adapter polls domain.info after each reg / renew call
until the status stabilises, with timeouts of 5 / 3 minutes
respectively, and surfaces the real registry-level error to the panel
when it lands. Do not rely on code: 1 from these endpoints to
assume success.
The same pattern applies to /api/domain/contact (template rebind)
and /api/domain/dns (NS change) — both are async, the change
propagates in 30–120 seconds. The adapter polls before declaring done.
Auth-info / EPP-code is two calls AND requires a sm reason
The transfer-out flow takes two requests:
POST /api/tranout/add— initiates the transfer-out record. Requires a non-emptysm("说明", explanation) field — without it you getSorry, The reason for transfer out cannot be empty.Most's SDK defaultssm: "Customer requested transfer-out"if the caller doesn't pass one.POST /api/tranout/code— fetches the actual EPP code synchronously off the record returned by step 1. No email round-trip (unlike Webnic).
Additionally, the 60-day ICANN transfer-lock applies — a freshly
registered domain rejects tranout/add with a message naming the
exact unlock date.
No programmatic transfer-lock toggle
GName surfaces the registry lock state in domain.info.zt = 53, but
does not document an endpoint to toggle it. The lock can only be
flipped from the GName control panel. Most's adapter throws
notSupported from setTransferLock so callers know to surface the
"go to the panel" instruction rather than silently no-oping.
Rate limits are masked, asymmetric per endpoint, and harsh
GName's official docs do not publish a rate limit per subscription
tier — Spaceship-style numbers are simply absent. Empirical stress
testing on an agent-tier (middle) account from a single source IP
gave us a two-layered picture; the limit you can spend is wildly
different per endpoint.
Account-wide ceiling (e.g. /api/user/balance) — generous:
| Pattern | Outcome |
|---|---|
| Sustained ≤ 200 rps over 5 s | 0 errors |
| Burst ≤ 500 parallel | 0 errors |
| Sustained 400 rps over 5 s | 100 % blocked |
| Burst 1 000 parallel | 100 % blocked |
Per-endpoint ceiling on /api/domain/check — ~100× stricter:
| In-flight requests | Outcome |
|---|---|
| 1 (sequential) | 24/25 OK over ~26 s |
| 2 concurrent | ~40 % rejected with "too frequent" |
| 3 concurrent | ~90 % rejected |
| 10 concurrent | ~96 % rejected |
GName surfaces the throttle through two different masked messages depending on which ceiling you hit:
-
Sorry, APPID is invalid(note the double space in the upstream template) — the account-wide ban, triggered by 400 + rps sustained or 1000 + parallel burst. Stays active for ≥ 3 minutes of complete silence on that appid before unblocking. There is noRetry-Afterheader. This is not an auth failure — your credentials are still valid. -
requests are too frequent. Please try again later— the short-burst per-endpoint throttle. Clears within a few seconds, but triggers fast on hot endpoints like/api/domain/check.
Most's SDK detects both messages and retries with exponential backoff — 2 s → 5 s → 10 s for "too frequent", 5 s → 15 s → 45 s for "APPID invalid", capped at 3 attempts each. Real auth failures (matching pattern coincidentally) surface after the retries are exhausted.
To stay safely under the thresholds in normal operation:
Provider.rate_limitfor GName defaults to 2 rps at the SDK layer — that's the realistic ceiling for/api/domain/check, and it doesn't hurt the lenient endpoints because the SDK's token bucket only spaces, it doesn't cap calls that come in slower.- The availability-check fan-out uses
parallelLimit: 1— attempting any concurrency on/api/domain/checkjust generates retry storms that net out to the same wall-time as sequential.
Practical impact on the panel: a search across N GName-supported TLDs takes roughly N seconds wall-clock (compared to ~1 s for the same query on Dynadot's 100-name batch endpoint). The frontend paginates GName-heavy queries at 25 names per batch and renders rows progressively, but the underlying GName API simply doesn't support bulk zone scans. If you operate from multiple IPs against the same appid, the limit appears to apply per-account, not per-IP — so sharding by IP doesn't help; you'd need separate appids.
Balance fields are pinyin abbreviations
/api/user/balance returns six fields with two-letter pinyin codes
instead of human-readable names:
| Field | Meaning |
|---|---|
zqian | total balance |
kqian | available to spend |
dqian | frozen (queued orders) |
czqian | total topped-up |
xfqian | total spent |
kjsqian | settle-able |
Most's checkAccount surfaces kqian as the headline figure (and
adds / zqian if the two differ, to flag that funds are frozen).
Authentication setup
In the GName control panel: User Center → Reseller → API Settings.
- Enable MFA on the account first — GName won't issue API keys until the account has TOTP 2FA bound.
- Generate
appid(public client id) andappkey(secret). - Whitelist your server's outbound IP — calls from any other IP get
Signature errorregardless of how correct the signature is.
The appid goes into Most's "App ID" field and the appkey into
"App Key" — Most never sends appkey over the wire, it only uses it
to compute the per-request gntoken.