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
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).
UPDATE:
To fix your code, you need to check the response content, which is what the send_request()
method returns. Also note that send_request()
already does a lot of request/response error checking and raises exceptions if necessary
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"
# send_request() already checks for request/response errors and
# raises exceptions if they happen, so we don't need to check for
# them here. Unless we want to gracefully handle errors
response_dict = wm.send_request(method, url, request_headers=headers, params=data)
# Check if there is response and requestStatus value is not ERROR
if response_dict and response_dict.get('requestStatus') != 'ERROR':
request_id = response_dict.get('requestId')
print(f'Report request submitted. Request ID: {request_id}')
else:
print('Report request failed.')
print(response_dict)