Calling a SOAP service, and why REST usually wins

The raw HTTP request a SOAP call produces, how faults are reported, and an honest comparison with REST today.

The call on the wire

curl -sS https://api.example.com/quotes \
  -H 'Content-Type: text/xml; charset=utf-8' \
  -H 'SOAPAction: "urn:example:quotes/GetQuote"' \
  --data-binary @request.xml
import requests

envelope = """<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
               xmlns:q="urn:example:quotes">
  <soap:Body>
    <q:GetQuote><q:symbol>AAPL</q:symbol></q:GetQuote>
  </soap:Body>
</soap:Envelope>"""

r = requests.post(
    "https://api.example.com/quotes",
    data=envelope.encode("utf-8"),
    headers={
        "Content-Type": "text/xml; charset=utf-8",
        "SOAPAction": '"urn:example:quotes/GetQuote"',
    },
    timeout=30,
)
r.raise_for_status()

# a SOAP fault comes back with HTTP 500 and a structured body
if "Fault" in r.text:
    raise RuntimeError(r.text)
  • The XML declaration matters: send UTF-8 and state the encoding in both the XML header and Content-Type.
  • SOAPAction is required by many SOAP 1.1 servers and is still read by some 1.2 stacks for routing.
  • Success can arrive with HTTP 200 while the body still contains a fault, so check the body, not only the status code.
  • Timeouts and retries need care: a retried request may be processed twice unless the service exposes an idempotency key in the header.

SOAP versus REST

DimensionSOAPREST
PayloadXML onlyJSON in practice, any media type by design
ContractFormal WSDL, machine-readableOpenAPI usually, often informal
TransportAny, in practice HTTPHTTP verbs and status codes
SecurityWS-Security, signed headersTLS plus OAuth 2 or API keys
ToolingGenerated clients, heavyweightcurl, any HTTP client, light SDKs
Browser supportAwkward: strict CORS, XML parsingNative
Good fitRegulated, transactional, long-lived contractsPublic APIs, mobile, most new services

REST wins for new work because it leans on what HTTP already gives you — methods, status codes, caching, proxies — and because JSON is cheaper to produce, parse and debug. SOAP keeps its ground where an industry body mandates it, where WS-Security or reliable messaging is contractually required, or where a WSDL-driven client is the whole point of the integration.

💡
Most systems end up with both: a SOAP adapter at the edge of the legacy estate, a modern JSON API in front of it. Wrapping rather than rewriting is normally the cheaper and lower-risk path.

FAQ

Do I need a SOAP library?
Not always, but usually yes. A hand-built request is fine for a single known call; for a real integration, generate a client from the WSDL so argument types, namespaces and faults are handled for you.
How do I debug a failed SOAP call?
Log the complete request and response envelopes including headers. Most failures are a namespace mismatch, a missing SOAPAction, a wrong element order that the XSD requires, or a fault body hidden behind an HTTP 500.

WSDL contracts and code generation What SOAP is and how the envelope works

Last refreshed 2026-09-18.