54

I have been trying to figure out how to use python-requests to send a request that the url looks like:

http://example.com/api/add.json?name='hello'&data[]='hello'&data[]='world'

Normally I can build a dictionary and do:

data = {'name': 'hello', 'data': 'world'}
response = requests.get('http://example.com/api/add.json', params=data)

That works fine for most everything that I do. However, I have hit the url structure from above, and I am not sure how to do that in python without manually building strings. I can do that, but would rather not.

Is there something in the requests library I am missing or some python feature I am unaware of?

Also what do you even call that type of parameter so I can better google it?

Buddy Lindsey
  • 3,560
  • 6
  • 30
  • 42
  • I dont think there is any feature in `requests` to build a url like that. You need to build it manually through code. – Arvind Apr 09 '14 at 22:04
  • Thought these type of urls are `url with query string`. But they dont have `[]` kind of thing in between. – Arvind Apr 09 '14 at 22:11

5 Answers5

113

All you need to do is putting it on a list and making the key as list like string:

data = {'name': 'hello', 'data[]': ['hello', 'world']}
response = requests.get('http://example.com/api/add.json', params=data)
Tomer Zait
  • 1,756
  • 2
  • 14
  • 12
  • 2
    I hope the original user found this answer, because it exactly solves the problem with the SendGrid API. Thanks @Tomer. – BenDundee Nov 06 '14 at 01:41
  • 1
    Just doing `data = {'name': 'hello', 'data': ['hello', 'world']}` works fine too with requests (2.9). – Rémy Greinhofer Mar 31 '16 at 01:22
  • 1
    @RémyGreinhofer That didn't work for me. I had to use Tomer's solution and I am using requests 2.10. – Sergio Jul 12 '16 at 20:09
  • 1
    `data = {'name': 'hello', 'data': ['hello', 'world']}` will be translated to: `data=hello&data=world&name=hello` and that's is not rfc compliant `data = {'name': 'hello', 'data[]': ['hello', 'world']}` will be translated to: `data%5B%5D=hello&data%5B%5D=world&name=hello` (`data[]=hello&data[]=world&name=hello`) – Tomer Zait Jul 14 '17 at 11:11
  • @TomerZait can you refer and link the RFC that you mention? – blueFast Jun 26 '18 at 12:09
  • 1
    Also works fine with `aiohttp`. – Xiwei Wang Dec 01 '21 at 13:09
  • `'data[]': ['hello', 'world']` didn't work for me while simple `'data': ['hello', 'world']` worked! (I used FastAPI ). – Konstantin Smolyanin Aug 15 '23 at 19:33
29

What u are doing is correct only. The resultant url is same what u are expecting.

>>> payload = {'name': 'hello', 'data': 'hello'}
>>> r = requests.get("http://example.com/api/params", params=payload)

u can see the resultant url:

>>> print(r.url)
http://example.com/api/params?name=hello&data=hello

According to url format:

In particular, encoding the query string uses the following rules:

  • Letters (A–Z and a–z), numbers (0–9) and the characters .,-,~ and _ are left as-is
  • SPACE is encoded as + or %20
  • All other characters are encoded as %HH hex representation with any non-ASCII characters first encoded as UTF-8 (or other specified encoding)

So array[] will not be as expected and will be automatically replaced according to the rules:

If you build a url like :

`Build URL: http://example.com/api/add.json?name='hello'&data[]='hello'&data[]='world'`

OutPut will be:

>>> payload = {'name': 'hello', "data[]": 'hello','data[]':'world'}
>>> r = requests.get("http://example.com/api/params", params=payload)
>>> r.url
u'http://example.com/api/params?data%5B%5D=world&name=hello'

This is because Duplication will be replaced by the last value of the key in url and data[] will be replaced by data%5B%5D.

If data%5B%5D is not the problem(If server is able to parse it correctly),then u can go ahead with it.

Source Link

Arvind
  • 2,525
  • 1
  • 13
  • 21
  • 2
    I agree with you everything you have said. Unfortunately, I have come across an API (http://sendgrid.com/docs/API_Reference/Marketing_Emails_API/emails.html, look at 'Add multiple email recipients to a list') which is expecting multiple of the same keys. So need a way force duplicates or I need to build the parameter string manually. – Buddy Lindsey Apr 10 '14 at 01:02
  • yes as i said u can make it using string operations and use that url but `requests` library does not provoid that ,if u want to stick to `requests` . – Arvind Apr 10 '14 at 05:57
  • Never hurts to hope. Going to mark this as the answer because it at least proves what I already thought. – Buddy Lindsey Apr 10 '14 at 16:26
  • This answer explains how requests are sent. However, if your API handles query parameter arrays correctly (as you can do with Django's `QueryDict.getlist()` method [https://docs.djangoproject.com/en/1.11/ref/request-response/#django.http.QueryDict.getlist]), then you can send query params like this and they will be fine. It's basically up to the API to handle the request if formed correctly. – Blairg23 Nov 21 '17 at 21:34
  • that's not going to work if u have more than 3 or more similar keys they will be overriden instead – urek mazino Apr 05 '23 at 22:20
9

One solution if using the requests module is not compulsory, is using the urllib/urllib2 combination:

payload = [('name', 'hello'), ('data[]', ('hello', 'world'))]
params = urllib.urlencode(payload, doseq=True)
sampleRequest = urllib2.Request('http://example.com/api/add.json?' + params)
response = urllib2.urlopen(sampleRequest)

Its a little more verbose and uses the doseq(uence) trick to encode the url parameters but I had used it when I did not know about the requests module.

For the requests module the answer provided by @Tomer should work.

sanchitarora
  • 882
  • 9
  • 18
0

Some api-servers expect json-array as value in the url query string. The requests params doesn't create json array as value for parameters.

The way I fixed this on a similar problem was to use urllib.parse.urlencode to encode the query string, add it to the url and pass it to requests

e.g.

from urllib.parse import urlencode
query_str = urlencode(params)
url = "?" + query_str
response = requests.get(url, params={}, headers=headers)
bnik
  • 49
  • 3
0

The solution is simply using the famous function: urlencode

>>> import urllib.parse
>>> params = {'q': 'Python URL encoding', 'as_sitesearch': 'www.urlencoder.io'}
>>> urllib.parse.urlencode(params)
'q=Python+URL+encoding&as_sitesearch=www.urlencoder.io'
Yazid Erman
  • 1,166
  • 1
  • 13
  • 24