Easily Bulk Transfer Domains From Cloudflare via the Cloudflare API

Cloudflare Registrar is a cost price domain registrar offering cheap domain transfers and registrations. However, at one point you may need to transfer many domains out of Cloudflare Registrar to another registrar – offering discount domain transfer pricing or specials. This guide will show you how to bulk transfer out the domains via Cloudflare Registrar API via command line or scripting.

Cloudflare Registrar offers a GUI based dashboard method for unlocking your domain and obtaining your EPP or AUTH code. If you have a few domains that should suffice. However, if you have hundreds of domains within Cloudflare Registrar for your Cloudflare Account, manually unlocking and obtaining your EPP/AUTH code for each domain will be time consuming. Cloudflare Registrar API allows you to programmatically script the process for unlocking your domains and obtaining your EPP/AUTH codes. Note, if you are looking to bulk transfer domains into Cloudflare Registrar, check out the guide here.

However, there’s a few undocumented commands not listed in the current version of the documentation (as at February 28, 2021) for obtaining the EPP/AUTH code which I will outline below as well.

  • Step 1. Obtain your Cloudflare Account Global API Key & Account ID
  • Step 2. Install jq JSON Tool
  • Step 3. Populating Variables On SSH Command Line/Shell Scripts
  • Step 4. Bulk Disable DNSSEC For Domains
  • Step 5. Bulk Unlock & Obtain EPP/AUTH Code For Cloudflare Domains
  • Step 6. Bulk Fast-Track Transfer Approvals

Step 1. Obtain your Cloudflare Account Global API Key & Account ID

Cloudflare Registrar API currently does not support Cloudflare API Tokens. So you will need to obtain your Cloudflare Account Global API Key from https://dash.cloudflare.com/profile/api-tokens. Then you’ll also need to obtain your Cloudflare Account ID via any of your domain’s dashboard right side column which lists your zone id and your account id. These two items will be used to populate variables on the SSH command line or via bash shell scripts you can write to run on Linux command line.

 

Step 2. Install jq JSON Tool

Install jq JSON tool which allows you to manipulate and filter Cloudflare Registrar API’s JSON output

yum -y install jq

or

apt-get install jq

Step 3. Populating Variables On SSH Command Line/Shell Scripts

You’ll need to populate the following variables with your Cloudflare Account ID, Cloudflare Account Global API Key, Cloudflare Account Email address and Cloudflare endpoint variables. Then on the command line in SSH session type or copy and paste them or set within your own shell scripts.

cfaccountid=your_cf_account_id
cftoken=your_cf_global_api_token
cfemail=your_cf_account_email_address
endpoint=https://api.cloudflare.com/client/v4/
endpoint_target=accounts/$cfaccountid/registrar/domains

If you use curl command line to query Cloudflare Registrar API with this command below, you will return just all the domain names that are registered with Cloudflare Registrar. The below command will save all the domains in text file cfregistrar-domains.txt.

curl -4sX GET "${endpoint}${endpoint_target}" -H "Content-Type:application/json" \
-H "X-Auth-Email: $cfemail" \
-H "X-Auth-Key: $cftoken" | jq -r '.result[].name' | tee cfregistrar-domains.txt

domain.com
domain.net

Then edit text file cfregistrar-domains.txt and remove any domain names that you do not want to transfer out. Leaving just a list of domains you want to transfer out.

Step 4. Bulk Disable DNSSEC For Domains

To ensure domain transfers go smoothly, we’ll bulk disable DNSSEC for the domains via Cloudflare DNSSEC API end point for https://api.cloudflare.com/client/v4/zones/ZONEID/dnssec. To do this we need to associated ZONEID for each domain name contained in previously populated cfregistrar-domains.txt text file.

The below for loop run querying the Cloudflare API will save each domain’s ZONEID and domain name to their own file zoneid-domainname.log

for domain in $(cat cfregistrar-domains.txt); do 
  endpoint=https://api.cloudflare.com/client/v4/
  endpoint_target="zones/?name=${domain}&status=active&page=1&per_page=50&order=status&direction=desc&match=all"
  zid=$(curl -4sX GET "${endpoint}${endpoint_target}" -H "Content-Type:application/json" -H "X-Auth-Email: $cfemail" -H "X-Auth-Key: $cftoken" | jq -r --arg d $domain '.result[] | select(.name == $d) | .id')
  echo "$zid $domain" | tee "zoneid-${domain}.log"
done

Then save the concatenated output for the zoneid files to zoneid-domains-list.log:

cat zoneid-*.log > zoneid-domains-list.log

The file will contain:

cat zoneid-domains-list.log
zoneid1 domain.com
zoneid2 domain.net

Check DNSSEC status

Now to do a while read loop reading the ZONEID and domain name to query the Cloudflare DNSSEC API end point to get the current DNSSEC status for each domain which is saved to file dnssec-${domain}.log. The DNSSEC status will either be enabled, disabled or pending-disabled.

