I need to POST a JSON from a client to a server. I'm using Python 2.7.1 and simplejson. The client is using Requests. The server is CherryPy. I can GET a hard-coded JSON from the server (code not shown), but when I try to POST a JSON to the server, I get "400 Bad Request".
Here is my client code:
data = {'sender': 'Alice',
'receiver': 'Bob',
'message': 'We did it!'}
data_json = simplejson.dumps(data)
payload = {'json_payload': data_json}
r = requests.post("http://localhost:8080", data=payload)
Here is the server code.
class Root(object):
def __init__(self, content):
self.content = content
print self.content # this works
exposed = True
def GET(self):
cherrypy.response.headers['Content-Type'] = 'application/json'
return simplejson.dumps(self.content)
def POST(self):
self.content = simplejson.loads(cherrypy.request.body.read())
Any ideas?
10 Answers 10
Starting with Requests version 2.4.2, you can use the json=
parameter (which takes a dictionary) instead of data=
(which takes a string) in the call:
>>> import requests
>>> r = requests.post('http://httpbin.org/post', json={"key": "value"})
>>> r.status_code
200
>>> r.json()
{'args': {},
'data': '{"key": "value"}',
'files': {},
'form': {},
'headers': {'Accept': '*/*',
'Accept-Encoding': 'gzip, deflate',
'Connection': 'close',
'Content-Length': '16',
'Content-Type': 'application/json',
'Host': 'httpbin.org',
'User-Agent': 'python-requests/2.4.3 CPython/3.4.0',
'X-Request-Id': 'xx-xx-xx'},
'json': {'key': 'value'},
'origin': 'x.x.x.x',
'url': 'http://httpbin.org/post'}
-
2Setting this to the accepted answer since this is more idiomatic as of 2.4.2. Keep in mind, for crazy unicode, this may not work.Charles R– Charles R2015年09月23日 23:00:28 +00:00Commented Sep 23, 2015 at 23:00
-
8I saw an example of this that took the dict object and performed json.dumps(object) before sending. Don't do this...it messes up your JSON. The above is perfect..you can pass it a python object and it turns into perfect json.MydKnight– MydKnight2020年05月15日 23:13:50 +00:00Commented May 15, 2020 at 23:13
-
7@MydKnight looking at the source code, Requests sets the content type then does
json.dumps(your_json)
and converts the result from astr
tobytes
.user3064538– user30645382021年01月12日 18:18:07 +00:00Commented Jan 12, 2021 at 18:18 -
1I logged into my account especially to give you +1 for this. Thanks. I was stuck for a couple of hours until I saw you could specify json=dict It' a script for Linode DNS API that I am working on. Thank you very much!user7259432– user72594322022年01月30日 23:27:29 +00:00Commented Jan 30, 2022 at 23:27
-
3Before I found this answer I had also to use
data=json.dumps(...)
but withoutContent-type: application/json
in the header it wouldn't work. Thisjson=...
thing works perfect.elano7– elano72022年07月01日 08:30:53 +00:00Commented Jul 1, 2022 at 8:30
It turns out I was missing the header information. The following works:
import requests
url = "http://localhost:8080"
data = {'sender': 'Alice', 'receiver': 'Bob', 'message': 'We did it!'}
headers = {'Content-type': 'application/json', 'Accept': 'text/plain'}
r = requests.post(url, data=json.dumps(data), headers=headers)
-
1Good catch - I saw your
application/json
inGET
and somehow missed that you hadn't provided it on the request. You may also need to make sure that you return something fromPOST
or you might get a500
.Nick Bastin– Nick Bastin2012年03月31日 04:01:45 +00:00Commented Mar 31, 2012 at 4:01 -
Doesn't seem to be necessary. When I print
r
, I get<Response [200]>
.Charles R– Charles R2012年04月01日 05:36:37 +00:00Commented Apr 1, 2012 at 5:36 -
How do I retrieve this json at the server side ?VaidAbhishek– VaidAbhishek2013年02月15日 12:01:21 +00:00Commented Feb 15, 2013 at 12:01
-
11Little heads up before using
json.dumps
here. Thedata
parameter ofrequests
works fine with dictionaries. No need for converting to a string.Advait Saravade– Advait Saravade2018年07月02日 06:32:17 +00:00Commented Jul 2, 2018 at 6:32 -
2@lolololol ol It's necessary. You are setting an argument for the function/method you're calling. In this case the function/method is named post(). The left side of the assignment operator
=
is the parameter name some dev defined (e.g.def post(some_param0, headers, ...):
). The righthand side of the assignment operator (i.e. assignment value) is the variable you defined (e.g.headers = { ... }
) that also just happens to be calledheaders
in this example. You could rename the right side of the assignment (e.g. the assignment value) to whatever you like!chevydog– chevydog2023年07月02日 21:50:05 +00:00Commented Jul 2, 2023 at 21:50
From requests 2.4.2 (https://pypi.python.org/pypi/requests), the "json" parameter is supported. No need to specify "Content-Type". So the shorter version:
requests.post('http://httpbin.org/post', json={'test': 'cheers'})
Which parameter between data
/ json
/ files
you need to use depends on a request header named Content-Type
(you can check this through the developer tools of your browser).
When the Content-Type
is application/x-www-form-urlencoded
, use data=
:
requests.post(url, data=json_obj)
When the Content-Type
is application/json
, you can either just use json=
or use data=
and set the Content-Type
yourself:
requests.post(url, json=json_obj)
requests.post(url, data=jsonstr, headers={"Content-Type":"application/json"})
When the Content-Type
is multipart/form-data
, it's used to upload files, so use files=
:
requests.post(url, files=xxxx)
-
+1. If you're using
curlify
to see the request made from the response, the content type header will not be set unless you follow these instructions.print(curlify.to_curl(project.request))
Olshansky– Olshansky2021年04月12日 20:54:53 +00:00Commented Apr 12, 2021 at 20:54 -
The key realization is that every parameter will perform automagic serialization without regard for the current value of the
Content-Type
header. Consequently,data=json_obj
is basically always incorrect; the value must be stringified first, either explicitly by the caller or implicitly via thejson
parameter.mkjeldsen– mkjeldsen2022年04月22日 07:43:37 +00:00Commented Apr 22, 2022 at 7:43
The better way is:
url = "http://xxx.xxxx.xx"
data = {
"cardno": "6248889874650987",
"systemIdentify": "s08",
"sourceChannel": 12
}
resp = requests.post(url, json=data)
-
this is the same as the accepted answer from 3 years beforeCorey Goldberg– Corey Goldberg2025年07月02日 19:02:53 +00:00Commented Jul 2 at 19:02
headers = {"charset": "utf-8", "Content-Type": "application/json"}
url = 'http://localhost:PORT_NUM/FILE.php'
r = requests.post(url, json=YOUR_JSON_DATA, headers=headers)
print(r.text)
-
this is the same as the accepted answer from 8 years beforeCorey Goldberg– Corey Goldberg2025年07月02日 19:03:16 +00:00Commented Jul 2 at 19:03
Works perfectly with python 3.5+
client:
import requests
data = {'sender': 'Alice',
'receiver': 'Bob',
'message': 'We did it!'}
r = requests.post("http://localhost:8080", json={'json_payload': data})
server:
class Root(object):
def __init__(self, content):
self.content = content
print self.content # this works
exposed = True
def GET(self):
cherrypy.response.headers['Content-Type'] = 'application/json'
return simplejson.dumps(self.content)
@cherrypy.tools.json_in()
@cherrypy.tools.json_out()
def POST(self):
self.content = cherrypy.request.json
return {'status': 'success', 'message': 'updated'}
-
1Note/reminder: the
json
arg automatically adds thecontent-type
header forPOST
in newer versions of Python.MarkHu– MarkHu2022年02月15日 19:59:28 +00:00Commented Feb 15, 2022 at 19:59
With current requests
you can pass in any data structure that dumps to valid JSON , with the json
parameter, not just dictionaries (as falsely claimed by the answer by Zeyang Lin).
import requests
r = requests.post('http://httpbin.org/post', json=[1, 2, {"a": 3}])
this is particularly useful if you need to order elements in the response.
It always recommended that we need to have the ability to read the JSON file and parse an object as a request body. We are not going to parse the raw data in the request so the following method will help you to resolve it.
def POST_request():
with open("FILE PATH", "r") as data:
JSON_Body = data.read()
response = requests.post(url="URL", data=JSON_Body)
assert response.status_code == 200
I solved it this way:
from flask import Flask, request
from flask_restful import Resource, Api
req = request.json
if not req :
req = request.form
req['value']
Explore related questions
See similar questions with these tags.
__init__
methods with acontent
argument (and does not claim to in the link you supply). In the detailed example they have, the user supplies the code that calls__init__
and provides the arguments, which we have not seen here so I have no idea what state your object is in when your# this works
comment is relevant.requests
package. Postman documentation