Python requests library returns 401 when calling Genesys /oauth/token with client credentials

I’ve been wrestling with this for a couple of days. Need to get an access token for a backend service using the Client Credentials flow. Not using the official Python SDK here, just raw requests.

The docs say to POST to /oauth/token with grant_type=client_credentials and pass the client_id and client_secret in the Authorization header using Basic Auth. Seems straightforward enough. But I keep hitting a 401 Unauthorized.

Here is the snippet I’m running:

import requests
import base64

client_id = "my-client-id"
client_secret = "my-client-secret"

# Encode credentials
creds = f"{client_id}:{client_secret}"
encoded = base64.b64encode(creds.encode()).decode()

headers = {
 "Authorization": f"Basic {encoded}",
 "Content-Type": "application/x-www-form-urlencoded"
}

payload = {
 "grant_type": "client_credentials"
}

response = requests.post(
 "https://api.mypurecloud.com/oauth/token",
 headers=headers,
 data=payload
)

print(response.status_code)
print(response.json())

The response body is just {"error": "invalid_client"}. I’ve double-checked the credentials in the Admin UI. They are correct. I can even use Postman with the exact same creds and it works fine. Postman sends the same Base64 string.

I tried adding scope to the payload too, but no change. Is there something subtle about how requests handles the header or the body encoding that I’m missing? Maybe the Content-Type? I’ve seen some old threads mention application/json but the spec says x-www-form-urlencoded.

Running this in a local venv on Mac. Python 3.9. Requests version 2.28.1.

Any ideas what I’m doing wrong here?