124
data = {
        'ids': [12, 3, 4, 5, 6 , ...]
    }
    urllib2.urlopen("http://abc.example/api/posts/create",urllib.urlencode(data))

I want to send a POST request, but one of the fields should be a list of numbers. How can I do that? (JSON?)

Stephen Ostermiller
  • 23,933
  • 14
  • 88
  • 109
TIMEX
  • 259,804
  • 351
  • 777
  • 1,080

9 Answers9

158

If your server is expecting the POST request to be json, then you would need to add a header, and also serialize the data for your request...

Python 2.x

import json
import urllib2

data = {
        'ids': [12, 3, 4, 5, 6]
}

req = urllib2.Request('http://example.com/api/posts/create')
req.add_header('Content-Type', 'application/json')

response = urllib2.urlopen(req, json.dumps(data))

Python 3.x

https://stackoverflow.com/a/26876308/496445


If you don't specify the header, it will be the default application/x-www-form-urlencoded type.

jdi
  • 90,542
  • 19
  • 167
  • 203
  • I have a question. is it possible to add multiple items in the header... like content type & client-id... @jdi – Omar Jandali Sep 05 '17 at 03:36
  • @OmarJandali, just call `add_header()` again, for each header you want to add. – jdi Sep 05 '17 at 03:42
  • i have the following coded but it is not printing anything. it was supposed to print the url and headers but nothing was printed... `req = urllib.Request('http://uat-api.synapsefi.com') req.add_header('X-SP-GATEWAY', 'client_id_asdfeavea561va9685e1gre5ara|client_secret_4651av5sa1edgvawegv1a6we1v5a6s51gv') req.add_header('X-SP-USER-IP', '127.0.0.1') req.add_header('X-SP-USER', '| ge85a41v8e16v1a618gea164g65') req.add_header('Content-Type', 'application/json') print(req)`... – Omar Jandali Sep 05 '17 at 04:45
  • urllib2 was not recognized so i just used urllib. i am also getting an error with the request. `The view tab.views.profileSetup didn't return an HttpResponse object. It returned None instead.` @jdi – Omar Jandali Sep 05 '17 at 04:47
  • @OmarJandali, please keep in mind that this answer was originally given in 2012, under python 2.x. You are using Python3 so the imports will be different. It would now be `import urllib.request` and `urllib.request.Request()`. Furthermore, printing the req object does nothing interesting. You can clearly see the headers have been added by printing `req.headers`. Beyond that, I am not sur why it isn't working in your application. – jdi Sep 05 '17 at 05:06
  • So i have two lines that i made changes to `response = urllib.request.Request(req, json.dumps(data))` and `req = urllib.request.Request('http://uat-api.synapsefi.com')`. i am getting a message saying `unknown url type: 'urllib.request.Request object at 0x00000250B05474A8'` from the second line that i made changes to... what is the way fro me to change the url type @jdi – Omar Jandali Sep 05 '17 at 05:53
  • @OmarJandali, you aren't calling `urlopen()` like the original example. That is why you are getting an error. See: https://pastebin.com/1gUdGYcP – jdi Sep 05 '17 at 06:02
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/153654/discussion-between-omar-jandali-and-jdi). – Omar Jandali Sep 05 '17 at 06:18
  • Than you so much, it is working and sending a response. it is sending a response in the wrong format `POST data should be bytes, an iterable of bytes, or a file object. It cannot be of type str.` – Omar Jandali Sep 05 '17 at 06:21
  • The python3 version is the one here: https://stackoverflow.com/a/26876308/5973334 – better merge it with the accepted answer – Kuzeko May 04 '18 at 13:57
  • @Kuzeko, thanks. I have added that reference to my answer – jdi May 05 '18 at 01:09
121

I recommend using the incredible requests module.

http://docs.python-requests.org/en/v0.10.7/user/quickstart/#custom-headers

url = 'https://api.github.com/some/endpoint'
payload = {'some': 'data'}
headers = {'content-type': 'application/json'}

response = requests.post(url, data=json.dumps(payload), headers=headers)
FogleBird
  • 74,300
  • 25
  • 125
  • 131
  • This gives me `TypeError: post() takes from 1 to 2 positional arguments but 3 were given` – zakdances Aug 25 '13 at 13:46
  • 3
    It is much more succinct to just use `json=payload` (which may have been introduced since this answer was written long ago) without specifying the header or calling `json.dumps()`. See other answers on this page. – rgov Nov 04 '21 at 13:02
92

For python 3.4.2, I found the following will work:

import urllib.request
import json

body = {'ids': [12, 14, 50]}
myurl = "http://www.testmycode.example"

req = urllib.request.Request(myurl)
req.add_header('Content-Type', 'application/json; charset=utf-8')
jsondata = json.dumps(body)
jsondataasbytes = jsondata.encode('utf-8')   # needs to be bytes
req.add_header('Content-Length', len(jsondataasbytes))
response = urllib.request.urlopen(req, jsondataasbytes)
Stephen Ostermiller
  • 23,933
  • 14
  • 88
  • 109
