Why am I getting a 429 error with the Reddit API? - python-3.x

I have been experimenting with flask and the Reddit api, but no matter what I try, I seem to be hitting the 429 'too many requests' error every time I try to receive the access token.
The initial user auth works without any issue, and I receive the code back and can validate that I've auth'd the app in my Reddit settings. I've also made sure to use a unique user-agent, as this seemed to resolve the issue for most people, and am not using any words like 'bot', 'curl', 'web' or anything else which would be a likely blocker.
As far as I'm aware, I am also well under the tolerance of getting rate limited with too many requests.
Given that this is the first time I'm using both flask, and the Reddit API, I'm sure I'm missing something obvious,but after 4 hours, copious Googling and reading all the docs, I don't understand what I'm getting wrong here.
import requests
import requests.auth
from flask import Flask, redirect, request, url_for
import string
import random
app = Flask(__name__)
client_id = "client id of my web app"
client_secret = "client secret of my web app"
base_auth_url = 'https://www.reddit.com/api/v1'
authorization_endpoint = '/authorize'
access_token_endpoint = '/access_token'
#app.route("/login")
def get_the_auth_code():
state = state_generator()
params = {
'client_id': client_id,
'response_type': 'code',
'state': state,
'redirect_uri': 'http://localhost:5000/redditor',
'scope': 'identity',
'user-agent': 'myapp v0.1 by /u/myredditusername'
}
return redirect(url_builder(authorization_endpoint, params))
#app.route("/redditor")
def get_the_access_token():
code = request.args.get('code')
client_auth = requests.auth.HTTPBasicAuth(client_id, client_secret)
post_data = {
'grant_type': 'authorization_code',
'code': code,
'redirect_uri': 'http://localhost:5000/redditor',
'user-agent': 'myapp v0.1 by /u/myredditusername'
}
response = requests.post(base_auth_url + access_token_endpoint, auth=client_auth, data=post_data)
token_json = response.json()
return token_json
def state_generator(size=25, chars=string.ascii_uppercase + string.digits):
return ''.join(random.choice(chars) for _ in range(size))
def url_builder(endpoint, parameters):
params = '&'.join(['%s=%s' % (k, v) for k, v in parameters.items()])
url = '%s%s?%s' % (base_auth_url, endpoint, params)
return url
if __name__ == "__main__":
app.run(debug=True)

With help from the Reddit community (specifically /u/nmtake), I found out where I was going wrong.
In my get_the_access_token() function, I was adding the user-agent field to my data parameters, instead of declaring it as part of the header.
Instead of:
post_data = {
'grant_type': 'authorization_code',
'code': code,
'redirect_uri': 'http://localhost:5000/redditor',
'user-agent': 'myapp v0.1 by /u/myredditusername'
}
response = requests.post(base_auth_url + access_token_endpoint, auth=client_auth, data=post_data)
I am now using the following, which works perfectly:
post_data = {
'grant_type': 'authorization_code',
'code': code,
'redirect_uri': 'http://localhost:5000/redditor',
}
post_headers = {
'user-agent': 'myapp v0.1 by /u/myredditusername'
}
response = requests.post(base_auth_url + access_token_endpoint, auth=client_auth, data=post_data, headers=post_headers)

Related

Yahoo API - Unable to request new access token once previous access token has expired