cat zoneid-domains-list.log | while read zid domain; do
  endpoint=https://api.cloudflare.com/client/v4/
  endpoint_target="zones/${zid}/dnssec"
  dnssec_status=$(curl -4sX GET "${endpoint}${endpoint_target}" -H "X-Auth-Email: $cfemail" -H "X-Auth-Key: $cftoken" -H "Content-Type: application/json" | jq -r '.result.status')
  echo "$domain $dnssec_status" | tee "dnssec-${domain}.log"
done

Example output:

domain.com pending-disabled
domain.net disabled

Disable DNSSEC

Then disable DNSSEC using another while read loop saving each API query’s result to file disable-dnssec-${domain}.log

cat zoneid-domains-list.log | while read zid domain; do
  endpoint=https://api.cloudflare.com/client/v4/
  endpoint_target="zones/${zid}/dnssec"
  echo "$domain"
  curl -4sX PATCH "${endpoint}${endpoint_target}" \
  -H "X-Auth-Email: $cfemail" -H "X-Auth-Key: $cftoken" \
  -H "Content-Type: application/json" --data '{"status":"disabled"}' | jq -r | tee "disable-dnssec-${domain}.log"
done

Example output below is where DNSSEC has already been disabled:

domain.com
{
  "result": null,
  "success": false,
  "errors": [
    {
      "code": 1004,
      "message": "DNSSEC is already disabled"
    }
  ],
  "messages": []
}
domain.net
{
  "result": null,
  "success": false,
  "errors": [
    {
      "code": 1004,
      "message": "DNSSEC is already disabled"
    }
  ],
  "messages": []
}

Disabling DNSSEC will take some time on Cloudflare’s end and isn’t immediate. So best to recheck DNSSEC status again before proceeding to the next step – ensuring all domains return a disabled DNSSEC status.

Step 5. Bulk Unlock & Obtain EPP/AUTH Code For Cloudflare Domains

Here we’ll be using a bash shell for loop iterating through a list of domain names returned from Cloudflare Registrar API end point for https://api.cloudflare.com/client/v4/accounts/$cfaccountid/registrar/domains and then unlocking each domain, resetting the EPP/AUTH code and then querying and printing out the domain name and EPP/AUTH code in a format that is ready for bulk domain registrar transfers to other domain registrars like Namesilo, Namecheap, Internet.bs, Porkbun and GoDaddy.

First, we’ll populate the domains bash array using step 3 populated cfregistrar-domains.txt file.

domains=$(cat cfregistrar-domains.txt)

If you echo the output of the domains bash array, it would be something like below:

echo ${domains[@]}
domain.com domain.net

Now the for loop that does all the domain unlocking and EPP/AUTH code retrieval. You’ll either be scripting this is a shell script or running the entire code as one command via copy and paste into SSH command line.

It will loop through the above domains bash array and for each domain name returned it will:

  • unlock the domain and save CF API output to log file ${d}-unlock.log where ${d} is your domain name.
  • will save domain’s reset EPP/AUTH code to log file at ${d}-reset-auth.log where ${d} is your domain name. This and the next step are not outlined in current Cloudflare Registrar API documentation which only outlines how to unlock the domain.
  • save full domain registrar info to ${d}-get-auth.log log where ${d} is your domain name and
  • only the domain and EPP/AUTH code properly formatted in ${d}-get-auth-summary.log where ${d} is your domain name.
  • these logs will be all saved in same working directory you run the commands from or run your shell script from
for d in ${domains[@]}; do
  domain=$d
  endpoint=https://api.cloudflare.com/client/v4/
  endpoint_target=accounts/$cfaccountid/registrar/domains/$d
  # unlock
  echo
  echo "unlock: $d"
  echo
  curl -4sX PUT "${endpoint}${endpoint_target}" -H "Content-Type:application/json" \
  -H "X-Auth-Email: $cfemail" \
  -H "X-Auth-Key: $cftoken" --data '{"locked":false}' | tee ${d}-unlock.log | jq
  echo "saved: ${d}-unlock.log"
  
  # reset auth code
  echo "reset auth code: $d"
  echo
  curl -4sX PUT "${endpoint}${endpoint_target}" -H "Content-Type:application/json" \
  -H "X-Auth-Email: $cfemail" \
  -H "X-Auth-Key: $cftoken" --data-raw '{"reset_auth_code":true}' | tee ${d}-reset-auth.log | jq
  echo "saved: ${d}-reset-auth.log"
  
  # obtain auth code
  echo "obtain auth code: $d"
  echo
  curl -4sX GET "${endpoint}${endpoint_target}" -H "Content-Type:application/json" \
  -H "X-Auth-Email: $cfemail" \
  -H "X-Auth-Key: $cftoken" | tee ${d}-get-auth.log | jq -r '.result | "\(.name) \(.auth_code)"' | tee ${d}-get-auth-summary.log
  echo "saved: ${d}-get-auth.log"
