Python: POST request to API failed - python

I am trying to send a POST request to Walmart API to request reports. But it is returning me 'Report request failed.' message with json. What am I doing wrong? I don't really have that much experience with API calls in python. Here is my code.
I have a class called "Walmart" to interact with the Walmart Marketplace API. It has methods to authenticate with the API, send requests and handle responses.
class Walmart(object):
def __init__(self, client_id, client_secret):
"""To get client_id and client_secret for your Walmart Marketplace
visit: https://developer.walmart.com/#/generateKey
"""
self.client_id = client_id
self.client_secret = client_secret
self.token = None
self.token_expires_in = None
self.base_url = "https://marketplace.walmartapis.com/v3"
session = requests.Session()
session.headers.update({
"WM_SVC.NAME": "Walmart Marketplace",
"WM_QOS.CORRELATION_ID": uuid.uuid4().hex,
"Content-Type": "application/x-www-form-urlencoded",
"Accept": "application/json",
})
session.auth = HTTPBasicAuth(self.client_id, self.client_secret)
self.session = session
# Get the token required for API requests
self.authenticate()
def authenticate(self):
data = self.send_request(
"POST", "{}/token".format(self.base_url),
body={
"grant_type": "client_credentials",
},
)
self.token = data["access_token"]
self.token_expires_in = data["expires_in"]
self.session.headers["WM_SEC.ACCESS_TOKEN"] = self.token
#property
def report(self):
return Report(connection=self)
def send_request(
self, method, url, params=None, body=None, json=None,
request_headers=None
):
# A unique ID which identifies each API call and used to track
# and debug issues; use a random generated GUID for this ID
headers = {
"WM_QOS.CORRELATION_ID": uuid.uuid4().hex,
"WM_SVC.NAME": "Walmart Marketplace",
"Content-Type": "application/x-www-form-urlencoded",
"Accept": "application/json",
}
if request_headers:
headers.update(request_headers)
response = None
if method == "GET":
response = self.session.get(url, params=params, headers=headers)
elif method == "PUT":
response = self.session.put(
url, params=params, headers=headers, data=body
)
elif method == "POST":
request_params = {
"params": params,
"headers": headers,
}
if json is not None:
request_params["json"] = json
else:
request_params["data"] = body
response = self.session.post(url, **request_params)
if response is not None:
try:
response.raise_for_status()
except requests.exceptions.HTTPError:
if response.status_code == 401:
raise WalmartAuthenticationError((
"Invalid client_id or client_secret. Please verify "
"your credentials from https://developer.walmart."
"com/#/generateKey"
))
elif response.status_code == 400:
data = response.json()
if "error" in data and data["error"][0]["code"] == \
"INVALID_TOKEN.GMP_GATEWAY_API":
# Refresh the token as the current token has expired
self.authenticate()
return self.send_request(
method, url, params, body, request_headers
)
raise
try:
return response.json()
except ValueError:
# In case of reports, there is no JSON response, so return the
# content instead which contains the actual report
return response.content
And here goes authentication and the request itself. I think I am doing it wrong with the send_request method, should I do it in a different way?
api_key = '<key>'
api_secret='<secret>'
wm = Walmart(api_key, api_secret)
wm.authenticate()
url = "https://marketplace.walmartapis.com/v3/reports/reportRequests"
headers = {
"WM_QOS.CORRELATION_ID": uuid.uuid4().hex,
"WM_SVC.NAME": "Walmart Marketplace",
"Content-Type": "application/x-www-form-urlencoded",
"Accept": "application/json",
}
data= {
"reportType": "ITEM_PERFORMANCE",
"reportVersion": "v1",
}
method="POST"
response_dict = wm.send_request(method, url, request_headers=headers, params=data)
if 'status_code' in response_dict and response_dict['status_code'] == 200:
response_json = response_dict.get('json')
request_id = response_json.get('requestId')
print(f'Report request submitted. Request ID: {request_id}')
else:
print('Report request failed.')
if 'json' in response_dict:
print(response_dict['json'])
else:
print(response_dict)
The response that I got was in the following form.
Report request failed.
{'requestId': '46a864e8-80e8-4019-86f0-d7a1575349a4', 'requestStatus': 'RECEIVED', 'requestSubmissionDate': '2023-02-15T18:55:03Z', 'reportType': 'ITEM_PERFORMANCE', 'reportVersion': 'v1'}
Any help is appreciated