I am attempting to use Yahoo's API for fantasy football. I am able to receive an access token and refresh token initially, but once that access token has expired, I am unable to get another one.
My code is as follows:
from requests import Request, get, post
import webbrowser
import base64
baseURL = 'https://api.login.yahoo.com/'
oauthENDPOINT = "https://api.login.yahoo.com/oauth2/request_auth"
## Generate a url using the endpoint and parameters above
params = {'client_id' : client_id,
'redirect_uri' : "oob",
'response_type' : 'code'}
p = Request('GET', oauthENDPOINT, params=params).prepare()
webbrowser.open(p.url)
The last line sends me to the Yahoo website where I allow myself access and receive authCode.
encoded = base64.b64encode((client_id + ':' + client_secret).encode("utf-8"))
headers = {
'Authorization': f'Basic {encoded.decode("utf-8")}',
'Content-Type': 'application/x-www-form-urlencoded'
}
data = {
'grant_type': 'authorization_code',
'redirect_uri': 'oob',
'code': authCode}
tokenResponse = post(baseURL + 'oauth2/get_token', headers=headers, data=data)
tokenResponseJSON = tokenResponse.json()
access_token = tokenResponseJSON['access_token']
refresh_token = tokenResponseJSON['refresh_token']
I now have all the information necessary to examine the settings of my league (for example).
fbURL = 'https://fantasysports.yahooapis.com/fantasy/v2'
leagueURL1 = f'{fbURL}/leagues;league_keys=nfl.l.{leagueID}/settings'
headers = {
'Authorization': f'Bearer {access_token}',
'Accept': 'application/json',
'Content-Type': 'application/json'
}
response2 = get(leagueURL1, headers=headers,params={'format': 'json'})
The above works as expected. However, the access_token lasts for 3600 seconds and once that time has expired I am unable to request a new one, using my refresh_token. My attempt:
accessTokenData = {
'grant_type': 'refresh_token',
'redirect_uri': 'oob',
'code': authCode,
'refresh_token': refresh_token
}
accessTokenResponse = post(baseURL + 'oauth2/get_token', headers=headers, data=accessTokenData)
accessTokenJSON = accessTokenResponse.json()
In the above, I am hoping to receive a new access_token, but instead accessTokenJSON is this:
{'error': {'localizedMessage': 'client request is not acceptable or not supported',
'errorId': 'INVALID_INPUT',
'message': 'client request is not acceptable or not supported'}}
Up to this point I have been following these steps, which worked well up to this point. What am I doing wrong? I understand many Python users use yahoo_oauth or rauth for authentication, but that involves saving the client_id and client_secret in a .json file separately and I'm looking to load those in dynamically. I don't think I'm very far away from succeeding, but I'm just missing something when it comes to generating a new refresh_token. All help much appreciated!
Thanks to referring back to our guide.
Managed to reproduce your error and it's really simple to solve.
You are redefining the headers variable in your request to the fantasyspot url.
The headers variable should be the same in the call for requesting a new access_token using the refresh_token as it was when initially getting both tokens using the auth_code.
So just define header before making requesting a new access_token. Should look like the the following:
headers = {
'Authorization': f'Basic {encoded.decode("utf-8")}',
'Content-Type': 'application/x-www-form-urlencoded'
}
response = post(base_url + 'oauth2/get_token', headers=headers, data=data)
Should work now.
Recommend using different variable names for the headers used for getting an access_token and the one used to the fantasy sport url.

How to convert Postman OAuth 2.0 to Python

I have been getting the issue when I'm trying to convert Postman OAuth 2.0 to Python3. I tried to research but seems unfortunate for me, I did not find any example
Here is my code:
from rauth import OAuth2Service
import json
def get_token(client_id, client_secret):
access_token = None
service = OAuth2Service(
name="Viafoura",
client_id=client_id,
client_secret=client_secret,
access_token_url='https://auth.viafoura.io/authorize_client'
)
data = {
'scope': 'xxxxx-xxxx-xxxx-xxxx-xxxxx',
'grant_type': 'client_credentials'
}
session = service.get_auth_session(data=data)
access_token = session
Here is OAuth 2.0 on Postman and it's working:
I want to get the access_token via Python3. Could anyone please help me on it?
Maybe it can help you, an example with the basic algorithm of OAuth2
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from requests import post, auth, exceptions
from json import loads
if __name__ == '__main__':
client_id = ''
client_secret = ''
user = ''
password = ''
access_point = 'https://account.lab.fiware.org/oauth2/token'
grant_type = 'password'
headers = {'Content-Type': 'application/x-www-form-urlencoded'}
auth = auth.HTTPBasicAuth(client_id, client_secret)
data = {'grant_type': grant_type,
'username': user,
'password': password}
resp = None
try:
resp = post(access_point, auth=auth, data=data, headers=headers, timeout=5)
except exceptions.ConnectionError:
exit(1)
if resp.status_code == 200:
resp = loads(resp.text)
if 'access_token' in resp:
print(resp['access_token'])
exit(0)
exit(1)
You need to fix access point, grant type. This source code can be found here
Sorry, I can't help directly with Viafoura and OAuth2Service library.

