HTTPError: Bad Request for Python urllib - python

This is my first project using API/python. Basically, I want to get information from trackhive.com to see where is the package. But first, to understand how it works, I'm just trying to create a track.
On the website, they give me this example:
from urllib2 import Request, urlopen
values = """
{
"tracking_number": "9361289676090919095393",
"slug": "usps"
}
"""
headers = {
'Content-Type': 'application/json',
'Authorization': 'Bearer Token'
}
request = Request('https://private-anon-6175dd5596-trackhive.apiary-proxy.com/trackings
', data=values, headers=headers)
response_body = urlopen(request).read()
As I'm using Python 3, my code is
import json
import urllib.request
values = """
{
"tracking_number": "9361289676090919095393",
"slug": "usps"
}
"""
headers = {
'Content-Type': 'application/json',
'Authorization': 'Bearer mytokenAPI'
}
url = 'https://private-anon-6175dd5596-trackhive.apiary-proxy.com/trackings'
request = urllib.request.Request(url,data=values, headers=headers)
response_body = urllib.request.urlopen(request, data=bytes(json.dumps(headers), encoding="utf-8")).read()
But when it calls the "urllib.request.urlopen", it returns "HTTPError: Bad Request". What am I doing wrong?
On the website, they said that the endpoint is located at: https://api.trackinghive.com. In their code, they are accessing: 'https://private-anon-6175dd5596-trackhive.apiary-proxy.com/trackings'. If I haven't access to their example, how could I know that I suppose to access this URL, and not something like "https://api.trackinghive.com/trackings"? Isn't it suppose to use the endpoint somewhere?
(I'm sorry about all these questions, I'm a little confused about how these codes with API and URL works)
I appreciate any help ^^
Edit:
Link to documentation:
https://trackhive.docs.apiary.io/#introduction/api-requirements/end-point

If I register on https://my.trackinghive.com/login and generate API key then code works for me.
I use requests instead of urllib because it is easier to create POST request. And documentation shows that it has to be POST, not GET.
And it is easier to send JSON data. It doesn't need header Content-Type': 'application/json' because requests will add it automatically when is use json=.... And it doesn't need to convert values to one string because requests will do it automatically.
BTW: I don't need module json to work with data. I use it only to convert it to string with indentation to display it.
import requests
values = {
"tracking_number": "9361289676090919095393",
"slug": "usps"
}
token = 'eyJh....'
headers = {
'Authorization': f'Bearer {token}' # both works for me
#'Authorization': f'{token}'
}
url = 'https://api.trackinghive.com/trackings'
r = requests.post(url, headers=headers, json=values)
print('\n--- as text ---\n')
print(r.text)
print()
print('\n--- as dict/list ---\n')
data = r.json()
print('keys:', list(data.keys()))
print('message:', data["meta"]["message"][0])
# - show it more readable -
print('\n--- more readable ---\n')
import json
text = json.dumps(data, indent=2)
print(text)
Result:
--- as text ---
{"meta":{"code":409,"message":["Tracking already exists."]},"data":{"_id":"60a5b5a0aa4e400011a0c657","current_status":"Pending","return_to_sender":false}}
--- as dict/list ---
keys: ['meta', 'data']
messge: Tracking already exists.
--- more readable ---
{
"meta": {
"code": 409,
"message": [
"Tracking already exists."
]
},
"data": {
"_id": "60a5b5a0aa4e400011a0c657",
"current_status": "Pending",
"return_to_sender": false
}
}

Related

Python requests PUT request with json parameter fails and data parameter succeeds

Problem
I've looked at some of the documentation about the json and data parameters and the differences between them. I think I understand the difference, best explained here, in my opinion.
However, I have a specific request that fails on PUT using json, but fails using data, and I'm not sure why. Can someone clarify why this is the case? Could it be that there is a list in the payload?
Context
I have requests==2.28.0 installed. Below is the code that submits the PUT requests to an API for PagerDuty, the incident management software, one using data (successful) and one using json (failing). Otherwise they are identical.
The weird thing is that their examples use the json parameter.
payload = f'{{"source_incidents": [{{"id": "{child_incident_id}", "type": "incident_reference"}}]}}'
headers = {
'Content-Type': "application/json",
'Accept': "application/vnd.pagerduty+json;version=2",
'From': email,
'Authorization': f"Token token={read_write_api_token}"
}
response = requests.put(f'https://api.pagerduty.com/incidents/{parent_incident_id}/merge', data=payload, headers=headers)
print("response: ", response)
Result: response: <Response [200]>
payload = f'{{"source_incidents": [{{"id": "{child_incident_id}", "type": "incident_reference"}}]}}'
headers = {
'Content-Type': "application/json",
'Accept': "application/vnd.pagerduty+json;version=2",
'From': email,
'Authorization': f"Token token={read_write_api_token}"
}
response = requests.put(f'https://api.pagerduty.com/incidents/{parent_incident_id}/merge', json=payload, headers=headers)
print("response: ", response)
Result: response: <Response [400]>
Your payload is a string while json parameter takes a dictionary. That's the whole point of the json argument (you don't have to encode it yourself):
If you need that header set and you don’t want to encode the dict yourself, you can also pass it directly using the json parameter (added in version 2.4.2) and it will be encoded automatically:
You should pass a dictionary if you want to use the json parameter:
payload = {
"source_incidents": [
{
"id": child_incident_id,
"type": "incident_reference"
}
]
}
which is more readable anyway.
Alternatively you could use json.loads to parse your string:
import json
payload = f'{{"source_incidents": [{{"id": "{child_incident_id}", "type": "incident_reference"}}]}}'
headers = {
'Content-Type': "application/json",
'Accept': "application/vnd.pagerduty+json;version=2",
'From': email,
'Authorization': f"Token token={read_write_api_token}"
}
response = requests.put(f'https://api.pagerduty.com/incidents/{parent_incident_id}/merge', data=json.loads(payload), headers=headers)
print("response: ", response)
That is what the requests library does with json data.
Converts your Python object to JSON using the json encoder.
Sets the "content-type" header to "application/json".
It is possible to implement it as shown here.
def put(uri, data=None, json=None):
if json and data:
raise Exception()
if json:
payload = json.dumps(json)
else:
payload = data
...
So the first request returns 200 because you passed valid JSON OBJECT through the "data" parameter.
And the second request fails because you passed STRING through the JSON that will be dumped with "json.dumps(obj)" for example.
As a result, it would be nothing more than a string, which is also a valid JSON object but not a javascript object.
As shown here, if you pass a string through "json.dumps" and a dictionary, it returns two different values: a string and an object.
>>> json.dumps("{}")
'"{}"'
>>> json.dumps({})
'{}'
>>>