You can try to print the response to see his contents, and there may be some additional info that will help you to fix your code. Also, you can try to debug your code line by line using this https://pypi.org/project/ipdb/

The response you get seems to be a successful response, as the property requestStatus is RECEIVED, and not ERROR (which is a possible value according to the API docs you linked).
So, the issue is probably with your response checks.
Based on your checks for response:
if 'status_code' in response_dict and response_dict['status_code'] == 200:
response_json = response_dict.get('json')
request_id = response_json.get('requestId')
print(f'Report request submitted. Request ID: {request_id}')
else:
print('Report request failed.')
if 'json' in response_dict:
print(response_dict['json'])
else:
print(response_dict)
either 'status_code' in response_dict or response_dict['status_code'] == 200 is false, what makes the else block to be executed. I recommend you to print(response_dict) before the if-else block, to see the whole content and see which of those 2 conditions is false and handle it accordingly.
I think that the issue is that the object you get from wm.send_request() does not contain status_code, since you get the content of the response (when you return response.json()) and not the session.Response object from the requests lib (see the docs).

Related

Microsoft Graph API move junk email into Inbox not working (giving 400 response)

For ref I am sharing code here. For the documentaiton I am following this link https://learn.microsoft.com/en-us/graph/api/message-move?view=graph-rest-1.0&tabs=http
but it is giving 400 error response
def move_junk_to_inbox(message_id: str):
status, access_token = get_token()
headers = {
"Content-Type": "application/json",
"Authorization": access_token
# "Prefer": 'outlook.body-content-type="text"',
}
folder_name = "Inbox"
base_url = "https://graph.microsoft.com/v1.0/"
end_point = f"users/{email_id}/messages/{message_id}/move"
move_url = base_url + end_point
body = {"destinationId": folder_name}
response = requests.post(move_url, headers=headers, data=body, timeout=TIMEOUT)
return response
I was able to solve it by using move event using this doc
https://learn.microsoft.com/en-us/graph/api/message-move?view=graph-rest-1.0&tabs=http
def move_email(self, email_id: str, message_id: str, target_folder: str):
try:
access_token, status = self.get_token()
if not status:
raise "Failed to generate auth token"
headers = {
"Content-Type": "application/json",
"Authorization": access_token,
"Prefer": 'outlook.body-content-type="text"',
}
body = {"destinationId": target_folder}
end_point = f"{self.base_url}/users/{email_id}/messages/{message_id}/move"
res = requests.post(end_point, headers=headers, json=body)
return res.json()
except Exception as e:
err = f"Error move mail: {str(e)}"
return None

Pinterest API 'bookmark' always returning the same string and data

I am trying to use pagination the way it is instructed in the Pinterest API Documentation, by passing 'bookmark' as a parameter to the next GET request in order to get the next batch of data.
However, the data returned is the EXACT same as the initial data I had received (without passing 'bookmark') and the value of 'bookmark' is also the same!
With this issue present, I keep receiving the same data over and over and can't get the entirety of the data. In my case I'm trying to list all campaigns.
Here is my python code:
url = f'https://api.pinterest.com/v5/ad_accounts/{ad_account_id}/campaigns'
payload = f"page_size=25"
headers = {
"Accept": "text/plain",
"Content-Type": "application/x-www-form-urlencoded",
"Authorization": f"Bearer {access_token}"
}
response = requests.request("GET", url, data=payload, headers=headers)
print(response)
feed = response.json()
print(feed)
bookmark=''
if 'bookmark' in feed:
bookmark = feed['bookmark']
print(bookmark)
while(bookmark != '' and bookmark != None and bookmark != 'null'):
url = f'https://api.pinterest.com/v5/ad_accounts/{ad_account_id}/{level}s'
payload = f"page_size=25&bookmark={bookmark}"
headers = {
"Accept": "text/plain",
"Content-Type": "application/x-www-form-urlencoded",
"Authorization": f"Bearer {access_token}"
}
response = requests.request("GET", url, data=payload, headers=headers)
print(response)
feed = response.json()
print(feed)
bookmark = feed['bookmark']
print(bookmark)
I think your condition in while is wrong, therefore you end up in the same loop. I'm currently also working with Pinterest API and below is my modified implementation how to get a list of ad accounts.
Basically you're testing if the bookmark is None. If yes, then you can return the result, otherwise you append the bookmark into query parameters and call the endpoint again.
from app.api.http_client import HttpClient
class PinterestAccountsClient():
def get_accounts(self, credentials) -> list[dict]:
headers = {
'Authorization': f"Bearer {credentials('access_token')}"
}
params = {
'page_size': 25
}
accounts = []
found_last_page = False
while not found_last_page:
try:
response = HttpClient.get(self.listing_url, headers=headers, params=params)
items = response.get('items', [])
bookmark = response.get('bookmark')
if bookmark is None:
found_last_page = True
else:
params['bookmark'] = bookmark
accounts.extend([{
'id': account['id'],
'name': account['name']
} for account in items])
return accounts