python requests getting unauthenticated or error 401

I'm trying to request post to my web server a notification but it shows error 401.
I already tested my API key in postman and it works but when I used it in python it shows error 401 or error:unathenticated.
Here's my code
import requests
req = requests.post('https://sampleweb.com/api/v1/devices/1/notifications',
json={ 'Authorization': 'Bearer eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9',
"notification": { "message":"hey", "level": 4}})
print(req.json())
file = open("alert_content.txt", "a")
file.write(req.text + "\n")
file.close()
I've searched and read some documentations regarding to my question and I already solved it. Here's the code.
import requests
url = "https://sampleweb.com/api/v1/devices/1/notifications"
auth = {'Authorization': 'Bearer eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ',
'content-type':'application/json'}
params = {"notification":{"message":message,"level":level}}
req = requests.post(url, headers= auth, json = params)
print(req.json())
file = open("alert_content.txt", "a")
file.write(req.text + "\n")
file.close()
The authorization needs the content-type and the params or parameters needed to be in json format. Now it works.

Google Actions Push Notification with Python?

I'm trying to figure out how to initiate a push notification using DialogFlow/Google Actions. I've cobbled together some test code, but when I test it, I get a '403 Forbidden' response. Can somebody provide me a good example of how to do this using Python 3.
`
import urllib.request
import json
notification = {
'userNotification': {
'title': 'Test Notification',
},
'target': {
'userId': 'user-id here',
'intent': 'Notification Intent',
'locale': 'en-US',
}
}
my_url = 'https://actions.googleapis.com/v2/conversations:send'
access_token = 'access token here'
request = urllib.request.Request(my_url)
request.add_header('Content-Type', 'application/json; charset=utf-8')
payload = { 'auth': { 'bearer': access_token },
'json': 'true',
'body': { 'customPushMessage': notification, 'isInSandbox':
'true' } };
jsondata = json.dumps(payload)
jsondataasbytes = jsondata.encode('utf-8')
response = urllib.request.urlopen(request, jsondataasbytes)
`
Can anybody provide any suggestions about how to get this to work?
=============================================================
Update: I revised the auth header as suggested and now I'm getting '401:Unauthorized'. I'm not sure if I'm creating the access token properly. Here's how I'm doing it:
I created an RSA256 private key on the Google Console website. I use that key to encode a JWT containing these fields:
{
"iss": [
"My Service Name",
"\"my_service-account#service_name.iam.gserviceaccount.com\""
],
"iat": 1544018697,
"exp": 1544019898,
"aud":
"https://www.googleapis.com/auth/actions.fulfillment.conversation\"",
"sub": [
"Push Notification",
"\"my-service-account#service_name.iam.gserviceaccount.com\""
]
}
I don't know if this is correct: the documentation for all of this is hard to pull together.
UPDATE 2:
I modified the code suggested by Prisoner, and I'm now able to get what appears to be a valid access_token. This is the modified code:
from oauth2client.client import GoogleCredentials
service_account_file = 'path-to-service_account_file'
credentials = GoogleCredentials.from_stream(SERVICE_ACCOUNT_FILE)
access_token = credentials.get_access_token().access_token
When I try to use the access token, I'm still getting a '401 Unauthorized' response. Has anybody actually done this? If so, can you give me some specifics on the correct URL to use and how to format the urllib.request message?
You've placed some things into the body of the request that belong in the header. In particular, the "403 forbidden" suggests that the Authorization header is either wrong or missing, and in your case, it looks like it is missing since you're trying to put it in the body.
The body of what you're sending should just contain the JSON with the customPushMessage attribute.
I'm not very familiar with python, but I think something like this is more what you want:
request = urllib.request.Request(my_url)
request.add_header('Authorization', 'bearer '+access_token)
request.add_header('Content-Type', 'application/json; charset=utf-8')
payload = { 'customPushMessage': notification }
jsondata = json.dumps(payload)
jsondataasbytes = jsondata.encode('utf-8')
response = urllib.request.urlopen(request, jsondataasbytes)
If you continue to get the "403 Forbidden" message - make sure your access_token is current and is actually an access token. Access tokens are created from the service account key, but are not the key itself. They have a limited lifetime (usually 1 hour), while the key is long-lasting.
Update about generating an access token from the keys.
You can't just create and sign a JWT to use as an access token.
The easiest way is to use the Google APIs Client Library for Python, which includes a library to handle OAuth with service accounts.
I haven't tested, but you should be able to do something like this, setting SERVICE_ACCOUNT_FILE to the location of where the keys are stored.
from google.oauth2 import service_account
SCOPES = ['https://www.googleapis.com/auth/actions.fulfillment.conversation']
SERVICE_ACCOUNT_FILE = '/path/to/service.json'
credentials = service_account.Credentials.from_service_account_file(
SERVICE_ACCOUNT_FILE, scopes=SCOPES)
access_token = credentials.get_access_token()
Ok, I've figured out how to get the the access_token, so that part of the problem is solved. I've run into another problem getting updates permission working for my test account, but I'm going to post a new question to cover that. I adapted code from another answer and this seems to work:
# Adapted from https://stackoverflow.com/questions/51821919/how-to-send-push-notification-from-google-assistant-through-dialogflow-fulfilmen/51961677#51961677
import io
import json
import requests
from google.oauth2 import service_account
import google.auth.transport.requests
def send_notification(path_to_service_account="default", intent="default"):
if path_to_service_account == "default":
PATH_TO_SERVICE_ACCOUNT = 'path to downloaded service account json file from Google Cloud Console'
else:
PATH_TO_SERVICE_ACCOUNT = path_to_service_account
if intent == "default":
INTENT = 'Notification Intent'
else:
INTENT = intent
REQUIRED_SCOPE = 'https://www.googleapis.com/auth/actions.fulfillment.conversation'
USER_ID = 'user id here'
INTENT = 'Your intent name'
# Get access token
with io.open(PATH_TO_SERVICE_ACCOUNT, 'r', encoding='utf-8') as json_fi:
credentials_info = json.load(json_fi)
credentials = service_account.Credentials.from_service_account_info(
credentials_info, scopes=[REQUIRED_SCOPE])
request = google.auth.transport.requests.Request()
credentials.refresh(request)
headers = {
'Authorization': 'Bearer ' + credentials.token
}
payload = {
'customPushMessage': {
'userNotification': {
'title': 'App Title',
'text': 'Simple Text'
},
'target': {
'userId': USER_ID,
'intent': INTENT,
# Expects a IETF BCP-47 language code (i.e. en-US)
'locale': 'en-US'
}
}
}
r = requests.request("POST", 'https://actions.googleapis.com/v2/conversations:send', data=json.dumps(payload), headers=headers)
print(str(r.status_code) + ': ' + r.text)

