Using the Cloudflare API to Manage DNS Records

U

I’m probably doing Cloudflare a disservice by categorising it as a CDN provider, but that’s certainly one of the many services they offer and perhaps how most individuals using their free offering see them. Like me, I’m certain the vast majority of that group use the Cloudflare dashboard to configure their domains, but Cloudflare provides an API that allows you to programmatically manage those DNS records through a command-line interface of a *nix shell such as Bash.

The documentation states:

Using Cloudflare’s API, you can do just about anything you can do on cloudflare.com via the customer dashboard.

Although the Cloudflare API documentation gives numerous examples, it took me a while to get to grips with them, so I thought it may be useful for others if I document my examples.

To use the Cloudflare API you’ll need the email address used to login to your Cloudflare account and your Cloudlare account’s global API key.

Table of Contents

 

 

 

 

2. ABOUT THE CODE SAMPLES

2.1 Code

Below is an example of code used throughout this article together with the output if the request is successful and the output if unsuccessful:

Dummy Content
EMAIL="steve@example.com"; \
KEY="08n46q4ofo0v5pc3u3g3eu517o69axu8s6ml4"; \
ZONE_ID="8b717207bcee4047af2e9dff95832996"; \
TYPE="A"; \
NAME="example.com"; \
CONTENT="203.0.113.50"; \
PROXIED="true"; \
TTL="1"; \
curl -X POST "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records/" \
    -H "X-Auth-Email: $EMAIL" \
    -H "X-Auth-Key: $KEY" \
    -H "Content-Type: application/json" \
    --data '{"type":"'"$TYPE"'","name":"'"$NAME"'","content":"'"$CONTENT"'","proxied":'"$PROXIED"',"ttl":'"$TTL"'}' \
    | python -m json.tool;
{
    "errors": [],
    "messages": [],
    "result": {
        "content": "203.0.113.50",
        "created_on": "2018-01-27T15:57:52.254408Z",
        "id": "02a929c163302fe6ad885e77ad7bd268",
        "locked": false,
        "meta": {
            "auto_added": false,
            "managed_by_apps": false
        },
        "modified_on": "2018-01-27T15:57:52.254408Z",
        "name": "example.com",
        "proxiable": true,
        "proxied": true,
        "ttl": 1,
        "type": "A",
        "zone_id": "8b717207bcee4047af2e9dff95832996",
        "zone_name": "example.com"
    },
    "success": true
}
{
    "errors": [
        {
            "code": 81057,
            "message": "The record already exists."
        }
    ],
    "messages": [],
    "result": null,
    "success": false
}

 

 

I’ve assigned certain values in the code to variables – EMAIL, KEY etc – to make it easier to see how this data is used throughout the code. Below is a sample DNS record on the Cloudflare customer dashboard together with a table summarising the variables used.

Cloudflare dashboard showing a single DNS record for example.com
Cloudflare dashboard showing a single DNS record for example.com

 

 

Variable Value
EMAIL The email address associated with your Cloudflare account.
KEY The global API key associated with your Cloudflare account.
TOKEN Similar to an API key, but allows more fine-tuned access.
DOMAIN The name of the domain to create a zone record for.
JUMP_START If true, automatically attempts to fetch existing DNS records when creating a domain’s zone record
ZONE_ID The unique ID of the domain’s zone record. Assigned by Cloudflare. Required when managing an existing zone record and its DNS records.
DNS_ID The unique ID given to each of the domain’s individual DNS records. Assigned by Cloudflare. Required when updating or deleting an existing DNS record.
TYPE The DNS record type including A, CNAME, MX and TXT records. This equates to the Type column on the Cloudflare dashboard.
NAME The DNS record name. This equates to the Name column on the Cloudflare dashboard.
CONTENT The DNS record content. This equates to the Value column on the Cloudflare dashboard.
PROXIED If true, a DNS record will pass through Cloudflare’s servers. Un-proxied records will not and are for DNS resolution only. Applicable to A and CNAME records only. This equates to the Status column on the Cloudflare dashboard.
TTL Valid TTL. Must be between 120 and 2,147,483,647 seconds, or 1 for automatic
PRIORITY The order in which servers should be contacted. Applicable to MX records only.
ALL If true, JSON output will be pretty-printed using Python’s json.tool module. Otherwise, output will be limited to specified data.

Variables Passed To The Cloudflare API

 

 

 

 

2.2 Output

All requests to Cloudflare’s API are made using HTTPS and return unformatted JSON data. To make this output readable, I’ve piped the JSON data through Python’s json.tool – a simple command line interface for the json module to pretty-print JSON objects. All key/value pairs are sorted alphabetically.

HTTPS requests to the API using the POST or DELETE methods return a JSON object containing 4 top-level key/value pairs. These keys are errors, messages, result and success. The values for these keys can be an empty array:

"errors": [],

 

…another object containing other key/value pairs:

"result": {
    "content": "203.0.113.50",
    "created_on": "2018-01-27T15:57:52.254408Z",
    ...
},

 

…or a boolean value:

"success": true

 

For HTTPS requests using GET, result key value objects are contained within an array:

"result": [
    {
    	...
        "id": "8b717207bcee4047af2e9dff95832996",
    	...
    }
],

 

 

In addition there is a fifth top-level key/value pair for the GET method named result_info whose value is an object containing other key/value pairs:

"result_info": {
    "count": 1,
    ...
},

 

 

 

 

2.3 Alternative Code and Output

Some requests to the Cloudflare API produce a lot of JSON data. As an alternative to the main code samples, I have provided alternative code whose output focuses on a particular piece of data – the unique ID of a domain’s DNS record for example. In the example below, the variable ALL is included and controls how JSON data is displayed. If ALL is true, JSON data is piped to Python’s json.tool as before. However, if false, only limited JSON data – as specified in the statements passed as a command to python – is displayed.

Dummy Content
EMAIL="steve@example.com"; \
KEY="08n46q4ofo0v5pc3u3g3eu517o69axu8s6ml4"; \
ZONE_ID="8b717207bcee4047af2e9dff95832996"; \
TYPE="A"; \
NAME="example.com"; \
CONTENT="203.0.113.50"; \
PROXIED="true"; \
TTL="1"; \
ALL="false"; \
curl -X POST "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records/" \
    -H "X-Auth-Email: $EMAIL" \
    -H "X-Auth-Key: $KEY" \
    -H "Content-Type: application/json" \
    --data '{"type":"'"$TYPE"'","name":"'"$NAME"'","content":"'"$CONTENT"'","proxied":'"$PROXIED"',"ttl":'"$TTL"'}' \
    | if $ALL; then python -m json.tool; else python -c "import sys,json;data=json.loads(sys.stdin.read()); print('Type: ' + data['result']['type'] + '\n' + 'DNS ID: ' + data['result']['id'] if data['success'] else 'ERROR: ' + data['errors'][0]['message'])"; fi
Type: A
DNS ID: 02a929c163302fe6ad885e77ad7bd268
ERROR: The record already exists.

 

 

 

 

2.4 API Keys vs API Tokens

Cloudflare are moving away from using legacy API keys to API tokens. All of the examples in this article use an API key to access the Cloudflare API and continue to work – for now. To use an API token, you need to change this example code…

Dummy Content
EMAIL="steve@example.com"; \
KEY="08n46q4ofo0v5pc3u3g3eu517o69axu8s6ml4"; \
ZONE_ID="8b717207bcee4047af2e9dff95832996"; \
TYPE="A"; \
NAME="example.com"; \
CONTENT="203.0.113.50"; \
PROXIED="true"; \
TTL="1"; \
curl -X POST "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records/" \
    -H "X-Auth-Email: $EMAIL" \
    -H "X-Auth-Key: $KEY" \
    -H "Content-Type: application/json" \
    --data '{"type":"'"$TYPE"'","name":"'"$NAME"'","content":"'"$CONTENT"'","proxied":'"$PROXIED"',"ttl":'"$TTL"'}' \
    | python -m json.tool;

 

 