Unable to filter, paginate, or add any data to Notion API query from python code

I've been trying to query the Notion API using Python, and as I have more than 100 results, I need to either filter them down or be able to paginate. I tried different variants of the following code to no avail:
headersNotion = {
"Authorization": "Bearer " + notionToken,
"Content-Type": "application/json",
"Notion-Version": "2021-05-13"
}
data = {
"start_cursor" : nextCursor
}
readUrl = f"https://api.notion.com/v1/databases/{databaseId}/query"
res = requests.request("POST", readUrl, data=data, headers=headers)
I've also tried with data = { "filter": {"cover" : "is_empty"} } or even with an idiotic or empty filter, but as soon as I add any sort of data to the request, I get a 400 error:
{"object": "error", "status": 400, "code": "invalid_json", "message": "Error parsing JSON body."}
Would anyone have any idea what I might be doing wrong?
look here headersNotion schould be headers.
https://developers.notion.com/reference/post-database-query
Just as example.
payload = {
'filter': {
'and': [
{
'property': 'aktualisiert am',
'date': {
'after': str(age)
}
},
{
'property': 'aktiv',
'text': {
'equals': 'yes'
}
}
]
}
}
headers = {
"Accept": "application/json",
"Authorization": "Bearer " + token,
"Content-Type": "application/json",
"Notion-Version": "2021-08-16"
}
def readDatabase(databaseId, headers, payload):
readUrl = f"https://api.notion.com/v1/databases/{databaseId}/query"
res = requests.request("POST", readUrl, json=payload, headers=headers)
datadb = res.json()
print(res.text) #for Debug
# Errors Returns a 404 HTTP response if the database doesn't exist, or if the integration doesn't have access to the database.
# Returns a 400 or a 429 HTTP response if the request exceeds the request limits.
# Returns 200 HTTP responce if OK
#print ('')
print ('')
if res.status_code == 200:
print('200 Success!') # Yes everything OK!
elif res.status_code == 404:
print('404 Not Found.')
#sendmail
subject = " Fehler!! 404 Database Not Found"
send_email(user, password, recipient, subject, body)
return
elif res.status_code == 429:
print(' Fehler !! 429 request exceeds the request limits.')
#sendmail
subject = " Fehler !! 429 request exceeds the request limits."
send_email(user, password, recipient, subject, body)
return
elif res.status_code == 400:
print('400 request exceeds the request limits.')
#sendmail
subject = " Fehler !! 400 request exceeds the request limits. "
send_email(user, password, recipient, subject, body)
return
# print(res.text)
# write csv File
with open('./dbmain.json', 'w', encoding='utf8') as f:
json.dump(datadb, f, ensure_ascii=False)

Spotify API refresh token

I am trying to use Spotify's API to get song details. My programming language is python and the code I am trying to execute is this:
def spotifysearch(searchterm):
headers = {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': 'Bearer OAUTH TOKEN',
}
params = (
('q', searchterm),
('type', 'track'),
('limit', '10'),
)
response = requests.get('https://api.spotify.com/v1/search', headers=headers, params=params)
a=response.json()
return a
However, my OAUTH TOKEN expires after every hour and I can't just manually get a new token every hour. I couldn't understand how to get my refresh token automatically.
Please provide me answer in either python or on curl (Both are fine with me)
Your first step will be to record:
when you got the token
when the token will expire
You can use dataclasses for this. Dataclasses have a post_init func, which is executed
after initialization. You should compute the expiry as a datetime there.
import dataclasses
#dataclasses.dataclass
class TToken:
access_token: str
refresh_token: str
created: datetime.datetime
expires: datetime.datetime
def __init_post__(self, data):
if data: # Not required
self.access_token = data['access_token']
self.created = datetime.datetime.utcnow()
self.expires = self.created + datetime.timedelta(minutes=59)
self.refresh_token = data['refresh_token']
def is_expired(self):
return datetime.datetime.utcnow() > self.expires
def as_header(self):
return {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': f'Bearer self.access_token',
}
For your tokens.
BASE64_CID_CSEC = <base64 encoded clientid:clientsecret>
def token_get(authorization_code: str):
data = {'form':{'grant_type': 'authorization_code',
'code': authorization_code,
'redirect_uri': <your redirect uri>},
'header':{'Authorization': f'Basic {BASE64_CID_CSEC}'}}
resp = requests.get('https://accounts.spotify.com/api/token', data=data['form'], headers=data['header'])
return TToken(resp)
def token_refresh(refresh_token: str):
data = {'form': {'grant_type': 'refresh_token', 'refresh_token': refresh_token},
'header': {'Authorization': f'Basic {BASE64_CID_CSEC}'}}
resp = requests.get('https://accounts.spotify.com/api/token', data=data['form'], headers=data['header'])
return TToken(resp)
Finally
token = token_get(authorization_code)
def spotify_search(searchterm: str):
if token.is_expired():
token = token_refresh(token.refresh_token)
params = (('q', searchterm),('type', 'track'),('limit', '10'))
resp = requests.get('https://api.spotify.com/v1/search', headers=token.as_header(), params=params)
return resp.json()

