278

I have a RESTful API that I have exposed using an implementation of Elasticsearch on an EC2 instance to index a corpus of content. I can query the search by running the following from my terminal (MacOSX):

curl -XGET 'http://ES_search_demo.com/document/record/_search?pretty=true' -d '{
  "query": {
    "bool": {
      "must": [
        {
          "text": {
            "record.document": "SOME_JOURNAL"
          }
        },
        {
          "text": {
            "record.articleTitle": "farmers"
          }
        }
      ],
      "must_not": [],
      "should": []
    }
  },
  "from": 0,
  "size": 50,
  "sort": [],
  "facets": {}
}'

How do I turn above into a API request using python/requests or python/urllib2 (not sure which one to go for - have been using urllib2, but hear that requests is better...)? Do I pass as a header or otherwise?

mkrieger1
  • 19,194
  • 5
  • 54
  • 65
user7289
  • 32,560
  • 28
  • 71
  • 88

5 Answers5

391

Using requests:

import requests
url = 'http://ES_search_demo.com/document/record/_search?pretty=true'
data = '''{
  "query": {
    "bool": {
      "must": [
        {
          "text": {
            "record.document": "SOME_JOURNAL"
          }
        },
        {
          "text": {
            "record.articleTitle": "farmers"
          }
        }
      ],
      "must_not": [],
      "should": []
    }
  },
  "from": 0,
  "size": 50,
  "sort": [],
  "facets": {}
}'''
response = requests.post(url, data=data)

Depending on what kind of response your API returns, you will then probably want to look at response.text or response.json() (or possibly inspect response.status_code first). See the quickstart docs here, especially this section.

Yacc
  • 1,068
  • 11
  • 16
andersschuller
  • 13,509
  • 2
  • 42
  • 33
  • 4
    @ParveenShukhala "Requests officially supports Python 2.6–2.7 & 3.3–3.5, and runs great on PyPy." -- https://pypi.python.org/pypi/requests/ – danio Dec 15 '16 at 09:10
  • 2
    As it's JSON you are sending, you can use the json parameter rather than data like so: response = requests.post(url, json=data) – Mark Chorley Jan 12 '18 at 14:58
118

Using requests and json makes it simple.

  1. Call the API
  2. Assuming the API returns a JSON, parse the JSON object into a Python dict using json.loads function
  3. Loop through the dict to extract information.

Requests module provides you useful function to loop for success and failure.

if(Response.ok): will help help you determine if your API call is successful (Response code - 200)

Response.raise_for_status() will help you fetch the http code that is returned from the API.

Below is a sample code for making such API calls. Also can be found in github. The code assumes that the API makes use of digest authentication. You can either skip this or use other appropriate authentication modules to authenticate the client invoking the API.

#Python 2.7.6
#RestfulClient.py

import requests
from requests.auth import HTTPDigestAuth
import json

# Replace with the correct URL
url = "http://api_url"

# It is a good practice not to hardcode the credentials. So ask the user to enter credentials at runtime
myResponse = requests.get(url,auth=HTTPDigestAuth(raw_input("username: "), raw_input("Password: ")), verify=True)
#print (myResponse.status_code)

# For successful API call, response code will be 200 (OK)
if(myResponse.ok):

    # Loading the response data into a dict variable
    # json.loads takes in only binary or string variables so using content to fetch binary content
    # Loads (Load String) takes a Json file and converts into python data structure (dict or list, depending on JSON)
    jData = json.loads(myResponse.content)

    print("The response contains {0} properties".format(len(jData)))
    print("\n")
    for key in jData:
        print key + " : " + jData[key]
else:
  # If response code is not ok (200), print the resulting http error code with description
    myResponse.raise_for_status()