Get Survey Response Survey Monkey (Python)

Im quite new to http request. Im having abit of troubleshooting trying to get survey results/responses from survey monkey api 3.
Here is the following code i have:
import requests
import json
client = requests.session()
headers = {
"Authorization": "bearer %s" % "VVZEO3u35o3JVDdd8z5Qhl-eRR5Er2igaV1gf8GS4dvRfYVk3SWu9nHginwyNnU.tAHEr-AtikR9Zpg7vL3-jIg3-6yuQkPBvVIw0AkpYN5807SCLIrGojsii3ihdGV-",
"Content-Type": "application/json"
}
data = {}
HOST = "https://api.surveymonkey.net"
#SURVEY_LIST_ENDPOINT = "/v3/surveys/%s/responses/%s/details" %("85160626","161")
SURVEY_LIST_ENDPOINT = "/v3/surveys/85160626/responses"
uri = "%s%s" % (HOST, SURVEY_LIST_ENDPOINT)
response = client.post(uri, headers=headers, data=json.dumps(data))
response_json = response.json()
#survey_list = response_json["data"]["surveys"]
print(response_json)
I keep getting error:
{'error': {'docs': 'https://developer.surveymonkey.com/api/v3/#error-codes', 'message': 'There was an error retrieving the requested resource.', 'id': '1020', 'name': 'Resource Not Found', 'http_status_code': 404}}
Any help is much appreciated, thanks,
Pon
If you're trying to fetch data, then you should be doing a GET request, not a post.
response = client.get(uri, headers=headers)
Otherwise it looks fine, just make sure the Survey ID is correct.

Resources