PayPal API - Transaction Search API: Response 400 INVALID REQUEST

I am trying to get a list of all transactions from my PayPal Business account by using the Transaction Search API, but I keep getting the 400 INVALID_REQUEST response.
According to this documentation, I am doing everything correctly with the headers.
I also gave access to my app to do this kind of search. Could anyone help me?
import requests, json
USERNAME = <MY USERNAME>
KEY = <MY SECRET KEY>
TOKEN = <MY TOKEN - GENERATED BY POSTMAN>
headers = {"Content-Type": "application/json",
"Accept-Language": "en_US",
"Authorization": "Bearer <MY TOKEN>",
"Accept": "application/json"
}
LINK = "https://api-m.paypal.com"
GET = "/v1/reporting/transactions?start_date=2021-01-01T00:00:00-0700&end_date=2021-06-01T00:00:00-0700"
GET_LINK = LINK + GET
response = requests.get(GET_LINK, auth=(USERNAME, KEY), headers=headers)
print(response)
Thanks
I recreated all code, generated TOKEN, ran request and got error 400
... but in response.text I got explanation:
"issue":"Date range is greater than 31 days"
If I change dates to
'start_date': '2021-01-01T00:00:00-0700',
'end_date': '2021-02-01T00:00:00-0700',
then it works for me.
BTW: You need (USERNAME, KEY) only to generate TOKEN and later you can use only TOKEN.
My full code used for tests:
It needs only CLIENT_ID and SECRET because it runs code to get TOKEN
import requests
# --- constants ---
CLIENT_ID = "ARg..." # 80 chars
SECRET = "EAl..." # 80 chars
#ENDPOINT = "https://api-m.sandbox.paypal.com" # Sandbox - doesn't have access to transactions
ENDPOINT = "https://api-m.paypal.com" # Live
DEBUG = True
# --- functions ---
def display_response(response):
print('response:', response)
print('url:', response.url)
print('text:', response.text)
def display_data(data):
for key, value in data.items():
if key == 'scope':
for item in value.split(' '):
print(key, '=', item)
else:
print(key, '=', value)
def get_token():
if DEBUG:
print('--- get token ---')
url = ENDPOINT + '/v1/oauth2/token'
headers = {
"Accept": "application/json",
"Accept-Language": "en_US",
}
payload = {
"grant_type": "client_credentials"
}
response = requests.post(url, auth=(CLIENT_ID, SECRET), data=payload)
if DEBUG:
display_response(response)
data = response.json()
if DEBUG:
display_data(data)
return data['access_token']
def get_transactions():
if DEBUG:
print('--- transaction ---')
url = ENDPOINT + "/v1/reporting/transactions"
headers = {
"Content-Type": "application/json",
"Accept-Language": "en_US",
"Authorization": f"Bearer {TOKEN}",
"Accept": "application/json"
}
payload = {
'start_date': '2021-01-01T00:00:00-0700',
'end_date': '2021-02-01T00:00:00-0700',
}
response = requests.get(url, headers=headers, params=payload)
if DEBUG:
display_response(response)
data = response.json()
if DEBUG:
display_data(data)
# --- main ---
TOKEN = get_token()
print('--- token ---')
print('TOKEN:', TOKEN)
get_transactions()

Categories