Python script gets "unsupported grant type" error when getting a token from a Server to Server app

API Endpoint(s) and/or Zoom API Event(s)
Link the API endpoint(s) and/orZoom API Event(s) you’re working with to help give context.
/oauth/token

Description
I created a Server to Server app to automate some reports using Python. Unfortunately, my script errors out at step 1, using account credentials to get an access token. Based on some other posts (links below), it seems like the trick is to get the content type right. Not having any luck so far, though. Any of you smart people out there figured out how to get the access token successfully?

Here’s what I’ve tried so far:

  • set headers={“Content-Type”: “application/x-www-form-urlencoded; charset=ascii”}
  • set headers={“Content-Type”: “application/x-www-form-urlencoded; charset=utf-8”}
  • set data = string instead of a dictionary
  • set params = {‘grant_type’: ‘account_credentials’}
  • sending grant_type and account_id as params instead of data

Error?
status_code = 400
reason = Bad Request
content = b’{“reason”:“unsupported grant type”,“error”:“unsupported_grant_type”}’

How To Reproduce
import requests
import os
client_auth = requests.auth.HTTPBasicAuth(os.getenv(‘zoom_client_id’), os.getenv(‘zoom_client_secret’))
v_token_raw = requests.post(
url=‘https://zoom.us/oauth/token’,
data={
‘grant_type’: ‘account_credentials’,
‘account_id’: os.getenv(‘zoom_account_id’)
},
auth=client_auth,
headers={“Content-Type”: “application/x-www-form-urlencoded”}
)

Similar issues that didn’t fix my problem

Not sure if this is helpful, but the Power shell example in the developer docs also returns status code 400 for me. Not knowledgeable enough about power shell to get the details of the error response, though.

request
Invoke-WebRequest -UseBasicParsing -Method POST -Uri https://zoom.us/oauth/token -ContentType ‘application/x-www-form-urlencoded’ -Body @{ grant_type=‘account_credentials’; account_id=removed } -Headers @{ Host=‘zoom.us’; Authorization=removed }

returns
Invoke-WebRequest : The remote server returned an error: (400) Bad Request.

The cURL example also returns “unsupported grant type”.

request
curl -X POST https://zoom.us/oauth/token -d ‘grant_type=account_credentials’ -d ‘account_id=removed’ -H ‘Host: zoom.us’ -H ‘Authorization: Basic removed’

returns
{“reason”:“unsupported grant type”,“error”:“unsupported_grant_type”}

When you tried formatting the data as a string, how did you generate that string? It’s supposed to be formatted as a query string, and I’m concerned that the Python Requests library is being unclear about how it’s interpreting that data parameter, which means it can be taking creative liberties.

I tried hard coding the grant_type and account_id in the url, but I’m getting the same error.

base64_auth_string = base64.b64encode(f"{os.getenv(‘zoom_client_id’)}:{os.getenv(‘zoom_client_secret’)}"
.encode(“ascii”)).decode(“ascii”)
v_token_raw = requests.post(
url=f"https://zoom.us/oauth/token/?grant_type=account_credentials&account_id=my account id removed",
headers={‘Authorization’: f’Basic {base64_auth_string}'}
)

Is that what you meant, @MultiplayerSession ? Since zoom wants a POST request, I thought the grant_type and account_id would be passed as application/x-www-form-urlencoded content instead of parameters.

I’m the biggest dummy. :person_facepalming: I clicked on the “OAuth” tile instead of scrolling down to find the " Server-to-Server OAuth" tile in the “Choose your app type” step. I created an actual Server to Server app and now my code gets an access token just fine.

working code
client_auth = requests.auth.HTTPBasicAuth(os.getenv(‘zoom_client_id’), os.getenv(‘zoom_client_secret’))
v_token_raw = requests.post(
url=‘https://zoom.us/oauth/token’,
data={
‘grant_type’: ‘account_credentials’,
‘account_id’: os.getenv(‘zoom_account_id’)
},
auth=client_auth
)

1 Like

Glad to hear it’s resolved. To cover your previous question, the query string still goes into the body of the request, not the URL. That’s why you’re declaring the Content-Type header (to disclose how to interpret the request body). This keeps your account ID out of various log files, since URLs tend to be recorded (but not bodies).