Api always returns Failed to parse request body, content-type must be application/json

I am writing a python program to update my DNS records via the api. However when every I make a request to update the record I get Failed to parse request body, content-type must be application/json.
my code:

import requests
import json
from sys import argv, exit

from requests import api

if len(argv) != 5:
    print("not enough args")

dynamic_ip =  requests.get("http://ip.42.pl/raw").text
domain = argv[1]
api_key = argv[2]
zone_id = argv[3]
record_id = argv[4]
headers = {
    'Authorization': f'Bearer {api_key}',
    'Content-Type': 'application/json'

data = {"type":"A", "name":f"{domain}", "content":f"{dynamic_ip}", "ttl":"auto", "proxied":True}
data = json.dumps(data)

response = requests.put(f'https://api.cloudflare.com/client/v4/zones/{zone_id}/dns_records/{record_id}', headers=headers, json=data)

is there some thing I am doing wrong?

I don’t see an email address field anywhere. It should be before the API Key. Granted, I’d be surprised if header order mattered…but one can always hope the error message is slightly mistaken.

If i do use X-Auth-Key and X-Auth-Email instead of Authorization: Bearer . I get a auth error.
https://api.cloudflare.com/#getting-started-requests in the getting started page Authorization: Bearer is a valid way of authenticating the request and in my case the only way that works.

I’m stumped, as I don’t use Python. I always use ‘curl’. Have you seen the actual output of your script?

yes. The error is the same even if i use curl. No idea why.

Can you post the curl command it’s producing? but swap out the sensitive info with stuff like EXAMPLE.COM,, TOKEN, EMAIL, etc. I’d like to give it a try.

curl -X PUT "https://api.cloudflare.com/client/v4/zones/${zone_id}/dns_records/${record_id}" \
         -H "Authorization: Bearer ${api_key}" \
         -H "Content-Type: application/json" \
         --data '{"type":"A", "name":"'${domain}'", "content":"${dynamic_ip}", "proxied":true}'

that`s the curl command I used.

I just ran it, and it worked…though your example was missing the ’ at the end.

curl -X PUT "https://api.cloudflare.com/client/v4/zones/MY_ZONE_ID/dns_records/EXISTING_RECORD_ID" \
	 -H "Authorization: Bearer BEARER_TOKEN_FOR_ZONE_DNS_EDIT" \
         -H "Content-Type: application/json" \
         --data '{"type":"A", "name":"'MY_FQDN", "content":"MY_IP_ADDRESS", "proxied":true}'

I missed the ’ during copy and paste. I think I may have missing the FQDN. Could you help me get it.

Ah, if the hostname in your DNS is ‘storage’, then FQDN would be storage.example.com. Or maybe that record is just The root of example.com. You didn’t specify, so I have to guess.

the record i am trying to update is nullrequest.com

Then I don’t know what the issue is. You said the ‘curl’ version of your request didn’t work, but mine did.

Or maybe the record_id is incorrect. There isn’t anything else I can suggest. You may have to contact Support to have them look at your syntax.

To contact Cloudflare Customer Support, login & go to https://dash.cloudflare.com/?account=support and select get more help. If you receive an automatic response that does not help you, please reply and indicate you need more help.

I figured it out ttl had to be set to 1

Oh…that wasn’t in the curl command you posted.