…to this…

Dummy Content
TOKEN="ZsJcjFDfHxaVnEjjfDdvgpFDHF6jsUGVvyzd6M8R"; \
ZONE_ID="8b717207bcee4047af2e9dff95832996"; \
TYPE="A"; \
NAME="example.com"; \
CONTENT="203.0.113.50"; \
PROXIED="true"; \
TTL="1"; \
curl -X POST "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records/" \
    -H "Authorization: Bearer $TOKEN" \
    -H "Content-Type: application/json" \
    --data '{"type":"'"$TYPE"'","name":"'"$NAME"'","content":"'"$CONTENT"'","proxied":'"$PROXIED"',"ttl":'"$TTL"'}' \
    | python -m json.tool;

 

 

 

 

2.5 Automated Shell Script

I found working with the Cloudflare API cumbersome and somewhat error prone and so put together a shell script to streamline adding new DNS records and updating and deleting existing DNS records. The script requires the domain to have an existing zone record and allows this…

EMAIL="steve@example.com"; \
KEY="08n46q4ofo0v5pc3u3g3eu517o69axu8s6ml4"; \
ZONE_ID="8b717207bcee4047af2e9dff95832996"; \
TYPE="A"; \
NAME="example.com"; \
CONTENT="203.0.113.50"; \
PROXIED="true"; \
TTL="1"; \
curl -X POST "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records/" \
    -H "X-Auth-Email: $EMAIL" \
    -H "X-Auth-Key: $KEY" \
    -H "Content-Type: application/json" \
    --data '{"type":"'"$TYPE"'","name":"'"$NAME"'","content":"'"$CONTENT"'","proxied":'"$PROXIED"',"ttl":'"$TTL"'}' \
    | python -m json.tool;

 

 

…to be replaced with:

./cf-dns.sh -d example.com -n example.com -c 203.0.113.50 -l 1 -x y -k

 

 

Much simpler and no need to be concerned with getting zone IDs or DNS record IDs. The GitHub repository with documentation and examples can be found at cloudflare-dns.

 

 

3. ClOUDFLARE RESTRICTIONS

Adding DNS records on Cloudflare for a given domain will have no affect on where that domain’s DNS resolves until its nameservers are changed via the domain’s registrar to point to Cloudflare’s. As such, you should be able to use any domain name when experimenting with the Cloudflare API, but Cloudflare does impose some restrictions:

  • You can’t create DNS records for recognised domains like google.com, microsoft.com, example.com etc. Should you try, you’ll receive the error: This zone is banned and cannot be added to Cloudflare at this time. I’ve used example.com in all of my code examples, but this is purely illustrative.

  • The domain must be registered. You can’t use google.test, microsoft.invalid, example.localhost even though these TLDs have been reserved for testing and documentation purposes by the IETF (RFC 2606). The error message is We were unable to identify example.localhost as a registered domain.

  • A valid IP address is also required. Using 12.345.678.9 returns DNS Validation Error. I used 203.0.113.50 which is in a range of IP addresses which the IETF (RFC 5737) have reserved for testing and documentation purposes.

 

 

4. WORKING CODE EXAMPLES

Below is a screenshot of the Cloudflare dashboard showing DNS records for the domain example.com. We’ll create each of these DNS records using the Cloudflare API.

Cloudflare dashboard showing DNS records for example.com
Cloudflare dashboard showing DNS records for example.com

 

 

Each of the following code examples when executed from the command-line should do exactly as described, providing valid data is supplied for EMAIL, KEY and where applicable DOMAIN, ZONE_ID and DNS_ID.

All code examples were successfully tested using cURL 7.54.0 and Python 2.7.10 on macOS High Sierra 10.13.3 and cURL 7.47.0 and Python 2.7.12 on Ubuntu 16.04.3.

 

 

4.1 ZONE RECORDS

4.1.1 List All Zone Records

Many interactions with the Cloudflare API require the domain’s zone ID. To list all the domains on your Cloudflare account together with their individual zone ID, use the following code:

Dummy Content
EMAIL="steve@example.com"; \
KEY="08n46q4ofo0v5pc3u3g3eu517o69axu8s6ml4"; \
curl -X GET "https://api.cloudflare.com/client/v4/zones" \
    -H "X-Auth-Email: $EMAIL" \
    -H "X-Auth-Key: $KEY" \
    -H "Content-Type: application/json" \
    | python -c $'import sys,json\ndata=json.loads(sys.stdin.read())\nif data["success"]:\n\tfor dict in data["result"]:print("Zone ID: " + dict["id"] + ", Domain: " + dict["name"])\nelse:print("ERROR(" + str(data["errors"][0]["code"]) + "): " + data["errors"][0]["message"])'
Zone ID:8b717207bcee4047af2e9dff95832996, Domain:example.com
Zone ID:cmgL5LrSzoguaB5b4bBQsEx0YtqEqya4, Domain:example.net
Zone ID:KsuNw7WlMISEOd2tiGlzTXYmJAeZMRJA, Domain:example.org
ERROR(9103): Unknown X-Auth-Key or X-Auth-Email

 

 

 

 

4.1.2 Create a New Zone Record for a Domain

In order to create DNS records for a domain, we first need to create a unique zone record for that domain to which we’ll later add these DNS records. To create a zone record for example.com, use the following code:

Dummy Content
EMAIL="steve@example.com"; \
KEY="08n46q4ofo0v5pc3u3g3eu517o69axu8s6ml4"; \
DOMAIN="example.com"; \
JUMP_START="false"; \
curl -X POST "https://api.cloudflare.com/client/v4/zones/" \
    -H "X-Auth-Email: $EMAIL" \
    -H "X-Auth-Key: $KEY" \
    -H "Content-Type: application/json" \
    --data '{"name":"'"$DOMAIN"'","jump_start":'"$JUMP_START"'}' \
    | python -m json.tool;
{
    "errors": [],
    "messages": [],
    "result": {
        "account": {
            "id": "2b38766c25a309dc4906a2ca27b7951e",
            "name": "steve@example.com"
        },
        "created_on": "2018-01-27T17:21:09.947727Z",
        "development_mode": 0,
        "id": "8b717207bcee4047af2e9dff95832996",
        "meta": {
            "custom_certificate_quota": 0,
            "multiple_railguns_allowed": false,
            "page_rule_quota": 3,
            "phishing_detected": false,
            "step": 4,
            "wildcard_proxiable": false
        },
        "modified_on": "2018-01-27T17:21:09.947727Z",
        "name": "example.com",
        "name_servers": [
            "elsa.ns.cloudflare.com",
            "sid.ns.cloudflare.com"
        ],
        "original_dnshost": null,
        "original_name_servers": [
            "ns1.sedoparking.com",
            "ns2.sedoparking.com"
        ],
        "original_registrar": null,
        "owner": {
            "email": "steve@example.com",
            "id": "4ca324382cc5077590a1808267ebd523",
            "type": "user"
        },
        "paused": false,
        "permissions": [
            "#access:edit",
            "#access:read",
            "#analytics:read",
            "#app:edit",
            "#billing:edit",
            "#billing:read",
            "#cache_purge:edit",
            "#dns_records:edit",
            "#dns_records:read",
            "#lb:edit",
            "#lb:read",
            "#logs:read",
            "#member:edit",
            "#member:read",
            "#organization:edit",
            "#organization:read",
            "#ssl:edit",
            "#ssl:read",
            "#subscription:edit",
            "#subscription:read",
            "#waf:edit",
            "#waf:read",
            "#worker:edit",
            "#worker:read",
            "#zone:edit",
            "#zone:read",
            "#zone_settings:edit",
            "#zone_settings:read"
        ],
        "plan": {
            "can_subscribe": false,
            "currency": "USD",
            "externally_managed": false,
            "frequency": "",
            "id": "0daaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
            "is_subscribed": true,
            "legacy_discount": false,
            "legacy_id": "free",
            "name": "Free Website",
            "price": 0
        },
        "status": "pending",
        "type": "full"
    },
    "success": true
}
{
    "errors": [
        {
            "code": 1061,
            "message": "example.com already exists"
        }
    ],
    "messages": [],
    "result": null,
    "success": false
}

 

 