mike gold
  • 1,551
  • 12
  • 12
  • 1
    Python3.6.2 this worked. Only adding header with req.add_header(...) worked for me. – Shalin LK Oct 31 '17 at 08:49
  • 1
    You do not need to specify the `Content-Length` header, it will be calculated by `urllib` automatically. – rgov Nov 04 '21 at 13:05
20

This works perfect for Python 3.5, if the URL contains Query String / Parameter value,

Request URL = https://bah2.example/ws/rest/v1/concept/ Parameter value = 21f6bb43-98a1-419d-8f0c-8133669e40ca

import requests

url = 'https://bahbah2.example/ws/rest/v1/concept/21f6bb43-98a1-419d-8f0c-8133669e40ca'
data = {"name": "Value"}
r = requests.post(url, auth=('username', 'password'), json=data)
print(r.status_code)
Stephen Ostermiller
  • 23,933
  • 14
  • 88
  • 109
MAX
  • 1,562
  • 4
  • 17
  • 25
  • 7
    in your code snipper, headers variable stays unused – psukys Jan 08 '17 at 12:42
  • **This answer is insecure.** Do not pass `verify=False`, which disables certificate validation and opens your code up to man-the-middle attacks. – rgov Nov 04 '21 at 13:07
  • I removed `verify=False` from the code sample to resolve the above comment. – rgov Nov 04 '21 at 13:13
12

Here is an example of how to use urllib.request object from Python standard library.

import urllib.request
import json
from pprint import pprint

url = "https://app.close.com/hackwithus/3d63efa04a08a9e0/"

values = {
    "first_name": "Vlad",
    "last_name": "Bezden",
    "urls": [
        "https://twitter.com/VladBezden",
        "https://github.com/vlad-bezden",
    ],
}


headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
}

data = json.dumps(values).encode("utf-8")
pprint(data)

try:
    req = urllib.request.Request(url, data, headers)
    with urllib.request.urlopen(req) as f:
        res = f.read()
    pprint(res.decode())
except Exception as e:
    pprint(e)
Vlad Bezden
  • 83,883
  • 25
  • 248
  • 179
  • 1
    works great using standard library urllib. Don't forget the data in Request(url, data, headers), otherwise it would look like a GET to the server. – Apurva Singh May 31 '22 at 16:09
  • @ApurvaSingh Putting the `data` automatically creates a 'Content-Type', 'application/json; charset=utf-8' header, like in the other answer? – Jay Aug 13 '22 at 13:18
  • @Jay turn on http connection debugging in Python code and check request headers in the output log – Apurva Singh Aug 14 '22 at 06:06
4

You have to add header,or you will get http 400 error. The code works well on python2.6,centos5.4

code:

    import urllib2,json

    url = 'http://www.google.com/someservice'
    postdata = {'key':'value'}

    req = urllib2.Request(url)
    req.add_header('Content-Type','application/json')
    data = json.dumps(postdata)

    response = urllib2.urlopen(req,data)
nullptr
  • 339
  • 2
  • 6
  • Note: This answer is very old and `urllib2` has been removed in Python 3. Look for other examples using `urllib` or `requests`. – rgov Nov 04 '21 at 13:00
4

In the lastest requests package, you can use json parameter in requests.post() method to send a json dict, and the Content-Type in header will be set to application/json. There is no need to specify header explicitly.

import requests

payload = {'key': 'value'}
requests.post(url, json=payload)
jdhao
  • 24,001
  • 18
  • 134
  • 273
  • Note that this will result in POSTed json with single quotes, which is technically invalid. – Jethro Apr 10 '20 at 11:06
  • @Jethro Have you observed errors when using single quotes? It is valid to use single quotes in Python. Personally, I haven't met any issues regarding this. – jdhao Apr 10 '20 at 11:32
  • Aah apologies I was mistaken, I thought my server was receiving single-quoted JSON but It turned out to be a separate issue and some misleading debugging. Cheers, this is much tidier than having to specify the header manually! – Jethro Apr 10 '20 at 13:09
3

The Requests package used in many answers here is great but not necessary. You can perform a POST of JSON data succinctly with the Python 3 standard library in one step:

import json
from urllib import request

request.urlopen(request.Request(
    'https://example.com/url',
    headers={'Content-Type': 'application/json'},
    data=json.dumps({
        'pi': 3.14159
    }).encode()
))

If you need to read the result, you can .read() from the returned file-like object and use json.loads() to decode a JSON response.

rgov
  • 3,516
  • 1
  • 31
  • 51
1

This one works fine for me with apis

import requests

data={'Id':id ,'name': name}
r = requests.post( url = 'https://apiurllink', data = data)
Sudhir G
  • 381
  • 4
  • 7
  • 2
    This is an incorrect answer. The `data=data` parameter sends a **form-encoded** request, which is not JSON. Use `json=data` instead. – rgov Nov 04 '21 at 12:58