Skip to content
Start here

Registrar Sandbox

Use the Registrar Sandbox API to test domain search, availability checks, registration, and domain management flows without buying real domains.

This API is a test environment for the production Registrar API.

Prerequisites

Before using this API, make sure you have:

  1. Cloudflare account — the caller must have a valid Cloudflare account.
  2. API authentication — create an API token with Registrar Sandbox permissions.

How the Sandbox API differs from the production Registrar API

Because the Sandbox API is intended for testing, it behaves differently from the production Registrar API in a few important ways:

  1. No billing — you will not be charged real money for purchasing a domain.
  2. No real domains — purchased domains are test records and will not be reachable on the Internet.
  3. No DNS zones — purchasing a domain does not create a zone resource.
  4. No Registration Express Mode — you must provide full contact data.

Sandbox purchases are still persisted. If you purchase a domain in the sandbox, that domain will not be available for others to purchase in the sandbox.

Terminology: domain extension

Throughout this API, “extension” refers to the domain extension part of a fully qualified domain name — the portion after the registrable label. For example, in example.co.uk, the extension is co.uk (not just uk). This covers both top-level domains like com and multi-level extensions like co.uk. This is distinct from other uses of the word “extension” (e.g., EPP extensions).

Supported extensions

The Sandbox API currently supports programmatic registration for these extensions:

com, net

The production Registrar API supports 40+ extensions.

Cloudflare Registrar supports 400+ extensions in the dashboard. Extensions not listed above can be registered at https://dash.cloudflare.com/{account_id}/domains/registrations.

Typical workflow

  1. Search — call GET /domain-search?q={keyword} to discover available domains.
  2. Check — call POST /domain-check with candidate domains to verify real-time availability and pricing.
  3. Review the response — if registrable: false, inspect reason to understand whether the domain is unavailable, the extension is not supported by this API, the extension is not supported by Cloudflare Registrar at all, or the extension’s registry has frozen new registrations.
  4. Handle premium domains — if tier: premium, premium registration is not currently supported by this API. The Sandbox API currently supports only com and net, which do not have premium registrations, but clients should still handle this response for consistency with the production Registrar API. Surface the premium pricing to the user, but do not proceed to POST /registrations for that domain.
  5. Observe the registration schema — call GET /extensions/:extension_name to discover the required values for registering this extension.
  6. Register — call POST /registrations with the chosen domain name for supported non-premium registrations.
  7. Confirm completion — if the response is 201 Created, registration completed within the default timeout and no polling is needed.
  8. Poll when needed — if the response is 202 Accepted, poll links.self from the workflow response.
  9. Stop for user action — if state: action_required, stop polling and surface context.action to the user. The workflow will not resolve on its own.
  10. Continue when blocked — if state: blocked, continue polling and inform the user that a third party, such as the extension registry or losing registrar, is delaying progress.
  11. Review failures before retrying — if state: failed, review error.code and error.message, then decide whether user action or a new Check call is needed.

Default behavior for mutating operations

By default, mutating operations such as create and update hold the connection for a bounded, server-defined amount of time while the operation completes. In most cases, the response contains a completed workflow status and no polling is required.

  • Completed within the synchronous wait window: Returns 201 (create) or 200 (update) with a workflow_status where state: succeeded and completed: true.
  • Still processing after the synchronous wait window: Returns 202 Accepted with a workflow_status where completed: false. Use the links.self URL to poll for completion.

Non-blocking mode

To receive an immediate 202 Accepted response without waiting, send the Prefer: respond-async request header (RFC 7240). The server will acknowledge it with a Preference-Applied: respond-async response header.

Polling

When the response is 202, poll the workflow status endpoint indicated by links.self in the response body until the workflow reaches a terminal state or requires user action.

Search for available domains
client.registrarSandbox.search(RegistrarSandboxSearchParams { account_id, q, extensions, limit } params, RequestOptionsoptions?): RegistrarSandboxSearchResponse { domains }
GET/accounts/{account_id}/registrar-sandbox/domain-search
Check domain availability
client.registrarSandbox.check(RegistrarSandboxCheckParams { account_id, domains } params, RequestOptionsoptions?): RegistrarSandboxCheckResponse { domains }
POST/accounts/{account_id}/registrar-sandbox/domain-check
ModelsExpand Collapse
Registration { auto_renew, created_at, domain_name, 4 more }