Alternatively, to limit the data that is displayed use:

Dummy Content
EMAIL="steve@example.com"; \
KEY="08n46q4ofo0v5pc3u3g3eu517o69axu8s6ml4"; \
DOMAIN="example.com"; \
JUMP_START="false"; \
ALL="false"; \
curl -X POST "https://api.cloudflare.com/client/v4/zones/" \
    -H "X-Auth-Email: $EMAIL" \
    -H "X-Auth-Key: $KEY" \
    -H "Content-Type: application/json" \
    --data '{"name":"'"$DOMAIN"'","jump_start":'"$JUMP_START"'}' \
    | if $ALL; then python -m json.tool; else python -c "import sys,json;data=json.loads(sys.stdin.read()); print('ZONE_ID: ' + data['result']['id'] if data['success'] else 'ERROR: ' + data['errors'][0]['message'])"; fi
ZONE_ID: 8b717207bcee4047af2e9dff95832996
ERROR: example.com already exists

 

 

 

 

4.1.3 List an Existing Zone Record for a Domain

To display the existing zone record for example.com, use the following code:

Dummy Content
EMAIL="steve@example.com"; \
KEY="08n46q4ofo0v5pc3u3g3eu517o69axu8s6ml4"; \
DOMAIN="example.com"; \
curl -X GET "https://api.cloudflare.com/client/v4/zones?name=$DOMAIN" \
    -H "X-Auth-Email: $EMAIL" \
    -H "X-Auth-Key: $KEY" \
    -H "Content-Type: application/json" \
    | python -m json.tool;
{
    "errors": [],
    "messages": [],
    "result": [
        {
            "account": {
            	"id": "2b38766c25a309dc4906a2ca27b7951e",
            	"name": "steve@example.com"
            },
            "created_on": "2018-01-27T17:39:39.206820Z",
            "development_mode": 0,
            "id": "8b717207bcee4047af2e9dff95832996",
            "meta": {
                "custom_certificate_quota": 0,
                "multiple_railguns_allowed": false,
                "page_rule_quota": 3,
                "phishing_detected": false,
                "step": 4,
                "wildcard_proxiable": false
            },
            "modified_on": "2018-01-27T17:39:39.206820Z",
            "name": "example.com",
            "name_servers": [
                "elsa.ns.cloudflare.com",
                "sid.ns.cloudflare.com"
            ],
            "original_dnshost": null,
            "original_name_servers": [
                "ns1.sedoparking.com",
                "ns2.sedoparking.com"
            ],
            "original_registrar": null,
            "owner": {
                "email": "steve@example.com",
                "id": "4ca324382cc5077590a1808267ebd523",
                "type": "user"
            },
            "paused": false,
            "permissions": [
                "#access:edit",
                "#access:read",
                "#analytics:read",
                "#app:edit",
                "#billing:edit",
                "#billing:read",
                "#cache_purge:edit",
                "#dns_records:edit",
                "#dns_records:read",
                "#lb:edit",
                "#lb:read",
                "#logs:read",
                "#member:edit",
                "#member:read",
                "#organization:edit",
                "#organization:read",
                "#ssl:edit",
                "#ssl:read",
                "#subscription:edit",
                "#subscription:read",
                "#waf:edit",
                "#waf:read",
                "#worker:edit",
                "#worker:read",
                "#zone:edit",
                "#zone:read",
                "#zone_settings:edit",
                "#zone_settings:read"
            ],
            "plan": {
                "can_subscribe": false,
                "currency": "USD",
                "externally_managed": false,
                "frequency": "",
                "id": "0daaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
                "is_subscribed": true,
                "legacy_discount": false,
                "legacy_id": "free",
                "name": "Free Website",
                "price": 0
            },
            "status": "pending",
            "type": "full"
        }
    ],
    "result_info": {
        "count": 1,
        "page": 1,
        "per_page": 20,
        "total_count": 1,
        "total_pages": 1
    },
    "success": true
}
{
    "errors": [],
    "messages": [],
    "result": [],
    "result_info": {
        "count": 0,
        "page": 1,
        "per_page": 20,
        "total_count": 0,
        "total_pages": 0
    },
    "success": true
}

 

 

Alternatively, to limit the data that is displayed use:

Dummy Content
EMAIL="steve@example.com"; \
KEY="08n46q4ofo0v5pc3u3g3eu517o69axu8s6ml4"; \
DOMAIN="example.com"; \
ALL="false"; \
curl -X GET "https://api.cloudflare.com/client/v4/zones?name=$DOMAIN" \
    -H "X-Auth-Email: $EMAIL" \
    -H "X-Auth-Key: $KEY" \
    -H "Content-Type: application/json" \
    | if $ALL; then python -m json.tool; else python -c "import sys,json;data=json.loads(sys.stdin.read()); print('ZONE_ID: ' + data['result'][0]['id'] if data['result'] else 'ERROR: Does a zone record for \"$DOMAIN\" exist?')"; fi
ZONE_ID: 8b717207bcee4047af2e9dff95832996
ERROR: Does a zone record for "example.com" exist?

 

 

 

 

4.1.4 Delete an Existing Zone Record for a Domain

To delete the existing zone record for example.com and all its related DNS records, use the following code. Note that we need to provide the unique ID of the domain’s existing zone record:

Dummy Content
EMAIL="steve@example.com"; \
KEY="08n46q4ofo0v5pc3u3g3eu517o69axu8s6ml4"; \
ZONE_ID="8b717207bcee4047af2e9dff95832996"; \
curl -X DELETE "https://api.cloudflare.com/client/v4/zones/$ZONE_ID" \
    -H "X-Auth-Email: $EMAIL" \
    -H "X-Auth-Key: $KEY" \
    -H "Content-Type: application/json" \
    | python -m json.tool;
{
    "errors": [],
    "messages": [],
    "result": {
        "id": "8b717207bcee4047af2e9dff95832996"
    },
    "success": true
}
{
    "errors": [
        {
            "code": 1001,
            "message": "Invalid zone identifier"
        }
    ],
    "messages": [],
    "result": null,
    "success": false
}

 

 

Alternatively, to limit the data that is displayed use:

Dummy Content
EMAIL="steve@example.com"; \
KEY="08n46q4ofo0v5pc3u3g3eu517o69axu8s6ml4"; \
ZONE_ID="8b717207bcee4047af2e9dff95832996"; \
ALL="false"; \
curl -X DELETE "https://api.cloudflare.com/client/v4/zones/$ZONE_ID" \
    -H "X-Auth-Email: $EMAIL" \
    -H "X-Auth-Key: $KEY" \
    -H "Content-Type: application/json" \
    | if $ALL; then python -m json.tool; else python -c "import sys,json;data=json.loads(sys.stdin.read()); print('Zone for ID \"$ZONE_ID\" deleted.' if data['success'] else 'ERROR: ' + data['errors'][0]['message'])"; fi
Zone for ID 8b717207bcee4047af2e9dff95832996 deleted.
ERROR: Invalid zone identifier

 

 

 

 

4.2 CREATE NEW DNS RECORDS

4.2.1 Create a New DNS [A] Record for a Domain

To create a DNS record that points example.com to the IP address 203.0.113.50, use the following code:

Dummy Content
EMAIL="steve@example.com"; \
KEY="08n46q4ofo0v5pc3u3g3eu517o69axu8s6ml4"; \
ZONE_ID="8b717207bcee4047af2e9dff95832996"; \
TYPE="A"; \
NAME="example.com"; \
CONTENT="203.0.113.50"; \
PROXIED="true"; \
TTL="1"; \
curl -X POST "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records/" \
    -H "X-Auth-Email: $EMAIL" \
    -H "X-Auth-Key: $KEY" \
    -H "Content-Type: application/json" \
    --data '{"type":"'"$TYPE"'","name":"'"$NAME"'","content":"'"$CONTENT"'","proxied":'"$PROXIED"',"ttl":'"$TTL"'}' \
    | python -m json.tool;
{
    "errors": [],
    "messages": [],
    "result": {
        "content": "203.0.113.50",
        "created_on": "2018-01-27T15:57:52.254408Z",
        "id": "02a929c163302fe6ad885e77ad7bd268",
        "locked": false,
        "meta": {
            "auto_added": false,
            "managed_by_apps": false
        },
        "modified_on": "2018-01-27T15:57:52.254408Z",
        "name": "example.com",
        "proxiable": true,
        "proxied": true,
        "ttl": 1,
        "type": "A",
        "zone_id": "8b717207bcee4047af2e9dff95832996",
        "zone_name": "example.com"
    },
    "success": true
}
{
    "errors": [
        {
            "code": 81057,
            "message": "The record already exists."
        }
    ],
    "messages": [],
    "result": null,
    "success": false
}

 

 

Alternatively, to limit the data that is displayed use:

Dummy Content
EMAIL="steve@example.com"; \
KEY="08n46q4ofo0v5pc3u3g3eu517o69axu8s6ml4"; \
ZONE_ID="8b717207bcee4047af2e9dff95832996"; \
TYPE="A"; \
NAME="example.com"; \
CONTENT="203.0.113.50"; \
PROXIED="true"; \
TTL="1"; \
ALL="false"; \
curl -X POST "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records/" \
    -H "X-Auth-Email: $EMAIL" \
    -H "X-Auth-Key: $KEY" \
    -H "Content-Type: application/json" \
    --data '{"type":"'"$TYPE"'","name":"'"$NAME"'","content":"'"$CONTENT"'","proxied":'"$PROXIED"',"ttl":'"$TTL"'}' \
    | if $ALL; then python -m json.tool; else python -c "import sys,json;data=json.loads(sys.stdin.read()); print('Type: ' + data['result']['type'] + '\n' + 'DNS ID: ' + data['result']['id'] if data['success'] else 'ERROR: ' + data['errors'][0]['message'])"; fi
Type: A
DNS ID: 02a929c163302fe6ad885e77ad7bd268
ERROR: The record already exists.

 

 

The Cloudflare dashboard now shows:

Cloudflare dashboard showing the newly created A record for example.com
Cloudflare dashboard showing the newly created A record for example.com

 

 

 

 

4.2.2 Create a New DNS [A] Record for a Sub-domain

To create a DNS record that points sub-domain.example.com to the IP address 203.0.113.50, use the following code:

Dummy Content
EMAIL="steve@example.com"; \
KEY="08n46q4ofo0v5pc3u3g3eu517o69axu8s6ml4"; \
ZONE_ID="8b717207bcee4047af2e9dff95832996"; \
TYPE="A"; \
NAME="sub-domain"; \
CONTENT="203.0.113.50"; \
PROXIED="true"; \
TTL="1"; \
curl -X POST "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records/" \
    -H "X-Auth-Email: $EMAIL" \
    -H "X-Auth-Key: $KEY" \
    -H "Content-Type: application/json" \
    --data '{"type":"'"$TYPE"'","name":"'"$NAME"'","content":"'"$CONTENT"'","proxied":'"$PROXIED"',"ttl":'"$TTL"'}' \
    | python -m json.tool;
{
    "errors": [],
    "messages": [],
    "result": {
        "content": "203.0.113.50",
        "created_on": "2018-01-31T10:05:17.945648Z",
        "id": "1edaebfd2c02a69beec51da4476e743e",
        "locked": false,
        "meta": {
            "auto_added": false,
            "managed_by_apps": false
        },
        "modified_on": "2018-01-31T10:05:17.945648Z",
        "name": "sub-domain.example.com",
        "proxiable": true,
        "proxied": true,
        "ttl": 1,
        "type": "A",
        "zone_id": "8b717207bcee4047af2e9dff95832996",
        "zone_name": "example.com"
    },
    "success": true
}
{
    "errors": [
        {
            "code": 81057,
            "message": "The record already exists."
        }
    ],
    "messages": [],
    "result": null,
    "success": false
}

 

 

Alternatively, to limit the data that is displayed use:

Dummy Content
EMAIL="steve@example.com"; \
KEY="08n46q4ofo0v5pc3u3g3eu517o69axu8s6ml4"; \
ZONE_ID="8b717207bcee4047af2e9dff95832996"; \
TYPE="A"; \
NAME="sub-domain"; \
CONTENT="203.0.113.50"; \
PROXIED="true"; \
TTL="1"; \
ALL="false"; \
curl -X POST "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records/" \
    -H "X-Auth-Email: $EMAIL" \
    -H "X-Auth-Key: $KEY" \
    -H "Content-Type: application/json" \
    --data '{"type":"'"$TYPE"'","name":"'"$NAME"'","content":"'"$CONTENT"'","proxied":'"$PROXIED"',"ttl":'"$TTL"'}' \
    | if $ALL; then python -m json.tool; else python -c "import sys,json;data=json.loads(sys.stdin.read()); print('Type: ' + data['result']['type'] + '\n' + 'DNS ID: ' + data['result']['id'] if data['success'] else 'ERROR: ' + data['errors'][0]['message'])"; fi
Type: A
DNS ID: 1edaebfd2c02a69beec51da4476e743e
ERROR: The record already exists.

 

 

The Cloudflare dashboard now shows:

Cloudflare dashboard showing the newly created A record for sub-domain.example.com
Cloudflare dashboard showing the newly created A record for sub-domain.example.com

 

 

 

 

4.2.3 Create a New DNS [CNAME] Record for an Alias

To create a DNS record that makes www.example.com an alias of example.com, use the following code:

Dummy Content
EMAIL="steve@example.com"; \
KEY="08n46q4ofo0v5pc3u3g3eu517o69axu8s6ml4"; \
ZONE_ID="8b717207bcee4047af2e9dff95832996"; \
TYPE="CNAME"; \
NAME="www"; \
CONTENT="example.com"; \
PROXIED="true"; \
TTL="1"; \
curl -X POST "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records/" \
    -H "X-Auth-Email: $EMAIL" \
    -H "X-Auth-Key: $KEY" \
    -H "Content-Type: application/json" \
    --data '{"type":"'"$TYPE"'","name":"'"$NAME"'","content":"'"$CONTENT"'","proxied":'"$PROXIED"',"ttl":'"$TTL"'}' \
    | python -m json.tool;
{
    "errors": [],
    "messages": [],
    "result": {
        "content": "example.com",
        "created_on": "2018-01-31T10:27:57.075092Z",
        "id": "7bdb2e46037df332e5abdd45f8f981f5",
        "locked": false,
        "meta": {
            "auto_added": false,
            "managed_by_apps": false
        },
        "modified_on": "2018-01-31T10:27:57.075092Z",
        "name": "www.example.com",
        "proxiable": true,
        "proxied": true,
        "ttl": 1,
        "type": "CNAME",
        "zone_id": "8b717207bcee4047af2e9dff95832996",
        "zone_name": "example.com"
    },
    "success": true
}
{
    "errors": [
        {
            "code": 81053,
            "message": "An A, AAAA or CNAME record already exists with that host."
        }
    ],
    "messages": [],
    "result": null,
    "success": false
}

 

 

Alternatively, to limit the data that is displayed use:

Dummy Content
EMAIL="steve@example.com"; \
KEY="08n46q4ofo0v5pc3u3g3eu517o69axu8s6ml4"; \
ZONE_ID="8b717207bcee4047af2e9dff95832996"; \
TYPE="CNAME"; \
NAME="www"; \
CONTENT="example.com"; \
PROXIED="true"; \
TTL="1"; \
ALL="false"; \
curl -X POST "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records/" \
    -H "X-Auth-Email: $EMAIL" \
    -H "X-Auth-Key: $KEY" \
    -H "Content-Type: application/json" \
    --data '{"type":"'"$TYPE"'","name":"'"$NAME"'","content":"'"$CONTENT"'","proxied":'"$PROXIED"',"ttl":'"$TTL"'}' \
    | if $ALL; then python -m json.tool; else python -c "import sys,json;data=json.loads(sys.stdin.read()); print('Type: ' + data['result']['type'] + '\n' + 'DNS ID: ' + data['result']['id'] if data['success'] else 'ERROR: ' + data['errors'][0]['message'])"; fi
Type: CNAME
DNS ID: 7bdb2e46037df332e5abdd45f8f981f5
ERROR: An A, AAAA or CNAME record already exists with that host.

 

 

The Cloudflare dashboard now shows:

Cloudflare dashboard showing the newly created CNAME record for www.example.com
Cloudflare dashboard showing the newly created CNAME record for www.example.com

 

 

 

 

4.2.4 Create a New DNS [MX] Record for a Domain

To create a DNS record that specifies the primary mail server to handle mail for example.com, use the following code:

Dummy Content
EMAIL="steve@example.com"; \
KEY="08n46q4ofo0v5pc3u3g3eu517o69axu8s6ml4"; \
ZONE_ID="8b717207bcee4047af2e9dff95832996"; \
TYPE="MX"; \
NAME="@"; \
CONTENT="aspmx.l.google.com"; \
PRIORITY="1"; \
TTL="1"; \
curl -X POST "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records/" \
    -H "X-Auth-Email: $EMAIL" \
    -H "X-Auth-Key: $KEY" \
    -H "Content-Type: application/json" \
    --data '{"type":"'"$TYPE"'","name":"'"$NAME"'","content":"'"$CONTENT"'","priority":'"$PRIORITY"',"ttl":'"$TTL"'}' \
    | python -m json.tool;
{
    "errors": [],
    "messages": [],
    "result": {
        "content": "aspmx.l.google.com",
        "created_on": "2018-01-31T10:36:15.396025Z",
        "id": "4983f68556095761d8f76af09f48b093",
        "locked": false,
        "meta": {
            "auto_added": false,
            "managed_by_apps": false
        },
        "modified_on": "2018-01-31T10:36:15.396025Z",
        "name": "example.com",
        "priority": 1,
        "proxiable": false,
        "proxied": false,
        "ttl": 1,
        "type": "MX",
        "zone_id": "8b717207bcee4047af2e9dff95832996",
        "zone_name": "example.com"
    },
    "success": true
}
{
    "errors": [
        {
            "code": 81057,
            "message": "The record already exists."
        }
    ],
    "messages": [],
    "result": null,
    "success": false
}

 

 

Alternatively, to limit the data that is displayed use:

Dummy Content
EMAIL="steve@example.com"; \
KEY="08n46q4ofo0v5pc3u3g3eu517o69axu8s6ml4"; \
ZONE_ID="8b717207bcee4047af2e9dff95832996"; \
TYPE="MX"; \
NAME="@"; \
CONTENT="aspmx.l.google.com"; \
PRIORITY="1"; \
TTL="1"; \
ALL="false"; \
curl -X POST "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records/" \
    -H "X-Auth-Email: $EMAIL" \
    -H "X-Auth-Key: $KEY" \
    -H "Content-Type: application/json" \
    --data '{"type":"'"$TYPE"'","name":"'"$NAME"'","content":"'"$CONTENT"'","priority":'"$PRIORITY"',"ttl":'"$TTL"'}' \
    | if $ALL; then python -m json.tool; else python -c "import sys,json;data=json.loads(sys.stdin.read()); print('Type: ' + data['result']['type'] + '\n' + 'DNS ID: ' + data['result']['id'] if data['success'] else 'ERROR: ' + data['errors'][0]['message'])"; fi
Type: MX
DNS ID: 4983f68556095761d8f76af09f48b093
ERROR: The record already exists.

 

 

The Cloudflare dashboard now shows:

Cloudflare dashboard showing newly created 1st MX record for example.com
Cloudflare dashboard showing the newly created 1st MX record for example.com

 

 

 

 

4.2.5 Create a Second New DNS [MX] Record for a Domain

To create a DNS record that specifies the secondary mail server to handle mail for example.com, use the following code:

Dummy Content
EMAIL="steve@example.com"; \
KEY="08n46q4ofo0v5pc3u3g3eu517o69axu8s6ml4"; \
ZONE_ID="8b717207bcee4047af2e9dff95832996"; \
TYPE="MX"; \
NAME="@"; \
CONTENT="alt1.aspmx.l.google.com"; \
PRIORITY="5"; \
TTL="1"; \
curl -X POST "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records/" \
    -H "X-Auth-Email: $EMAIL" \
    -H "X-Auth-Key: $KEY" \
    -H "Content-Type: application/json" \
    --data '{"type":"'"$TYPE"'","name":"'"$NAME"'","content":"'"$CONTENT"'","priority":'"$PRIORITY"',"ttl":'"$TTL"'}' \
    | python -m json.tool;
{
    "errors": [],
    "messages": [],
    "result": {
        "content": "alt1.aspmx.l.google.com",
        "created_on": "2018-01-31T11:05:36.960561Z",
        "id": "6b21db8d6a20c0011971d4ec2c3b8445",
        "locked": false,
        "meta": {
            "auto_added": false,
            "managed_by_apps": false
        },
        "modified_on": "2018-01-31T11:05:36.960561Z",
        "name": "example.com",
        "priority": 1,
        "proxiable": false,
        "proxied": false,
        "ttl": 1,
        "type": "MX",
        "zone_id": "8b717207bcee4047af2e9dff95832996",
        "zone_name": "example.com"
    },
    "success": true
}
{
    "errors": [
        {
            "code": 81057,
            "message": "The record already exists."
        }
    ],
    "messages": [],
    "result": null,
    "success": false
}

 

 

Alternatively, to limit the data that is displayed use:

Dummy Content
EMAIL="steve@example.com"; \
KEY="08n46q4ofo0v5pc3u3g3eu517o69axu8s6ml4"; \
ZONE_ID="8b717207bcee4047af2e9dff95832996"; \
TYPE="MX"; \
NAME="@"; \
CONTENT="alt1.aspmx.l.google.com"; \
PRIORITY="5"; \
TTL="1"; \
ALL="false"; \
curl -X POST "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records/" \
    -H "X-Auth-Email: $EMAIL" \
    -H "X-Auth-Key: $KEY" \
    -H "Content-Type: application/json" \
    --data '{"type":"'"$TYPE"'","name":"'"$NAME"'","content":"'"$CONTENT"'","priority":'"$PRIORITY"',"ttl":'"$TTL"'}' \
    | if $ALL; then python -m json.tool; else python -c "import sys,json;data=json.loads(sys.stdin.read()); print('Type: ' + data['result']['type'] + '\n' + 'DNS ID: ' + data['result']['id'] if data['success'] else 'ERROR: ' + data['errors'][0]['message'])"; fi
Type: MX
DNS ID: 6b21db8d6a20c0011971d4ec2c3b8445
ERROR: The record already exists.

 

 

The Cloudflare dashboard now shows:

Cloudflare dashboard showing the newly created 2nd MX record for example.com
Cloudflare dashboard showing the newly created 2nd MX record for example.com

 

 

 

 

4.2.6 Create a New DNS [TXT] Record

To create a DNS record that specifies the sender policy framework (SPF) for example.com, use the following code:

Dummy Content
EMAIL="steve@example.com"; \
KEY="08n46q4ofo0v5pc3u3g3eu517o69axu8s6ml4"; \
ZONE_ID="8b717207bcee4047af2e9dff95832996"; \
TYPE="TXT"; \
NAME="@"; \
CONTENT="v=spf1 include:_spf.google.com ~all"; \
TTL="1"; \
curl -X POST "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records/" \
    -H "X-Auth-Email: $EMAIL" \
    -H "X-Auth-Key: $KEY" \
    -H "Content-Type: application/json" \
    --data '{"type":"'"$TYPE"'","name":"'"$NAME"'","content":"'"$CONTENT"'","ttl":'"$TTL"'}' \
    | python -m json.tool;
{
    "errors": [],
    "messages": [],
    "result": {
        "content": "v=spf1 include:_spf.google.com ~all",
        "created_on": "2018-01-31T11:13:04.971069Z",
        "id": "3c90b59fca40473f26d8edf4c7e5fdb1",
        "locked": false,
        "meta": {
            "auto_added": false,
            "managed_by_apps": false
        },
        "modified_on": "2018-01-31T11:13:04.971069Z",
        "name": "example.com",
        "proxiable": false,
        "proxied": false,
        "ttl": 1,
        "type": "TXT",
        "zone_id": "8b717207bcee4047af2e9dff95832996",
        "zone_name": "example.com"
    },
    "success": true
}
{
    "errors": [
        {
            "code": 81057,
            "message": "The record already exists."
        }
    ],
    "messages": [],
    "result": null,
    "success": false
}

 

 

Alternatively, to limit the data that is displayed use:

Dummy Content
EMAIL="steve@example.com"; \
KEY="08n46q4ofo0v5pc3u3g3eu517o69axu8s6ml4"; \
ZONE_ID="8b717207bcee4047af2e9dff95832996"; \
TYPE="TXT"; \
NAME="@"; \
CONTENT="v=spf1 include:_spf.google.com ~all"; \
TTL="1"; \
ALL="false"; \
curl -X POST "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records/" \
    -H "X-Auth-Email: $EMAIL" \
    -H "X-Auth-Key: $KEY" \
    -H "Content-Type: application/json" \
    --data '{"type":"'"$TYPE"'","name":"'"$NAME"'","content":"'"$CONTENT"'","ttl":'"$TTL"'}' \
    | if $ALL; then python -m json.tool; else python -c "import sys,json;data=json.loads(sys.stdin.read()); print('Type: ' + data['result']['type'] + '\n' + 'DNS ID: ' + data['result']['id'] if data['success'] else 'ERROR: ' + data['errors'][0]['message'])"; fi
Type: TXT
DNS ID: 3c90b59fca40473f26d8edf4c7e5fdb1
ERROR: The record already exists.

 

 

The Cloudflare dashboard now shows:

Cloudflare dashboard showing newly created TXT record for example.com
Cloudflare dashboard showing the newly created TXT record for example.com

 

 

 

 

4.3. LIST, UPDATE & DELETE EXISTING DNS RECORDS

4.3.1 List All DNS Records for a Zone

To list all the DNS records associated with the zone record for example.com, use the following code:

Dummy Content
EMAIL="steve@example.com"; \
KEY="08n46q4ofo0v5pc3u3g3eu517o69axu8s6ml4"; \
ZONE_ID="8b717207bcee4047af2e9dff95832996"; \
curl -X GET "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records" \
    -H "X-Auth-Email: $EMAIL" \
    -H "X-Auth-Key: $KEY" \
    -H "Content-Type: application/json" \
    | python -m json.tool;
{
    "errors": [],
    "messages": [],
    "result": [
        {
            "content": "203.0.113.50",
            "created_on": "2018-02-09T08:40:30.673141Z",
            "id": "02a929c163302fe6ad885e77ad7bd268",
            "locked": false,
            "meta": {
                "auto_added": false,
                "managed_by_apps": false
            },
            "modified_on": "2018-02-09T08:40:30.673141Z",
            "name": "example.com",
            "proxiable": true,
            "proxied": true,
            "ttl": 1,
            "type": "A",
            "zone_id": "8b717207bcee4047af2e9dff95832996",
            "zone_name": "example.com"
        },
        {
            "content": "203.0.113.50",
            "created_on": "2018-02-09T08:43:22.134574Z",
            "id": "1edaebfd2c02a69beec51da4476e743e",
            "locked": false,
            "meta": {
                "auto_added": false,
                "managed_by_apps": false
            },
            "modified_on": "2018-02-09T08:43:22.134574Z",
            "name": "sub-domain.example.com",
            "proxiable": true,
            "proxied": true,
            "ttl": 1,
            "type": "A",
            "zone_id": "8b717207bcee4047af2e9dff95832996",
            "zone_name": "example.com"
        },
        {
            "content": "example.com",
            "created_on": "2018-02-09T08:44:10.505243Z",
            "id": "7bdb2e46037df332e5abdd45f8f981f5",
            "locked": false,
            "meta": {
                "auto_added": false,
                "managed_by_apps": false
            },
            "modified_on": "2018-02-09T08:44:10.505243Z",
            "name": "www.example.com",
            "proxiable": true,
            "proxied": true,
            "ttl": 1,
            "type": "CNAME",
            "zone_id": "8b717207bcee4047af2e9dff95832996",
            "zone_name": "example.com"
        },
        {
            "content": "alt1.aspmx.l.google.com",
            "created_on": "2018-02-09T08:47:23.832766Z",
            "id": "6b21db8d6a20c0011971d4ec2c3b8445",
            "locked": false,
            "meta": {
                "auto_added": false,
                "managed_by_apps": false
            },
            "modified_on": "2018-02-09T08:47:23.832766Z",
            "name": "example.com",
            "priority": 5,
            "proxiable": false,
            "proxied": false,
            "ttl": 1,
            "type": "MX",
            "zone_id": "8b717207bcee4047af2e9dff95832996",
            "zone_name": "example.com"
        },
        {
            "content": "aspmx.l.google.com",
            "created_on": "2018-02-09T08:46:28.139571Z",
            "id": "4983f68556095761d8f76af09f48b093",
            "locked": false,
            "meta": {
                "auto_added": false,
                "managed_by_apps": false
            },
            "modified_on": "2018-02-09T08:46:28.139571Z",
            "name": "example.com",
            "priority": 1,
            "proxiable": false,
            "proxied": false,
            "ttl": 1,
            "type": "MX",
            "zone_id": "8b717207bcee4047af2e9dff95832996",
            "zone_name": "example.com"
        },
        {
            "content": "v=spf1 include:_spf.google.com ~all",
            "created_on": "2018-02-09T08:48:36.208222Z",
            "id": "3c90b59fca40473f26d8edf4c7e5fdb1",
            "locked": false,
            "meta": {
                "auto_added": false,
                "managed_by_apps": false
            },
            "modified_on": "2018-02-09T08:48:36.208222Z",
            "name": "example.com",
            "proxiable": false,
            "proxied": false,
            "ttl": 1,
            "type": "TXT",
            "zone_id": "8b717207bcee4047af2e9dff95832996",
            "zone_name": "example.com"
        }
    ],
    "result_info": {
        "count": 6,
        "page": 1,
        "per_page": 20,
        "total_count": 6,
        "total_pages": 1
    },
    "success": true
}
{
    "errors": [],
    "messages": [],
    "result": [],
    "result_info": {
        "count": 0,
        "page": 1,
        "per_page": 20,
        "total_count": 0,
        "total_pages": 0
    },
    "success": true
}

 

 

A far better alternative perhaps is to use the code below which limits the output to specific data and is useful if you need to quickly get the ID of an existing DNS record in order to update or delete it.

Dummy Content

EMAIL="steve@example.com"; \
KEY="08n46q4ofo0v5pc3u3g3eu517o69axu8s6ml4"; \
ZONE_ID="8b717207bcee4047af2e9dff95832996"; \
curl -X GET "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records" \
    -H "X-Auth-Email: $EMAIL" \
    -H "X-Auth-Key: $KEY" \
    -H 'Content-Type: application/json' \
    | python -c $'import sys,json\ndata=json.loads(sys.stdin.read())\nif data["success"]:\n\tfor dict in data["result"]:print("ID: " + dict["id"] + ", Type: " + dict["type"] + ", " + "Name: " + dict["name"] + ", " + "Content: " + dict["content"])\nelse:print("ERROR(" + str(data["errors"][0]["code"]) + "): " + data["errors"][0]["message"])'
