How To Call A SOAP API Using Python Posted in Design J Simpson March 19, 2024 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. Also read: Protecting SOAP Against OWASP’s Top Ten API Risks 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. The latest API insights straight to your inbox