A domain registration resource representing the current state of a registered domain.

auto_renew: boolean

Whether automatic renewal occurs before expiration.

created_at: string

When the domain was registered. Present when the registration resource exists.

formatdate-time
domain_name: string

Provides a fully qualified domain name (FQDN), including the extension (e.g., example.com, mybrand.app). The domain name uniquely identifies a registration. Cloudflare permits only one registration per domain, making the domain name a natural idempotency key for registration requests.

expires_at: string | null

When the domain registration expires. Ready registrations include this value; only registration_pending may return null.

formatdate-time
locked: boolean

Whether the domain is locked for transfer.

privacy_mode: "off" | "redaction"

Current WHOIS privacy mode for the registration.

One of the following:
"off"
"redaction"
status: "active" | "registration_pending" | "expired" | 3 more

Current registration status.

  • active: The domain operates with an active registration.
  • registration_pending: Registration remains in progress.
  • expired: The domain registration expired.
  • suspended: The registry suspended the domain.
  • redemption_period: The domain entered the redemption grace period.
  • pending_delete: The registry scheduled the domain for deletion.
One of the following:
"active"
"registration_pending"
"expired"
"suspended"
"redemption_period"
"pending_delete"
WorkflowStatus { completed, created_at, links, 4 more }

Status of an async registration workflow.

completed: boolean

Indicates whether the workflow reached a terminal state. A succeeded or failed state returns true; pending, in_progress, action_required, and blocked return false.

created_at: string
formatdate-time
state: "pending" | "in_progress" | "action_required" | 3 more

Describes the workflow lifecycle state.

  • pending: The workflow awaits processing.
  • in_progress: Processing started. Continue polling links.self. An internal deadline limits the duration of this state.
  • action_required: The workflow pauses for user action. See context.action for details. Stop automated polling until the user completes the required action.
  • blocked: A third party, such as the domain extension’s registry or a losing registrar, prevents progress. Continue polling because the block may resolve when the third party responds.
  • succeeded: Terminal state. The operation completed successfully. completed equals true. For registrations, context.registration contains the resulting registration resource.
  • failed: Terminal state. The operation failed. completed equals true. See error.code and error.message for the reason. Require user review before retrying.
One of the following:
"pending"
"in_progress"
"action_required"
"blocked"
"succeeded"
"failed"
updated_at: string
formatdate-time
context?: Record<string, unknown>

Provides workflow-specific data.

For domain-centric workflows, context.domain_name identifies the workflow subject.

error?: Error | null

Provides error details when a workflow reaches the failed state. The workflow type (registration, update, etc.) and underlying registry response determine the specific codes and messages. Workflow error codes differ from immediate HTTP error errors[].code values in non-2xx responses. Surface error.message to the user for context.

code: string

Machine-readable error code identifying the failure reason.

message: string

Human-readable explanation of the failure. May include registry-specific details.

RegistrarSandboxSearchResponse { domains }

Contains the search results.

domains: Array<Domain>

Lists domain suggestions in relevance order. An empty array indicates that the search criteria matched zero domains.

name: string

The fully qualified domain name (FQDN) in punycode format for internationalized domain names (IDNs).

registrable: boolean

Indicates domain availability according to potentially stale, non-authoritative search data.

  • true: The domain appears available. Use POST /domain-check to confirm before registration.
  • false: Search results mark the domain ineligible for registration through this API. See reason for details.
pricing?: Pricing { currency, registration_cost, renewal_cost }

Provides annual pricing information for a registrable domain. This object appears only when registrable is true. The API returns all per-year prices as strings to preserve decimal precision.

registration_cost and renewal_cost frequently have the same value, but may differ, especially when registries set different premium rates for initial registration and renewal. For a multi-year registration (e.g., 4 years), registration_cost applies to the first year and renewal_cost applies to each subsequent year. The values reflect the current registry rate, which may change over time. Search and Check may surface premium pricing, but this API currently supports standard registrations only.

currency: string

ISO-4217 currency code for the prices (e.g., “USD”, “EUR”, “GBP”).

registration_cost: string

The first-year cost to register this domain. For premium domains (tier: premium), the registry sets this price, which may significantly exceed standard pricing. For multi-year registrations, this cost applies to the first year only; renewal_cost applies to subsequent years.

