I am trying to hook up to MailChimp's api, in my Django application, to add an email to one of my lists. Seems pretty simple enough. I add my api key to the header of the request, with the email address and other variable in the body of the request. every time I try and connect though, I get a response status code of 400. The message says there is a JSON parsing error, and that my JSON is either formatted incorrectly, or there is missing data required for the request. I am making this same api call however with Postman, and am getting a good response back.
view function
import requests
def join_newsletter(request, email):
# hash the user's email for mailchimp's API
# m = hashlib.md5()
# c_email = email
# m.update(c_email.encode('utf-8'))
# email_hash = m.hexdigest()
api_key = 'apikey ' + settings.MAILCHIMP_API
api_endpoint = api_endpoint
data = {
"email_address": email,
"status": "subscribed"
}
header = {
'Authorization': api_key
}
r = requests.post(api_endpoint, data=data, headers=header)
message = r.content
return message
Related
I am trying to web scrape Discord messages using Python from specific server channels.
import requests
import json
def retrieve_messages(channelid):
headers = {
'authorization': 'enter the authorization code'
}
# need to make a request to the url
r = requests.get(
f'https://discord.com/api/v9/channels/{channelid}/messages', headers=headers)
# create JSON object
jsonn = json.loads(r.text)
# we can now use a for loop on this JSON object
for value in jsonn:
print(value, '\n') # new line as well to separate each message
retrieve_messages('channel server id')
I am expecting the messages to be outputted in the terminal but instead I keep receiving the following output.
The output that I am getting instead of the messages
you're probably using a bot
token = "token"
headers = {
'authorization': f'Bot {token}'
}
try this
I am trying to write a function in python that returns the json from a request to the smmry API. I was able to get it working with the SM_URL request like this:
def summry():
API_ENDPOINT = "https://api.smmry.com"
API_KEY = "B..."
params = {
"SM_API_KEY":API_KEY,
"SM_URL":"https:..."
}
r = requests.get(url=API_ENDPOINT, params=params)
return r.json()
However, I am not sure how you would do this for passing in a block of text instead of a URL. I have tried making the request with sm_api_input=my_input but that returned an error of insufficient variables. I have also tried it with a POST request and got the same error.
If anyone is curious, this is how I solved the problem. Turns out I needed an Expect: 100-continue header and the sm_api_input is a separate post field instead of a get query.
def summry(text):
API_KEY = "B..."
API_ENDPOINT = "https://api.smmry.com"
data = {
"sm_api_input":text
}
params = {
"SM_API_KEY":API_KEY
}
header_params = {"Expect":"100-continue"}
r = requests.post(url=API_ENDPOINT, params=params, data=data, headers=header_params)
return r.json()
I have a django rest API end point login which takes username and password in form of json object as below.
{
username: email,
password: password,
}
and returns a json object containing a token
{
token : 0234jh324234j2hiy342
}
Now i want to write a test in behave. I have following feature file.
Feature: Login User
By providing different credentials we check if our login API end point is working as expected or not
Scenario: Login User by Providing Authentication Credentials
Given I provide user authentication credentials
Then I must get a reponse with status code 200 and a jSon object with token
and following is my auth.py file
from behave import *
import requests
import json
#given('I have user authentication credentials')
def set_impl(context):
url = 'https://example.com/v1/login'
headers = {'content-type': 'application/json'}
body = {
"username": "xyz#email.com",
"password": "abcdef123",
}
#when('I make an http post call')
def step_impl(context):
context.res = requests.post(url, data=json.dumps(body), headers=headers)
#then('I must get a reponse with status code 200 and a jSon object with token')
def step_impl(context):
assert context.res.status == 200
I am unable to access the url, header and body from #given decorator in #when decorator. And how can i check the json in response against my expected json.
Per #KlausD.'s suggestion, you should add your variables to the behave's context object. I've edited your code to add your variables as the context object's attributes.
from behave import *
import requests
import json
#given('I have user authentication credentials')
def set_impl(context):
context.url = 'https://example.com/v1/login'
context.headers = {'content-type': 'application/json'}
context.body = {
"username": "xyz#email.com",
"password": "abcdef123",
}
#when('I make an http post call')
def step_impl(context):
context.res = requests.post(context.url, data=json.dumps(context.body), headers=context.headers)
#then('I must get a reponse with status code 200 and a jSon object with token')
def step_impl(context):
assert context.res.status == 200
As for checking the JSON in your response against your expected JSON...
Check out the requests package's response object here to find out how to get the response object's attributes.
Open your own expected JSON file via open(), grab the value that corresponds to the token key, and do an assert expectedToken == responseToken, or something of that sort.
Here is a snippet of code:
api_url = {url}
auth_head = {key: value} << my api authentication header
data = {'title':'Python'} << the valid json object format that the api accepts
Here is what I tried:
data = json.dumps(data)
json_obj = json.loads(data)
response = requests.post(api_url, headers = auth_head, data = json_obj)
print(response.text)
Here is the output:
"400 Bad request"
To make sure the url and my api token key work I tried GET:
response = requests.get(api_url, headers = auth_head)
print(response.status_code)
Output:
"200"
So the url and api token work fine. I feel I am not sending the correct json object construct that the api wants
You don't need to send data as json. Just send data as dictionary. It will accept. requests automatically will encode your dictionary to json
I am implementing Coinbase's exchange API using custom auth in requests-python. The following code works with all the (authenticated) GET-based calls, but fails for all the authenticated POST-based calls (I haven't tried with DELETE or UPDATE verbs). I don't understand why the signature wouldn't work for both, because the payload is timestamp + method + path for GETs and timestamp + method + path + body for PUTs, so custom auth seems correct. Something is going wrong with adding the body and changing GET to POST. Thanks!
You can get your API keys for trying it out here: https://gdax.com/settings
import json, hmac, hashlib, time, requests, base64
from requests.auth import AuthBase
class CoinbaseAuth(AuthBase):
SIGNATURE_HTTP_HEADER = 'CB-ACCESS-SIGN'
TIMESTAMP_HTTP_HEADER = 'CB-ACCESS-TIMESTAMP'
KEY_HTTP_HEADER = 'CB-ACCESS-KEY'
PASSPHRASE_HTTP_HEADER = 'CB-ACCESS-PASSPHRASE'
def __init__(self, api_key, secret_key, passphrase):
self.api_key = api_key
self.secret_key = secret_key
self.passphrase = passphrase
def __call__(self, request):
#Add headers
request.headers[CoinbaseAuth.KEY_HTTP_HEADER] = self.api_key
request.headers[CoinbaseAuth.PASSPHRASE_HTTP_HEADER] = self.passphrase
timestamp = str(time.time())
request.headers[CoinbaseAuth.TIMESTAMP_HTTP_HEADER] = timestamp
#add signature
method = request.method
path = request.path_url
content = request.body
message = timestamp + method + path
if content:
message += content
hmac_key = base64.b64decode(self.secret_key)
sig = hmac.new(hmac_key, message, hashlib.sha256)
sig_b64 = sig.digest().encode("base64").rstrip("\n")
#Add signature header
request.headers[CoinbaseAuth.SIGNATURE_HTTP_HEADER] = sig_b64
return request
#Get your keys here: https://gdax.com/settings
key = 'KEY GOES HERE'
secret = 'SECRET GOES HERE'
passphrase = 'PASSPHRASE GOES HERE'
api_url = 'https://api.gdax.com:443/'
auth = CoinbaseAuth(API_KEY, API_SECRET, API_PASS)
#GETs work, shows account balances
r = requests.get(api_url + 'accounts', auth=auth)
print r.json()
#POSTs fail: {message: 'invalid signature'}
order = {}
order['size'] = 0.01
order['price'] = 100
order['side'] = 'buy'
order['product_id'] = 'BTC-USD'
r = requests.post(api_url + 'orders', data=json.dumps(order), auth=auth)
print r.json()
And the output:
GET call: 200: [{u'available': .......}]
POST call: 400: {u'message': u'invalid signature'}
EDIT: POSTing 'a' instead of valid JSON-encoded data results in the same signature error (rather than a JSON decoding error from the server), so I don't think it is the way I'm forming the data. Notably, if I omit the body -- request.post(..., data='',...) --- the server responds appropriately with {u'message': u'Missing product_id'}.
I don't know why, but if I change the data keyword argument to requests.post() to json it works:
r = requests.post(api_url + 'orders', json=order, auth=auth)
EDIT: The only thing that changes, AFAICT, is the content-type in the header is changed from text to JSON. So it is likely that or a unicode vs ASCII encoding issue. Here's the issue for the library that added this feature recently: https://github.com/kennethreitz/requests/issues/2025#issuecomment-46337236
I believe the content needs to be a json string with no spaces (this is what the node example does anyway). Maybe try this:
message += json.dumps(content).replace(' ', '')
I had the same exact problem until I looked at the public gdax API for nodeJS and found that they are using some additional headers that were not mentioned in the GDAX API docs. I added them and then it started working. See my answer to the following: GDAX API Always Returns Http 400 "Invalid Signature" Even though I do it exactly like in the API Doc