HVS
  • 2,427
  • 3
  • 21
  • 19
  • 6
    Last portion with iteration over keys will not always work because JSON document may have array as a top level element. So, it would be an error to try to get `jData[key]` – Denis The Menace Jun 28 '16 at 11:25
  • @DenisTheMenace if it is an array, how would I loop around it? – qasimalbaqali Mar 12 '17 at 19:51
  • @qasimalbaqali the same way you loop over dictionary. But array elements will be simply `jData`, not `jData[key]` – Denis The Menace Mar 13 '17 at 15:41
  • Sidenote: If your API returns a large JSON response, you can pretty print it like this: `print(json.dumps(jData, indent=4, sort_keys=True))` – Marco Nov 01 '17 at 13:15
  • 2
    Under python3, the following was spit out 'JSON must be str not bytes'. This is fixed by decoding the output, i.e. json.loads(myResponse.content.decode('utf-8')). Also you should wrap key and jData key with str() so when the RESTful API returns integers, it doesn't complain. – Mirkules Jun 08 '18 at 06:24
13

So you want to pass data in body of a GET request, better would be to do it in POST call. You can achieve this by using both Requests.

Raw Request

GET http://ES_search_demo.com/document/record/_search?pretty=true HTTP/1.1
Host: ES_search_demo.com
Content-Length: 183
User-Agent: python-requests/2.9.0
Connection: keep-alive
Accept: */*
Accept-Encoding: gzip, deflate

{
  "query": {
    "bool": {
      "must": [
        {
          "text": {
            "record.document": "SOME_JOURNAL"
          }
        },
        {
          "text": {
            "record.articleTitle": "farmers"
          }
        }
      ],
      "must_not": [],
      "should": []
    }
  },
  "from": 0,
  "size": 50,
  "sort": [],
  "facets": {}
}

Sample call with Requests

import requests

def consumeGETRequestSync():
data = '{
  "query": {
    "bool": {
      "must": [
        {
          "text": {
            "record.document": "SOME_JOURNAL"
          }
        },
        {
          "text": {
            "record.articleTitle": "farmers"
          }
        }
      ],
      "must_not": [],
      "should": []
    }
  },
  "from": 0,
  "size": 50,
  "sort": [],
  "facets": {}
}'
url = 'http://ES_search_demo.com/document/record/_search?pretty=true'
headers = {"Accept": "application/json"}
# call get service with headers and params
response = requests.get(url,data = data)
print "code:"+ str(response.status_code)
print "******************"
print "headers:"+ str(response.headers)
print "******************"
print "content:"+ str(response.text)

consumeGETRequestSync()
gvir
  • 256
  • 3
  • 4
12

Below is the program to execute the rest api in python-

import requests
url = 'https://url'
data = '{  "platform": {    "login": {      "userName": "name",      "password": "pwd"    }  } }'
response = requests.post(url, data=data,headers={"Content-Type": "application/json"})
print(response)
sid=response.json()['platform']['login']['sessionId']   //to extract the detail from response
print(response.text)
print(sid)
Shashank G
  • 912
  • 1
  • 16
  • 26
  • Not work for me. Instead I use the advice here. Seems work: https://realpython.com/api-integration-in-python/ – KWC Oct 11 '22 at 09:33
0

For now, httpx is a better option than requests. It has broadly request-compatible API, supports async, HTTP/2 and has a good performance. It is used by, for example, python-telegram-bot - a popular client for Telegram bots.

import httpx
url = 'http://ES_search_demo.com/document/record/_search?pretty=true'
data = {
  "query": {
    "bool": {
      "must": [
        {
          "text": {
            "record.document": "SOME_JOURNAL"
          }
        },
        {
          "text": {
            "record.articleTitle": "farmers"
          }
        }
      ],
      "must_not": [],
      "should": []
    }
  },
  "from": 0,
  "size": 50,
  "sort": [],
  "facets": {}
}
response = httpx.post(url, json=data)
print(response.json())

However, if you want to request Elasticsearch, it's better to use specific elasticsearch API clients: elasticsearch or high-level elasticsearch-dsl

Nokados
  • 191
  • 1
  • 5