renewal_cost: string

Per-year renewal cost for this domain. Applied to each year beyond the first year of a multi-year registration, and to each annual auto-renewal thereafter. May differ from registration_cost, especially for premium domains where initial registration often costs more than renewals.

reason?: "extension_not_supported_via_api" | "extension_not_supported" | "extension_disallows_registration" | 2 more

Appears only when registrable is false and explains the advisory search result. Use POST /domain-check for authoritative status.

  • extension_not_supported_via_api: Cloudflare Registrar supports this extension in the dashboard but currently excludes it from programmatic registration through this API.
  • extension_not_supported: Cloudflare Registrar excludes this extension entirely.
  • extension_disallows_registration: The extension’s registry temporarily or permanently freezes new registrations.
  • domain_premium: The domain carries premium pricing. This API currently supports standard registrations only.
  • domain_unavailable: The domain appears unavailable.
One of the following:
"extension_not_supported_via_api"
"extension_not_supported"
"extension_disallows_registration"
"domain_premium"
"domain_unavailable"
tier?: "standard" | "premium"

The pricing tier for this domain. A registrable value of true always includes this field, which defaults to standard for most domains. A registrable value of false may omit it.

  • standard: Standard registry pricing.
  • premium: Premium domain with higher pricing from the registry.
One of the following:
"standard"
"premium"
RegistrarSandboxCheckResponse { domains }

Contains the availability check results.

domains: Array<Domain>

Array of domain availability results. Results for unsupported extensions contain registrable: false and a reason field. The response may omit malformed domain names.

name: string

The fully qualified domain name (FQDN) in punycode format for internationalized domain names (IDNs).

registrable: boolean

Indicates programmatic registration eligibility according to a real-time registry check.

  • true: The domain is available for registration. The response includes the pricing object.
  • false: A restriction prevents registration. See the reason field for details. Some results, such as premium domains, may still include tier.
pricing?: Pricing { currency, registration_cost, renewal_cost }

Provides annual pricing information for a registrable domain. This object appears only when registrable is true. The API returns all per-year prices as strings to preserve decimal precision.

registration_cost and renewal_cost frequently have the same value, but may differ, especially when registries set different premium rates for initial registration and renewal. For a multi-year registration (e.g., 4 years), registration_cost applies to the first year and renewal_cost applies to each subsequent year. The values reflect the current registry rate, which may change over time. Search and Check may surface premium pricing, but this API currently supports standard registrations only.

currency: string

ISO-4217 currency code for the prices (e.g., “USD”, “EUR”, “GBP”).

registration_cost: string

The first-year cost to register this domain. For premium domains (tier: premium), the registry sets this price, which may significantly exceed standard pricing. For multi-year registrations, this cost applies to the first year only; renewal_cost applies to subsequent years.

renewal_cost: string

Per-year renewal cost for this domain. Applied to each year beyond the first year of a multi-year registration, and to each annual auto-renewal thereafter. May differ from registration_cost, especially for premium domains where initial registration often costs more than renewals.

reason?: "extension_not_supported_via_api" | "extension_not_supported" | "extension_disallows_registration" | 2 more

Appears only when registrable is false and explains the result.

  • extension_not_supported_via_api: Cloudflare Registrar supports this extension in the dashboard but currently excludes it from programmatic registration through this API. The user can register via https://dash.cloudflare.com/{account_id}/domains/registrations.
  • extension_not_supported: Cloudflare Registrar excludes this extension entirely.
  • extension_disallows_registration: The extension’s registry temporarily or permanently freezes new registrations. Registrars currently cannot register domains on this extension.
  • domain_premium: The domain carries premium pricing. This API currently supports standard registrations only.
  • domain_unavailable: An existing registration, reservation, or other registry restriction makes the domain unavailable on a supported extension.
One of the following:
"extension_not_supported_via_api"
"extension_not_supported"
"extension_disallows_registration"
"domain_premium"
"domain_unavailable"
tier?: "standard" | "premium"

The pricing tier for this domain. A registrable value of true always includes this field, which defaults to standard for most domains. A registrable value of false may omit it.

  • standard: Standard registry pricing.
  • premium: Premium domain with higher pricing from the registry.
One of the following:
"standard"
"premium"