done

Example output from for loop which iterated over domain.com and domain.net domains.

unlock: domain.com

{
  "result": {
    "message": "Domain updates complete!"
  },
  "success": true,
  "errors": [],
  "messages": []
}
saved: domain.com-unlock.log
reset auth code: domain.com

{
  "result": {
    "message": "Domain updates complete!"
  },
  "success": true,
  "errors": [],
  "messages": []
}
saved: domain.com-reset-auth.log
obtain auth code: domain.com

domain.com 3-2-HP0-oIbu-9Hb
saved: domain.com-get-auth.log

unlock: domain.net

{
  "result": {
    "message": "Domain updates complete!"
  },
  "success": true,
  "errors": [],
  "messages": []
}
saved: domain.net-unlock.log
reset auth code: domain.net

{
  "result": {
    "message": "Domain updates complete!"
  },
  "success": true,
  "errors": [],
  "messages": []
}
saved: domain.net-reset-auth.log
obtain auth code: domain.net

domain.net iZY-ErqP--6n2-50
saved: domain.net-get-auth.log

Now each domain would of saved a log file named ${d}-get-auth-summary.log. You can concatenate them to show all domains and their EPP/AUTH code in a format you can use to bulk domain transfer to another domain registrar like Namesilo, Namecheap, Internet.bs, Porkbun and GoDaddy.

ls -lrt *-get-auth-summary.log
-rw-r--r-- 1 root root 33 Feb 28 08:00 domain.com-get-auth-summary.log
-rw-r--r-- 1 root root 33 Feb 28 08:00 domain.net-get-auth-summary.log
cat *-get-auth-summary.log

domain.com 3-2-HP0-oIbu-9Hb
domain.net iZY-ErqP--6n2-50

If in future you need to transfer domains back to Cloudflare Domain Registrar, you can also use the Cloudflare API to do such as outlined in examples for Namecheap and Namesilo transfer to Cloudflare Registrar here. I’ll update this guide with further examples in the future so check back frequently.

Step 6. Bulk Fast-Track Transfer Approvals

Once you initiate the domain transfers at your new domain registrar, Cloudflare will send an email for each domain you’re transferring out outlining that it will take up to 5 days to transfer out.

Outbound Domain Transfer Initiated

Hi,

Domain: domain.com

Cloudflare Registrar received an authenticated request to transfer the domain listed above to another registrar.
You do not need to respond to this message to confirm your transfer away. Cloudflare will automatically release
the domain to your new registrar five days after your request. Want to finish the transfer earlier? You can manually
approve the request immediately in the Overview page of the Cloudflare dashboard for your domain.

If you want to cancel this transfer and stay with Cloudflare, you must visit the Overview page of the dashboard and
select “Reject Transfer Out” from your options within the next five days.

On behalf of the Cloudflare team, we are sorry that we will no longer be providing registrar services for your domain

However, you can manually fast-track the domain transfer if you go to each domain’s management configuration page and hit the approve transfer button. Of course, if you have many domains being transferred out, it would take forever! The official Cloudflare API doesn’t document any way to use the API to approve each domain transfer out. But an undocumented API endpoint and query seem to work so you can bulk approve all your domain transfers as outlined below.

Approve Transfer Requests

The undocumented endpoint is at https://api.cloudflare.com/client/v4/zones/ZONEID/registrar/domains/DOMAINNAME/transfer_out where you send a POST request with the payload {"operation":"approve"}.

Using a while read loop piping in the previously generated zoneid-domains-list.log file:

cat zoneid-domains-list.log | while read zid domain; do
  endpoint=https://api.cloudflare.com/client/v4/
  endpoint_target="zones/${zid}/registrar/domains/${domain}/transfer_out"
  echo "$domain"
  curl -4sX POST "${endpoint}${endpoint_target}" \
  -H "X-Auth-Email: $cfemail" -H "X-Auth-Key: $cftoken" \
  -H "Content-Type: application/json" --data '{"operation":"approve"}' | jq -r | tee "approve-transfer-${domain}.log"
done

Example out:

domain.com
{
  "result": {
    "message": "Transfer request approved"
  },
  "success": true,
  "errors": [],
  "messages": []
}
domain.net
{
  "result": {
    "message": "Transfer request approved"
  },
  "success": true,
  "errors": [],
  "messages": []
}

If you already manually approved the transfer via the dashboard, then you’d get the following output:

{
  "result": {
    "message": "You are not allowed to perform this action"
  },
  "success": true,
  "errors": [],
  "messages": []
}

Now you should get an email from Cloudflare confirming approval of the domain transfers.

Domain Transfer Approved

Hi,

We have received your approval to transfer domain.com away from Cloudflare Registrar. We will begin the process
of transferring the domain to your new registrar now.  If you have questions, please contact [email protected].

The Cloudflare Registrar team