ID: 02a929c163302fe6ad885e77ad7bd268, Type: A, Name: example.com, Content: 203.0.113.50
ID: 1edaebfd2c02a69beec51da4476e743e, Type: A, Name: sub-domain.example.com, Content: 203.0.113.50
ID: 7bdb2e46037df332e5abdd45f8f981f5, Type: CNAME, Name: www.example.com, Content: example.com
ID: 6b21db8d6a20c0011971d4ec2c3b8445, Type: MX, Name: example.com, Content: alt1.aspmx.l.google.com
ID: 4983f68556095761d8f76af09f48b093, Type: MX, Name: example.com, Content: aspmx.l.google.com
ID: 3c90b59fca40473f26d8edf4c7e5fdb1, Type: TXT, Name: example.com, Content: v=spf1 include:_spf.google.com ~all
ERROR(10000): Authentication error

 

 

 

 

4.3.2 List DNS Records for a Zone Based on DNS Record Name

To list all the DNS records associated with the zone record for example.com whose name is example.com, use the following code:

Dummy Content
EMAIL="steve@example.com"; \
KEY="08n46q4ofo0v5pc3u3g3eu517o69axu8s6ml4"; \
ZONE_ID="8b717207bcee4047af2e9dff95832996"; \
NAME="example.com"; \
curl -X GET "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records?name=$NAME" \
    -H "X-Auth-Email: $EMAIL" \
    -H "X-Auth-Key: $KEY" \
    -H "Content-Type: application/json" \
    | python -m json.tool;
{
    "errors": [],
    "messages": [],
    "result": [
        {
            "content": "203.0.113.50",
            "created_on": "2018-02-09T08:40:30.673141Z",
            "id": "02a929c163302fe6ad885e77ad7bd268",
            "locked": false,
            "meta": {
                "auto_added": false,
                "managed_by_apps": false
            },
            "modified_on": "2018-02-09T08:40:30.673141Z",
            "name": "example.com",
            "proxiable": true,
            "proxied": true,
            "ttl": 1,
            "type": "A",
            "zone_id": "8b717207bcee4047af2e9dff95832996",
            "zone_name": "example.com"
        },
        {
            "content": "alt1.aspmx.l.google.com",
            "created_on": "2018-02-09T08:47:23.832766Z",
            "id": "6b21db8d6a20c0011971d4ec2c3b8445",
            "locked": false,
            "meta": {
                "auto_added": false,
                "managed_by_apps": false
            },
            "modified_on": "2018-02-09T08:47:23.832766Z",
            "name": "example.com",
            "priority": 5,
            "proxiable": false,
            "proxied": false,
            "ttl": 1,
            "type": "MX",
            "zone_id": "8b717207bcee4047af2e9dff95832996",
            "zone_name": "example.com"
        },
        {
            "content": "aspmx.l.google.com",
            "created_on": "2018-02-09T08:46:28.139571Z",
            "id": "4983f68556095761d8f76af09f48b093",
            "locked": false,
            "meta": {
                "auto_added": false,
                "managed_by_apps": false
            },
            "modified_on": "2018-02-09T08:46:28.139571Z",
            "name": "example.com",
            "priority": 1,
            "proxiable": false,
            "proxied": false,
            "ttl": 1,
            "type": "MX",
            "zone_id": "8b717207bcee4047af2e9dff95832996",
            "zone_name": "example.com"
        },
        {
            "content": "v=spf1 include:_spf.google.com ~all",
            "created_on": "2018-02-09T08:48:36.208222Z",
            "id": "3c90b59fca40473f26d8edf4c7e5fdb1",
            "locked": false,
            "meta": {
                "auto_added": false,
                "managed_by_apps": false
            },
            "modified_on": "2018-02-09T08:48:36.208222Z",
            "name": "example.com",
            "proxiable": false,
            "proxied": false,
            "ttl": 1,
            "type": "TXT",
            "zone_id": "8b717207bcee4047af2e9dff95832996",
            "zone_name": "example.com"
        }
    ],
    "result_info": {
        "count": 4,
        "page": 1,
        "per_page": 20,
        "total_count": 4,
        "total_pages": 1
    },
    "success": true
}
{
    "errors": [],
    "messages": [],
    "result": [],
    "result_info": {
        "count": 0,
        "page": 1,
        "per_page": 20,
        "total_count": 0,
        "total_pages": 0
    },
    "success": true
}

 

 

 

 

4.3.3 List DNS Records for a Zone Based on DNS Record Type

To list all the DNS records associated with the zone record for example.com whose type is MX, use the following code:

Dummy Content
EMAIL="steve@example.com"; \
KEY="08n46q4ofo0v5pc3u3g3eu517o69axu8s6ml4"; \
ZONE_ID="8b717207bcee4047af2e9dff95832996"; \
TYPE="MX"; \
curl -X GET "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records?type=$TYPE" \
    -H "X-Auth-Email: $EMAIL" \
    -H "X-Auth-Key: $KEY" \
    -H "Content-Type: application/json" \
    | python -m json.tool;
{
    "errors": [],
    "messages": [],
    "result": [
        {
            "content": "alt1.aspmx.l.google.com",
            "created_on": "2018-02-09T10:55:50.545902Z",
            "id": "6b21db8d6a20c0011971d4ec2c3b8445",
            "locked": false,
            "meta": {
                "auto_added": false,
                "managed_by_apps": false
            },
            "modified_on": "2018-02-09T10:55:50.545902Z",
            "name": "example.com",
            "priority": 5,
            "proxiable": false,
            "proxied": false,
            "ttl": 1,
            "type": "MX",
            "zone_id": "8b717207bcee4047af2e9dff95832996",
            "zone_name": "example.com"
        },
        {
            "content": "aspmx.l.google.com",
            "created_on": "2018-02-09T10:55:01.615968Z",
            "id": "4983f68556095761d8f76af09f48b093",
            "locked": false,
            "meta": {
                "auto_added": false,
                "managed_by_apps": false
            },
            "modified_on": "2018-02-09T10:55:01.615968Z",
            "name": "example.com",
            "priority": 1,
            "proxiable": false,
            "proxied": false,
            "ttl": 1,
            "type": "MX",
            "zone_id": "8b717207bcee4047af2e9dff95832996",
            "zone_name": "example.com"
        }
    ],
    "result_info": {
        "count": 2,
        "page": 1,
        "per_page": 20,
        "total_count": 2,
        "total_pages": 1
    },
    "success": true
}
{
    "errors": [],
    "messages": [],
    "result": [],
    "result_info": {
        "count": 0,
        "page": 1,
        "per_page": 20,
        "total_count": 0,
        "total_pages": 0
    },
    "success": true
}

 

 

 

 

4.3.4 List DNS Records for a Zone Based on DNS Record Name and Type

To list all the DNS records associated with the zone record for example.com whose name is example.com and type is MX, use the following code:

Dummy Content
EMAIL="steve@example.com"; \
KEY="08n46q4ofo0v5pc3u3g3eu517o69axu8s6ml4"; \
ZONE_ID="8b717207bcee4047af2e9dff95832996"; \
NAME="example.com"; \
TYPE="MX"; \
curl -X GET "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records?name=$NAME&type=$TYPE" \
    -H "X-Auth-Email: $EMAIL" \
    -H "X-Auth-Key: $KEY" \
    -H "Content-Type: application/json" \
    | python -m json.tool;
{
    "errors": [],
    "messages": [],
    "result": [
        {
            "content": "alt1.aspmx.l.google.com",
            "created_on": "2018-02-09T08:47:23.832766Z",
            "id": "ec3045d58f0cde044d73198af3dc77f4",
            "locked": false,
            "meta": {
                "auto_added": false,
                "managed_by_apps": false
            },
            "modified_on": "2018-02-09T08:47:23.832766Z",
            "name": "dummy.com",
            "priority": 5,
            "proxiable": false,
            "proxied": false,
            "ttl": 1,
            "type": "MX",
            "zone_id": "8b717207bcee4047af2e9dff95832996",
            "zone_name": "dummy.com"
        },
        {
            "content": "aspmx.l.google.com",
            "created_on": "2018-02-09T08:46:28.139571Z",
            "id": "cab6d9fa24a3946530f0c1868d83bf8d",
            "locked": false,
            "meta": {
                "auto_added": false,
                "managed_by_apps": false
            },
            "modified_on": "2018-02-09T08:46:28.139571Z",
            "name": "dummy.com",
            "priority": 1,
            "proxiable": false,
            "proxied": false,
            "ttl": 1,
            "type": "MX",
            "zone_id": "8b717207bcee4047af2e9dff95832996",
            "zone_name": "dummy.com"
        }
    ],
    "result_info": {
        "count": 2,
        "page": 1,
        "per_page": 20,
        "total_count": 2,
        "total_pages": 1
    },
    "success": true
}
{
    "errors": [],
    "messages": [],
    "result": [],
    "result_info": {
        "count": 0,
        "page": 1,
        "per_page": 20,
        "total_count": 0,
        "total_pages": 0
    },
    "success": true
}

 

 

 

 

4.3.5 Update an Individual DNS Record

To update an individual DNS record we need to pass its unique ID to the Cloudflare API. So, to update the CNAME record for www.example.com so that PROXIED is changed to false and TTL is changed to 2 minutes, use the following code. Note that even though TYPE, NAME and CONTENT are not changing, they still need to be included otherwise the Cloudflare API returns an error:

Dummy Content
EMAIL="steve@example.com"; \
KEY="08n46q4ofo0v5pc3u3g3eu517o69axu8s6ml4"; \
ZONE_ID="8b717207bcee4047af2e9dff95832996"; \
DNS_ID="7bdb2e46037df332e5abdd45f8f981f5"; \
TYPE="CNAME"; \
NAME="www"; \
CONTENT="example.com"; \
PROXIED="false"; \
TTL="120";\
curl -X PUT "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records/$DNS_ID" \
    -H "X-Auth-Email: $EMAIL" \
    -H "X-Auth-Key: $KEY" \
    -H "Content-Type: application/json" \
    --data '{"type":"'"$TYPE"'","name":"'"$NAME"'","content":"'"$CONTENT"'","proxied":'"$PROXIED"',"ttl":'"$TTL"'}' \
    | python -m json.tool;
{
    "errors": [],
    "messages": [],
    "result": {
        "content": "example.com",
        "created_on": "2018-02-09T09:58:05.239777Z",
        "id": "7bdb2e46037df332e5abdd45f8f981f5",
        "locked": false,
        "meta": {
            "auto_added": false,
            "managed_by_apps": false
        },
        "modified_on": "2018-02-09T09:58:05.239777Z",
        "name": "www.example.com",
        "proxiable": true,
        "proxied": false,
        "ttl": 120,
        "type": "CNAME",
        "zone_id": "8b717207bcee4047af2e9dff95832996",
        "zone_name": "example.com"
    },
    "success": true
}
{
    "errors": [
        {
            "code": 1020,
            "message": "Invalid DNS record identifier"
        }
    ],
    "messages": [],
    "result": null,
    "success": false
}

 

 

DNS CNAME record pre-update for example.com
DNS CNAME record pre-update for example.com

 

 

DNS CNAME record post-update for example.com
DNS CNAME record post-update for example.com

 

 

 

 

4.3.6 Delete an Individual DNS Record

To delete an individual DNS record associated with the zone record for example.com, use the following code. Note thet the DNS record’s unique ID needs to be included:

Dummy Content
EMAIL="steve@example.com"; \
KEY="08n46q4ofo0v5pc3u3g3eu517o69axu8s6ml4"; \
ZONE_ID="8b717207bcee4047af2e9dff95832996"; \
DNS_ID="7bdb2e46037df332e5abdd45f8f981f5"; \
curl -X DELETE "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records/$DNS_ID" \
    -H "X-Auth-Email: $EMAIL" \
    -H "X-Auth-Key: $KEY" \
    -H "Content-Type: application/json" \
    | python -m json.tool;
{
    "errors": [],
    "messages": [],
    "result": {
        "id": "7bdb2e46037df332e5abdd45f8f981f5"
    },
    "success": true
} 
{
    "errors": [
        {
            "code": 1032,
            "message": "Invalid DNS record identifier"
        }
    ],
    "messages": [],
    "result": null,
    "success": false
}

 

 

About the author

A native Brit exiled in Japan, Steve spends too much of his time struggling with the Japanese language, dreaming of fish & chips and writing the occasional blog post he hopes others will find helpful.

13 responses

13 Comments

  • is there a way to list dns records from cloudflare using its API not using curl, in php, jquery or javascript?
    if possible with example pls. Thanks

    • Hi Khasan,

      The get-dns.py script on the cloudflare-dns repo’s develop branch uses the Python requests library to access the Cloudflare API, but I’ve no experience using PHP, jQuery or JavaScript to do so.

      Regards, Steve.

      • thanks for reply, take a look at this link https://developers.cloudflare.com/api/operations/dns-records-for-a-zone-list-dns-records.
        Here is given examples how to use with javascript, jquery but i could not fetch the records according to example. May be anyone could help me. Thanks again.

        • Hi Khasan,

          I tried the XMLHttpRequest and fetch examples but ran into issues with CORS. With fetch I included mode: no-cors, which resulted in an HTTP 400 error.

          const options = {
            method: 'GET',
            headers: {'Content-Type': 'application/json', 'X-Auth-Email': 'steve@example.com', 'X-Auth-Key': '08n46q4ofo0v5pc3u3g3eu517o69axu8s6ml4'},
            mode: 'no-cors'
          };
          
          fetch('https://api.cloudflare.com/client/v4/zones/8b717207bcee4047af2e9dff95832996/dns_records', options)
            .then(response => response.json())
            .then(response => console.log(response))
            .catch(err => console.error(err));
          

          Regards, Steve

    • Hi Serhii,

      As far as I’m aware there is no single API call to delete all DNS records for a domain. You could first delete the domain’s zone record and then re-create it. I assume it’ll have the same affect as you’re trying to achieve, but you’ll also lose all of the Cloudflare configuration settings for the domain.

      Regards, Steve.

  • 4.3.1 List All DNS Records for a Zone

    Check and update the code for the alternative example. The output success tab is correct, but the code tab is wrong.
    Should be
    | python3.8 -c $’import sys,json\ndata=json.loads(sys.stdin.read())\n\
    if data[“success”]:\n\tfor dict in data[“result”]:\
    print(“ID: ” + dict[“id”] + “, Type: ” + dict[“type”] + “, ” + \
    “Name: ” + dict[“name”] + “, ” + “Content: ” + dict[“content”])\n\
    else:print(“ERROR(” + str(data[“errors”][0][“code”]) + “): ” + data[“errors”][0]
    [“message”])’
    Much nicer scripting examples here than the cloudflare API site.

    • Thanks for spotting that Bill and bringing it to my attention. Have made the necessary correction.

      Regards, Steve.

  • Thanks a lot Steven!
    I was thinking CloudFlare API is something difficult, but it was so easy to add a bunch of domains using API and a simple bash script.
    The power of automation!

  • Sir, plz tell me how to get zone id of a domain and then update dns records. My output is coming like this when i rquest zone id:

    % Total % Received % Xferd Average Speed Time Time Time Current
    Dload Upload Total Spent Left Speed
    100 1790 0 1790 0 0 3572 0 –:–:– –:–:– –:–:– 3572
    ZONE_ID: 403f83180ec13138ab591ceff04adccb

Steve

Recent Comments

Recent Posts