Registrar SandboxRegistrations

Create Registration
client.registrarSandbox.registrations.create(RegistrationCreateParams { account_id, domain_name, acknowledgements, 6 more } params, RequestOptionsoptions?): RegistrationCreateResponse { completed, created_at, links, 4 more }
POST/accounts/{account_id}/registrar-sandbox/registrations
List Registrations
client.registrarSandbox.registrations.list(RegistrationListParams { account_id, cursor, direction, 2 more } params, RequestOptionsoptions?): CursorPagination<RegistrationListResponse { auto_renew, created_at, domain_name, 4 more } >
GET/accounts/{account_id}/registrar-sandbox/registrations
Get Registration
client.registrarSandbox.registrations.get(stringdomainName, RegistrationGetParams { account_id } params, RequestOptionsoptions?): RegistrationGetResponse { auto_renew, created_at, domain_name, 4 more }
GET/accounts/{account_id}/registrar-sandbox/registrations/{domain_name}
Update Registration
client.registrarSandbox.registrations.edit(stringdomainName, RegistrationEditParams { account_id, auto_renew, Prefer } params, RequestOptionsoptions?): RegistrationEditResponse { completed, created_at, links, 4 more }
PATCH/accounts/{account_id}/registrar-sandbox/registrations/{domain_name}
ModelsExpand Collapse
RegistrationCreateResponse { completed, created_at, links, 4 more }

Status of an async registration workflow.

completed: boolean

Indicates whether the workflow reached a terminal state. A succeeded or failed state returns true; pending, in_progress, action_required, and blocked return false.

created_at: string
formatdate-time
state: "pending" | "in_progress" | "action_required" | 3 more

Describes the workflow lifecycle state.

  • pending: The workflow awaits processing.
  • in_progress: Processing started. Continue polling links.self. An internal deadline limits the duration of this state.
  • action_required: The workflow pauses for user action. See context.action for details. Stop automated polling until the user completes the required action.
  • blocked: A third party, such as the domain extension’s registry or a losing registrar, prevents progress. Continue polling because the block may resolve when the third party responds.
  • succeeded: Terminal state. The operation completed successfully. completed equals true. For registrations, context.registration contains the resulting registration resource.
  • failed: Terminal state. The operation failed. completed equals true. See error.code and error.message for the reason. Require user review before retrying.
One of the following:
"pending"
"in_progress"
"action_required"
"blocked"
"succeeded"
"failed"
updated_at: string
formatdate-time
context?: Record<string, unknown>

Provides workflow-specific data.

For domain-centric workflows, context.domain_name identifies the workflow subject.

error?: Error | null

Provides error details when a workflow reaches the failed state. The workflow type (registration, update, etc.) and underlying registry response determine the specific codes and messages. Workflow error codes differ from immediate HTTP error errors[].code values in non-2xx responses. Surface error.message to the user for context.

code: string

Machine-readable error code identifying the failure reason.

message: string

Human-readable explanation of the failure. May include registry-specific details.

RegistrationListResponse { auto_renew, created_at, domain_name, 4 more }

A domain registration resource representing the current state of a registered domain.

auto_renew: boolean

Whether automatic renewal occurs before expiration.

created_at: string

When the domain was registered. Present when the registration resource exists.

formatdate-time
domain_name: string

Provides a fully qualified domain name (FQDN), including the extension (e.g., example.com, mybrand.app). The domain name uniquely identifies a registration. Cloudflare permits only one registration per domain, making the domain name a natural idempotency key for registration requests.

expires_at: string | null

When the domain registration expires. Ready registrations include this value; only registration_pending may return null.

formatdate-time
locked: boolean

Whether the domain is locked for transfer.

privacy_mode: "off" | "redaction"

Current WHOIS privacy mode for the registration.

One of the following:
"off"
"redaction"
status: "active" | "registration_pending" | "expired" | 3 more

Current registration status.

  • active: The domain operates with an active registration.
  • registration_pending: Registration remains in progress.
  • expired: The domain registration expired.
  • suspended: The registry suspended the domain.
  • redemption_period: The domain entered the redemption grace period.
  • pending_delete: The registry scheduled the domain for deletion.
One of the following:
"active"
"registration_pending"
"expired"
"suspended"
"redemption_period"
"pending_delete"
RegistrationGetResponse { auto_renew, created_at, domain_name, 4 more }

