How-To-Call-A-SOAP-API-Using-Python

How To Call A SOAP API Using Python

Posted in

Before there was REST, there was SOAP. Based on XML, SOAP is widely responsible for the rise of web services, setting a precedent for APIs. While SOAP APIs may not be as prevalent as other architectures like REST, plenty of developers still prefer SOAP for its structure, datatype control, and defined standards. Strong typing also makes SOAP useful for situations requiring robust API security.

As an additional benefit, SOAP can be invoked from virtually every programming language. To give you an idea of how you might integrate SOAP APIs into your workflow, we’re going to show you how to make a SOAP API call within Python. First, we’ll start with some background to help you decide between SOAP and REST.

What Is SOAP?

SOAP stands for Simple Object Access Protocol. It started out as a messaging standard created by the World Wide Web Consortium (WWWC). It uses an XML format to annotate its request and response messages using the XML schema. As of 2020, 23% of APIs were using SOAP. Not only is it a powerful architecture in its own right, but it’s also an important antecedent to APIs in general.

SOAP often follows the Remote Procedure Call (RPC) protocol, where a function or a method is passed a parameter and then returns a result. Before SOAP, many RPC requests needed to be implemented in specific programming languages or environments. Many of the earliest RPC requests were written in C, for example. This could cause problems for communication and collaboration, as an RPC request written in C might not be able to communicate with one written in Java.

How To Call SOAP APIs

Nearly every programming language has a library for working with SOAP. While making SOAP calls without using a library is possible, it’s often not practical. SOAP messages are large and bulky due to their reliance on XML SOAP libraries to handle that abstraction for you.

Here’s an example of requesting user info from an imaginary API using the Zeep library in Python.

from zeep import Client

client = Client('https://www.example.com/exampleapi')
result = client.service.GetUser(123) # request user with ID 123

name = result['Username']

A diagram of the SOAP call would look like this:

<?xml version="1.0"?>
<soap:Envelope xmlns:soap="https://www.w3.org/2003/05/soap-envelope">
  <soap:Header>
  </soap:Header>
  <soap:Body>
    <m:GetUser>
      <m:UserId>123</m:UserId>
    </m:GetUser>
  </soap:Body>
</soap:Envelope>

The returned SOAP message looks something like this.

<?xml version="1.0"?>

<soap:Envelope
xmlns:soap="https://www.w3.org/2003/05/soap-envelope/"
soap:encodingStyle="https://www.w3.org/2003/05/soap-encoding">

<soap:Body>
  <m:GetUserResponse>
    <m:Username>Tony Stark</m:Username>
  </m:GetUserResponse>
</soap:Body>

</soap:Envelope>

If you were to make all of the SOAP API calls using HTTP requests rather than a library, it could look something like this:

import requests
req_headers = {"content-type": "text/xml"}
req_body =  "<?xml version=\"1.0\"?>"
req_body += "<soap:Envelope xmlns:soap=\"https://www.w3.org/2003/05/soap-envelope\">"
req_body += "<soap:Header></soap:Header>"
req_body += "<soap:Body>"
req_body += "<m:GetUser>"
req_body += "<m:UserId>123</m:UserId>"
req_body += "</m:GetUser>"
req_body += "v/soap:Body>"
req_body += "</soap:Envelope>"
response = requests.post(
  "https://www.example.com/exampleapi",
  data=req_body,
  headers=req_headers
)

Contents Of A SOAP Message

These SOAP API requests give you an example of the expected format. Let’s take a moment and look at each component.

soap:Envelope

SOAP uses XML, but it needs a way to differentiate between assets and other XML documents. The soap:Envelope signifies that the XML is SOAP. The soap:Envelope tag also requires a Namespace attribute. It can also include an encodingStyle attribute. All of the other SOAP components are returned inside the envelope.

soap:Header

Headers in SOAP are optional, but you can extend SOAP’s extensibility via SOAP Modules using soap:Header. These modules can be either optional or required. If they’re mandatory, the mustUnderstand attribute must be set to true.

soap:Body

The bulk of the SOAP response will be wrapped inside of the soap:Body tag. This response can be customized using Namespaces, but it doesn’t have to be. By default, soap:Body will return the procedure’s name, parameters, and any returned data.

soap:Fault

The soap:Fault tag returns any errors. Some examples of error codes in SOAP include:

  • Code: a machine-readable error code
  • Reason: a human-readable error reason
  • Node: the node where the SOAP error occurred
  • Role: What role the node performs where the SOAP error occurred
  • Detail: application-specific error details, including both human and machine-readable data

SOAP vs. REST

REST and SOAP have a lot in common. There are fundamental differences, however. Understanding the similarities and differences between SOAP and REST will give you a greater understanding of each architecture. It should also help you know when to use REST vs. SOAP and vice versa.

The biggest difference between SOAP and REST is that SOAP is highly structured, whereas REST and RESTful APIs are more flexible.

Soap Vs. Rest

SOAP REST
An XML-based message protocol An architectural-style protocol
Uses WSDL for communication Uses XML and JSON for communication
Results are not human readable Results are human readable
Uses HTTP for transfers, but can Transfers are HTTP only
also use FTP or SMTP
Difficult to implement in JavaScript Easy to use in JavaScript
Performance is less efficient than REST Is much more efficient than SOAP

Generally speaking, using a REST API will be a better pick when you need an API that’s going to be scalable or use standard CRUD commands. SOAP APIs might be a better fit when you need your transactions to be utterly secure.