How to delete an attachable using the Quickbooks API?

I am struggling to delete an attachable using the Quickbooks API.
I keep getting the error: ""Message":"Error parsing object","Detail":"ObjectParserError: Request Body is Empty","code":"2310","element":""
I wrote the following function (I put hard coded values to make sure that it works properly). The accessToken is definitely accurate as it works properly for other functions.
def deleteAttachable(accessToken):
base_url = 'https://sandbox-quickbooks.api.intuit.com/'
url = '{0}/v3/company/{1}/attachable?operation=delete'.format(base_url, cfg.qBData["realm_id"])
auth_header = 'Bearer {0}'.format(accessToken)
headers = {
"SyncToken": "0",
"domain": "QBO",
'Id': "5000000000001149164",
"FileName" : "bf546e8b-1dc4-4e42-b305-6435c28e2d8a1-AMAZON THERMOSTAT.pdf",
'Authorization': auth_header,
'Content-type': 'application/json'
}
print(url)
print(headers)
response = requests.post(url, headers=headers)

Convert a PDF to DOCX using Adobe PDF Services via REST API (with Python)

I am trying to query Adobe PDF services API to generate (export) DOCX from PDF documents.
I just wrote a python code to generate a Bearer Token in order to be identified from Adobe PDF services (see the question here: https://stackoverflow.com/questions/68351955/tunning-a-post-request-to-reach-adobe-pdf-services-using-python-and-a-rest-api). Then I wrote the following piece of code, where I tried to follow the instruction in this page concerning the EXPORT option of Adobe PDF services (here: https://documentcloud.adobe.com/document-services/index.html#post-exportPDF).
Here is the piece of code :
import requests
import json
from requests.structures import CaseInsensitiveDict
N/B: I didn't write the part of the code generating the Token and enabling identification by the server
>> This part is a POST request to upload my PDF file via form parameters
URL = "https://cpf-ue1.adobe.io/ops/:create?respondWith=%257B%2522reltype%2522%253A%2520%2522http%253A%252F%252Fns.adobe.com%252Frel%252Fprimary%2522%257D"
headers = CaseInsensitiveDict()
headers["x-api-key"] = "client_id"
headers["Authorization"] = "Bearer MYREALLYLONGTOKENIGOT"
headers["Content-Type"] = "application/json"
myfile = {"file":open("absolute_path_to_the_pdf_file/input.pdf", "rb")}
j="""
{
"cpf:engine": {
"repo:assetId": "urn:aaid:cpf:Service-26c7fda2890b44ad9a82714682e35888"
},
"cpf:inputs": {
"params": {
"cpf:inline": {
"targetFormat": "docx"
}
},
"documentIn": {
"dc:format": "application/pdf",
"cpf:location": "C:/Users/a-bensghir/Downloads/P_D_F/trs_pdf_file_copy.pdf"
}
},
"cpf:outputs": {
"documentOut": {
"dc:format": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"cpf:location": "C:/Users/a-bensghir/Downloads/P_D_F/output.docx"
}
}
}"""
resp = requests.post(url=URL, headers=headers, json=json.dumps(j), files=myfile)
print(resp.text)
print(resp.status_code)
The status of the code is 400
I am tho well authentified by the server
But I get the following as a result of print(resp.text) :
{"requestId":"the_request_id","type":"Bad Request","title":"Not a multipart request. Aborting.","status":400,"report":"{\"error_code\":\"INVALID_MULTIPART_REQUEST\"}"}
I think that I have problems understanding the "form parameters" from the Adobe Guide concerning POST method for the EXPORT job of the API (https://documentcloud.adobe.com/document-services/index.html).
Would you have any ideas for improvement. thank you !
Make you variable j as a python dict first then create a JSON string from it.
What's also not super clear from Adobe's documentation is the value for documentIn.cpf:location needs to be the same as the key used for you file. I've corrected this to InputFile0 in your script. Also guessing you want to save your file so I've added that too.
import requests
import json
import time
URL = "https://cpf-ue1.adobe.io/ops/:create?respondWith=%257B%2522reltype%2522%253A%2520%2522http%253A%252F%252Fns.adobe.com%252Frel%252Fprimary%2522%257D"
headers = {
'Authorization': f'Bearer {token}',
'Accept': 'application/json, text/plain, */*',
'x-api-key': client_id,
'Prefer': "respond-async,wait=0",
}
myfile = {"InputFile0":open("absolute_path_to_the_pdf_file/input.pdf", "rb")}
j={
"cpf:engine": {
"repo:assetId": "urn:aaid:cpf:Service-26c7fda2890b44ad9a82714682e35888"
},
"cpf:inputs": {
"params": {
"cpf:inline": {
"targetFormat": "docx"
}
},
"documentIn": {
"dc:format": "application/pdf",
"cpf:location": "InputFile0"
}
},
"cpf:outputs": {
"documentOut": {
"dc:format": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"cpf:location": "C:/Users/a-bensghir/Downloads/P_D_F/output.docx"
}
}
}
body = {"contentAnalyzerRequests": json.dumps(j)}
resp = requests.post(url=URL, headers=headers, data=body, files=myfile)
print(resp.text)
print(resp.status_code)
poll = True
while poll:
new_request = requests.get(resp.headers['location'], headers=headers)
if new_request.status_code == 200:
open('test.docx', 'wb').write(new_request.content)
poll = False
else:
time.sleep(5)
I don't know why the docx file (its well created by the way) doesn't open, telling via popup that the content is not readable. maybe it's due to the 'wb' parsing methos
I had the same issue. Typecasting to 'bytes' the request contents solved it.
poll = True
while poll:
new_request = requests.get(resp.headers['location'], headers=headers)
if new_request.status_code == 200:
with open('test.docx', 'wb') as f:
f.write(bytes(new_request.content))
poll = False
else:
time.sleep(5)

HTTP 400 error using Python Requests POST and Intercom API

I am trying to pull a list of conversations from Intercom using their API, restricted based on the date that they were updated.
Their site (https://developers.intercom.com/intercom-api-reference/reference#search-for-conversations) says
To search for conversations, you need to send a POST request to
https://api.intercom.io/conversations/search. This will accept a query
object in the body which will define your filters in order to search
for conversations.
So I tried:
import requests
url = 'https://api.intercom.io/conversations/search'
data_params = {
"query": {
"field": "updated_at",
"operator": ">",
"value": 1560436784
}
}
headers = {'Authorization' : 'Bearer ******************', 'Accept':'application/json', 'Content-Type':'application/json'}
r = requests.post(url, headers = headers, data = data_params)
r.status_code
This consistently runs into a 400 'Bad Request' error, but I'm struggling to see why. I've also tried json = data_params in the post call but same result. I appreciate the code might not be fully reproducible as it requires an authenticated API connection, but if there's anything obviously wrong it would be good to know!
Obviously, there seems no obviously error here.
for the 400 Bad Request . I think you should do the check of you data_params if there's something missing or in bad format. also the headers
You should try converting the data_params dictionary to json.
I tested with json.dumps and this seems to have worked:
import json
url = 'https://api.intercom.io/conversations/search'
data_params = {
"query": {
"field": "updated_at",
"operator": ">",
"value": 1560436784
}
}
headers = {'Authorization' : 'Bearer ******************', 'Accept':'application/json', 'Content-Type':'application/json'}
r = requests.post(url, headers = headers, data = json.dumps(data_params))
print(r.status_code)

Tomtom webfleet order api gives 40 general error

I am trying to insert a destination order using action=insertDestinationOrder
I am using POST method with all required parameters, but keep getting
{
"errorCode": 40,
"errorMsg": "general error"
}
I have checked using postman too. But still same.
Below is the request using python requests package.
import requests
url = "https://csv.telematics.tomtom.com/extern"
payload = "orderid=TO0049&country=DE&city=Cologne&latitude=50974519&ordertype=delivery%20order&zip=50735&longitude=6977319&street=Am%20Niehler%20Hafen%20%26%20Stapelkai%2C%2050735%20Cologne%20(Niehl)&account=XXXX&username=XXXX&password=XXXX&apikey=XXXX&lang=en&action=insertDestinationOrder&ordertext=Am%20Niehler%20Hafen%20%26%20Stapelkai%2C%2050735%20Cologne%20(Niehl)&useUTF8=true&outputformat=json"
headers = {
'content-type': "application/x-www-form-urlencoded"
}
response = requests.request("POST", url, data=payload, headers=headers)
print(response.text)
Need some help.
Try adding a ? to the end of the url, or to the start of the payload:
url = "https://csv.telematics.tomtom.com/extern?"
or
payload = "?orderid=TO0049&country=DE&city=Cologne&latitude=50974519&ordertype=delivery%20order&zip=50735&longitude=6977319&street=Am%20Niehler%20Hafen%20%26%20Stapelkai%2C%2050735%20Cologne%20(Niehl)&account=XXXX&username=XXXX&password=XXXX&apikey=XXXX&lang=en&action=insertDestinationOrder&ordertext=Am%20Niehler%20Hafen%20%26%20Stapelkai%2C%2050735%20Cologne%20(Niehl)&useUTF8=true&outputformat=json"

Categories