A domain registration resource representing the current state of a registered domain.

auto_renew: boolean

Whether automatic renewal occurs before expiration.

created_at: string

When the domain was registered. Present when the registration resource exists.

formatdate-time
domain_name: string

Provides a fully qualified domain name (FQDN), including the extension (e.g., example.com, mybrand.app). The domain name uniquely identifies a registration. Cloudflare permits only one registration per domain, making the domain name a natural idempotency key for registration requests.

expires_at: string | null

When the domain registration expires. Ready registrations include this value; only registration_pending may return null.

formatdate-time
locked: boolean

Whether the domain is locked for transfer.

privacy_mode: "off" | "redaction"

Current WHOIS privacy mode for the registration.

One of the following:
"off"
"redaction"
status: "active" | "registration_pending" | "expired" | 3 more

Current registration status.

  • active: The domain operates with an active registration.
  • registration_pending: Registration remains in progress.
  • expired: The domain registration expired.
  • suspended: The registry suspended the domain.
  • redemption_period: The domain entered the redemption grace period.
  • pending_delete: The registry scheduled the domain for deletion.
One of the following:
"active"
"registration_pending"
"expired"
"suspended"
"redemption_period"
"pending_delete"
RegistrationEditResponse { completed, created_at, links, 4 more }

Status of an async registration workflow.

completed: boolean

Indicates whether the workflow reached a terminal state. A succeeded or failed state returns true; pending, in_progress, action_required, and blocked return false.

created_at: string
formatdate-time
state: "pending" | "in_progress" | "action_required" | 3 more

Describes the workflow lifecycle state.

  • pending: The workflow awaits processing.
  • in_progress: Processing started. Continue polling links.self. An internal deadline limits the duration of this state.
  • action_required: The workflow pauses for user action. See context.action for details. Stop automated polling until the user completes the required action.
  • blocked: A third party, such as the domain extension’s registry or a losing registrar, prevents progress. Continue polling because the block may resolve when the third party responds.
  • succeeded: Terminal state. The operation completed successfully. completed equals true. For registrations, context.registration contains the resulting registration resource.
  • failed: Terminal state. The operation failed. completed equals true. See error.code and error.message for the reason. Require user review before retrying.
One of the following:
"pending"
"in_progress"
"action_required"
"blocked"
"succeeded"
"failed"
updated_at: string
formatdate-time
context?: Record<string, unknown>

Provides workflow-specific data.

For domain-centric workflows, context.domain_name identifies the workflow subject.

error?: Error | null

Provides error details when a workflow reaches the failed state. The workflow type (registration, update, etc.) and underlying registry response determine the specific codes and messages. Workflow error codes differ from immediate HTTP error errors[].code values in non-2xx responses. Surface error.message to the user for context.

code: string

Machine-readable error code identifying the failure reason.

message: string

Human-readable explanation of the failure. May include registry-specific details.

Registrar SandboxRegistration Status

Get Registration Status
client.registrarSandbox.registrationStatus.get(stringdomainName, RegistrationStatusGetParams { account_id } params, RequestOptionsoptions?): RegistrationStatusGetResponse { completed, created_at, links, 4 more }
GET/accounts/{account_id}/registrar-sandbox/registrations/{domain_name}/registration-status
ModelsExpand Collapse
RegistrationStatusGetResponse { completed, created_at, links, 4 more }

Status of an async registration workflow.

completed: boolean

Indicates whether the workflow reached a terminal state. A succeeded or failed state returns true; pending, in_progress, action_required, and blocked return false.

created_at: string
formatdate-time
state: "pending" | "in_progress" | "action_required" | 3 more

Describes the workflow lifecycle state.

  • pending: The workflow awaits processing.
  • in_progress: Processing started. Continue polling links.self. An internal deadline limits the duration of this state.
  • action_required: The workflow pauses for user action. See context.action for details. Stop automated polling until the user completes the required action.
  • blocked: A third party, such as the domain extension’s registry or a losing registrar, prevents progress. Continue polling because the block may resolve when the third party responds.
  • succeeded: Terminal state. The operation completed successfully. completed equals true. For registrations, context.registration contains the resulting registration resource.
  • failed: Terminal state. The operation failed. completed equals true. See error.code and error.message for the reason. Require user review before retrying.
