As has been mentioned in comments and answers, the 401 code signifies that your token is not valid. This could definitely happen with a once-valid token that has now expired (they have a limited lifetime).
When you received the response for your initial request to get an access token, see if that response contained a refresh token value as key refresh_token
. If so, presumably you have saved this away. If not, start the process of obtaining a new access token but this time save the refresh token. When your access token has expired the refresh token can be used to obtain a new access token. So before you call raise_for_status
, you should first test for "errors" that are possibly recoverable. Read the comments in the following code:
import requests
url = 'https://api.cc.email/v3/contacts'
# I don't believe you send the api_key here:
#url_completed = url + '?api_key=' + api_key
post_request = {
"email_address": {
"address": "abc123@gmail.com"
},
"first_name": "First",
"last_name": "Last",
"create_source": "Account",
"list_memberships": ["xxxxxxxxx"]
}
access_token = 'xxxxxxx'
headers = {
'Authorization': f'Bearer {access_token}',
# Correct header:
'Content-Type': 'application/json'
}
# data= changed to json=:
resp = requests.post(url, json=post_request, headers=headers)
if resp.status != 201
# contact was not successfully added
if resp.status_code == 401:
# possible expired token
access_token = get_new_access_token()
# Try again with the new access_token
headers['Authorization'] = f'Bearer {access_token}'
resp = requests.post(url, json=post_request, headers=headers)
# contact already exists?
if resp.status_code != '409':
# No, raise an exception
resp.raise_for_status()
# Here to update existing client.
# But this requires knowing the contact_id, which is not
# returned as a documented value. And you can't get it by fetching
# the contact for which you already need to know the contact_id.
# Who designed this API? However, the contact_id is
# buried in an error message (at least today it is -- who knows about
# tomorrow?).
import re
result = resp.json()
# Look for contact_id
for item in result:
m = re.search(r'\b[a-f0-9]+(?:-[a-f0-9]+)+\b', item['error_message'])
if m:
break
if not m:
raise Exception('Cannot locate contact_id')
contact_id = m[0]
# Now attempt to do an update:
resp = requests.put(f'{url}/{contact_id}', json=post_request, headers=headers)
resp.raise_for_status()
def get_new_access_token():
refresh_token = 'yyyyyyy'
client_id = 'zzzzzzz'
post_data = {'refresh_token': refresh_token, 'grant_type': 'refresh_token', 'client_id': client_id}
url = 'https://authz.constantcontact.com/oauth2/default/v1/token'
headers = {
'Accept': 'application/json',
'Content-Type': 'application/x-www-form-urlencoded'
}
resp = requests.post(url, data=post_data, headers=headers)
resp.raise_for_status()
result = resp.json()
access_token = result['access_token']
if 'refresh_token' in result:
refesh_token = result['refresh_token']
# Save new access_token and refresh_token somewhere:
...
return access_token