Simple Object Access Protocol, or SOAP, was the new hotness in web service technology…some 15 or 20 years ago. It was built around XML, Web Service Definition Language (WSDL), XML namespaces and other complex ideas.

Today’s web service standard is Representational state transfer (REST)–a much simpler approach to data transmission over the web. Instead of trading around clunky XML files, REST APIs typically leverage sleeker JSON (JavaScript Object Notation) documents in their communication.

But some things never die and, recently, I found myself elbow-deep into a number of SOAP APIs while trying to pull data from a vendor product. I wrote a Python client to interface with those APIs. While Python has a number of packages designed to work with the technology, I wanted to stick with just the requests package to keep my dependencies minimal. Ultimately, my client worked well and I wanted to share a few tidbits here that I learned along the way to get my requests code to successfully call SOAP web services.

1. The header can be tricky

Getting your header right is critical to successful service calls. I found two header elements essential for my code: SOAPAction and Content-Type. It was important that I set SOAPAction to a url corresponding with the particular web method I wished to call. The vendor documentation was pretty important here to determine what that url should be.

What’s interesting about Content-Type is that the web is full of valid suggestions for the proper value: text/xml and application/soap+xml are two that I’ve seen bandied about. In my case, neither value worked. Again from the vendor documentation, the value that made my calls work was application/x-www-form-urlencoded. So, my header dictionary looked roughly like this:

headers = {'SOAPAction': 'http://somesite.com/webservices/SomeMethod', 'Content-Type': 'application/x-www-form-urlencoded'}

2. The post data doesn’t necessarily need to be XML

Crazy notion, right? Posting non-XML to a SOAP API? Early on in my work, I kept trying to format all my post arguments into a single XML document and tried to push that document to the web method with my requests call, but the code would never work. At some point, I stumbled upon a forum or discussion thread where one of the participants posted code that actually used a dictionary for his post data object–what you would normally do with a REST API. I was taken aback but gave it a go and, to my astonishment, it worked! Some web methods required simple parameters like strings and integers, ready made for Python dictionaries. A few did have a parameter or two of XML. For those, I simply had to push a string representation of a properly formatted XML document. My code looked something like this:

import requests

str_xml = '<some_doc><some_elem>1</some_elem></some_doc>'
post_data = {'token': 'blah', 'search_xml': str_xml}
ws_url = 'https://somesite.com/ws/something.asmx/SomeMethod'
resp = requests.post(ws_url, data=post_data, headers=headers)

3. Parsing the XML response can be tricky

I “believe” the most appropriate way to deal with XML responses in the response object is through the content property. But, since the response is supposed to be XML, I wanted to run the content through ElementTree to get a proper XML document I could more easily process. In my early attempts, I passed the content value to ElementTree’s fromstring function to get back a proper XML document that I can process like any other XML document. Or so I thought.

The rub is that fromstring returns an XML element, not an XML document. You have to add one more line, a call to the ElementTree constructor itself, you get the proper XML document object you can use in the rest of your code. My response processing code then looked like this:

import xml.etree.ElementTree as ET


resp = requests.post(ws_url, data=post_data, headers=headers)

resp_elem = ET.fromstring(resp.content)
resp_doc = ET.ElementTree(resp_elem)

# now, you can use functions like find and findall with the resp_doc object

So, the next time you find yourself having to work with SOAP APIs–and hopefully you don’t–there are some handy tips and tricks to consider.