One of the following:
"pending"
"in_progress"
"action_required"
"blocked"
"succeeded"
"failed"
updated_at: string
formatdate-time
context?: Record<string, unknown>

Provides workflow-specific data.

For domain-centric workflows, context.domain_name identifies the workflow subject.

error?: Error | null

Provides error details when a workflow reaches the failed state. The workflow type (registration, update, etc.) and underlying registry response determine the specific codes and messages. Workflow error codes differ from immediate HTTP error errors[].code values in non-2xx responses. Surface error.message to the user for context.

code: string

Machine-readable error code identifying the failure reason.

message: string

Human-readable explanation of the failure. May include registry-specific details.

Registrar SandboxUpdate Status

Get Update Status
client.registrarSandbox.updateStatus.get(stringdomainName, UpdateStatusGetParams { account_id } params, RequestOptionsoptions?): UpdateStatusGetResponse { completed, created_at, links, 4 more }
GET/accounts/{account_id}/registrar-sandbox/registrations/{domain_name}/update-status
ModelsExpand Collapse
UpdateStatusGetResponse { completed, created_at, links, 4 more }

Status of an async registration workflow.

completed: boolean

Indicates whether the workflow reached a terminal state. A succeeded or failed state returns true; pending, in_progress, action_required, and blocked return false.

created_at: string
formatdate-time
state: "pending" | "in_progress" | "action_required" | 3 more

Describes the workflow lifecycle state.

  • pending: The workflow awaits processing.
  • in_progress: Processing started. Continue polling links.self. An internal deadline limits the duration of this state.
  • action_required: The workflow pauses for user action. See context.action for details. Stop automated polling until the user completes the required action.
  • blocked: A third party, such as the domain extension’s registry or a losing registrar, prevents progress. Continue polling because the block may resolve when the third party responds.
  • succeeded: Terminal state. The operation completed successfully. completed equals true. For registrations, context.registration contains the resulting registration resource.
  • failed: Terminal state. The operation failed. completed equals true. See error.code and error.message for the reason. Require user review before retrying.
One of the following:
"pending"
"in_progress"
"action_required"
"blocked"
"succeeded"
"failed"
updated_at: string
formatdate-time
context?: Record<string, unknown>

Provides workflow-specific data.

For domain-centric workflows, context.domain_name identifies the workflow subject.

error?: Error | null

Provides error details when a workflow reaches the failed state. The workflow type (registration, update, etc.) and underlying registry response determine the specific codes and messages. Workflow error codes differ from immediate HTTP error errors[].code values in non-2xx responses. Surface error.message to the user for context.

code: string

Machine-readable error code identifying the failure reason.

message: string

Human-readable explanation of the failure. May include registry-specific details.

Registrar SandboxExtensions

List extensions
client.registrarSandbox.extensions.list(ExtensionListParams { account_id, cursor, direction, 3 more } params, RequestOptionsoptions?): CursorPagination<ExtensionListResponse { metadata, registration_schema } >
GET/accounts/{account_id}/registrar-sandbox/extensions
Get extension
client.registrarSandbox.extensions.get(stringextension, ExtensionGetParams { account_id } params, RequestOptionsoptions?): ExtensionGetResponse { metadata, registration_schema }
GET/accounts/{account_id}/registrar-sandbox/extensions/{extension}
ModelsExpand Collapse
ExtensionListResponse { metadata, registration_schema }

Extension entry with metadata and JSON Schema documents for the registration operation.

metadata: Metadata { name, tld }

Extension metadata.

name: string

The full name of the extension. For example, “co.uk”, or “uk”.

tld: string

The TLD of the extension. For example, for “co.uk”, it is “uk”. For “uk”, it is “uk”.

registration_schema: unknown

JSON Schema describing the expected input structure for registration operations on this extension.

ExtensionGetResponse { metadata, registration_schema }

Extension entry with metadata and JSON Schema documents for the registration operation.

metadata: Metadata { name, tld }

Extension metadata.

name: string

The full name of the extension. For example, “co.uk”, or “uk”.

tld: string

The TLD of the extension. For example, for “co.uk”, it is “uk”. For “uk”, it is “uk”.

registration_schema: unknown

JSON Schema describing the expected input structure for registration operations on this extension.