SOAP API Use Case

Let’s finish with an example SOAP API call to give you an idea of what that might look like, to give you a better idea of when to use SOAP.

POST /Quotation HTTP/1.0
Host: www.xyz.org
Content-Type: text/xml; charset = utf-8
Content-Length: nnn

<?xml version = "1.0"?>
<SOAP-ENV:Envelope
   xmlns:SOAP-ENV = "http://www.w3.org/2001/12/soap-envelope"
   SOAP-ENV:encodingStyle = "http://www.w3.org/2001/12/soap-encoding">

   <SOAP-ENV:Body xmlns:m = "http://www.xyz.org/quotations">
      <m:GetQuotation>
         <m:QuotationsName>MiscroSoft</m:QuotationsName>
      </m:GetQuotation>
   </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

As you can see, this SOAP API call makes a POST request to the /Quotation endpoint of the website xyz.org. It also specifies which version of HTTP it’s using and what format of response it is expecting.

This should also give you some ideas of possible applications for SOAP APIs. Since you’re only accepting results in XML using the character set UTF-8, for example, you could automatically block and and all results with the slightest variation. A SOAP API’s usefulness may be limited and highly specific, but it’s still worth familiarizing yourself to have in your toolkit.

How To Call A SOAP API Using Python

Now, let’s see SOAP APIs in action. We’re going to write a Python script that will call a SOAP API using the requests library. To start, open your text editor of choice and input the following code:

import requests
# SOAP request URL
url = "http://webservices.oorsprong.org/websamples.countryinfo/CountryInfoService.wso"

# structured XML
payload = """<?xml version=\"1.0\" encoding=\"utf-8\"?>
            <soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">
                <soap:Body>
                    <CountryIntPhoneCode xmlns=\"http://www.oorsprong.org/websamples.countryinfo\">
                        <sCountryISOCode>IN</sCountryISOCode>
                    </CountryIntPhoneCode>
                </soap:Body>
            </soap:Envelope>"""
# headers
headers = {
    'Content-Type': 'text/xml; charset=utf-8'
}
# POST request
response = requests.request("POST", url, headers=headers, data=payload)

# prints the response
print(response.text)
print(response)

This code starts by importing the requests library. It then defines the SOAP URL. The following section is the most important, though, as it specifies the SOAP API formatting using the soap:Body tag.

Last but not least, the Python script defines the headers in JSON.

Save that file and run it. You should see a result like the following:

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <m:CountryIntPhoneCodeResponse xmlns:m="http://www.oorsprong.org/websamples.countryinfo">
      <m:CountryIntPhoneCodeResult>91</m:CountryIntPhoneCodeResult>
    </m:CountryIntPhoneCodeResponse>
  </soap:Body>
</soap:Envelope>
<Response [200]>

Method 2: Calling SOAP APIs In Python Using Zeep

We’ll finish up with an example of one more method for calling a SOAP API in Python. This time we’re going to use the Zeep library.

Start by making sure you have Zeep installed.

Pip3 install Zeep

Once Zeep is installed, create a new file in your text editor and input the following code.

import zeep

# set the WSDL URL
wsdl_url = "http://webservices.oorsprong.org/websamples.countryinfo/CountryInfoService.wso?WSDL"

# set method URL
method_url = "http://webservices.oorsprong.org/websamples.countryinfo/CountryIntPhoneCode"

# set service URL
service_url = "http://webservices.oorsprong.org/websamples.countryinfo/CountryInfoService.wso"

# create the header element
header = zeep.xsd.Element(
    "Header",
    zeep.xsd.ComplexType(
        [
            zeep.xsd.Element(
                "{http://www.w3.org/2005/08/addressing}Action", zeep.xsd.String()
            ),
            zeep.xsd.Element(
                "{http://www.w3.org/2005/08/addressing}To", zeep.xsd.String()
            ),
        ]
    ),
)
# set the header value from header element
header_value = header(Action=method_url, To=service_url)

# initialize zeep client
client = zeep.Client(wsdl=wsdl_url)

# set country code for India
country_code = "IN"

# make the service call
result = client.service.CountryIntPhoneCode(
    sCountryISOCode=country_code,
    _soapheaders=[header_value]
)
# print the result
print(f"Phone Code for {country_code} is {result}")

# set country code for United States
country_code = "US"

# make the service call
result = client.service.CountryIntPhoneCode(
    sCountryISOCode=country_code,
    _soapheaders=[header_value]
)

# POST request
response = client.service.CountryIntPhoneCode(
    sCountryISOCode=country_code,
    _soapheaders=[header_value]
)

# print the result
print(f"Phone Code for {country_code} is {result}")
print(response)

Using the Zeep library, you can easily get the country phone codes of as many countries as you like. It then inserts the country code and phone code into the sentence “Phone Code for {country_code} is {result}”).”

Once you run your new Python script, you should get a result like the following.

Phone Code for IN is 91
Phone Code for US is 1

Final Thoughts: Calling a SOAP API Using Python

As we have seen, SOAP APIs are alive and well. Developers still have the need for strongly typed, secure transactions. They might not be that common, but many legacy products and services are still built around SOAP APIs. Even if it weren’t, it’d still be worthwhile to understand SOAP APIs for the role they played in the widespread adoption of APIs.

In this piece, we’ve introduced you to the SOAP API format and shown some comparisons with other protocols like REST. Then, we finished up by showing you how you can work with SOAP APIs in Python so you can try it out for yourself.