Skip to content
SOAP APIs: Building and Consuming XML Web Services

SOAP APIs: Building and Consuming XML Web Services

DodaTech Updated Jun 20, 2026 8 min read

SOAP (Simple Object Access Protocol) is an XML-based protocol for web services that provides standardized message formats, error handling, and security. While REST dominates modern web APIs, SOAP remains essential in enterprise systems — banking, healthcare, telecom, and government.

Learning Path

    flowchart LR
  A["XPath Functions<br/>Reference"] --> B["SOAP APIs<br/>XML Web Services"]
  B --> C["XML Validation<br/>DTD & XSD"]
  C --> D["REST APIs<br/>Comparison"]
  style B fill:#f90,color:#fff,stroke-width:2px
  
What you’ll learn: SOAP protocol structure, WSDL, envelope/header/body, building SOAP services in Python and Java, error handling with SOAP faults, and SOAP vs REST comparison. Why it matters: SOAP powers 60%+ of enterprise web services in banking, healthcare (HL7), and government systems. Understanding SOAP is essential for enterprise integration work. Real-world use: Durga Antivirus Pro integrates with enterprise security systems via SOAP — receiving threat intelligence feeds and reporting scan results through standardized SOAP endpoints.

SOAP Message Structure

A SOAP message is an XML document with three main parts:

<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope
    xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

    <soap:Header>
        <!-- Optional: authentication, routing, transaction info -->
        <auth:Authentication xmlns:auth="http://doda.tech/auth">
            <auth:ApiKey>sk-live-example-key</auth:ApiKey>
        </auth:Authentication>
    </soap:Header>

    <soap:Body>
        <!-- The actual request or response data -->
        <GetPrice xmlns="http://doda.tech/ pricing">
            <ProductId>BOOK-001</ProductId>
            <Currency>USD</Currency>
        </GetPrice>
    </soap:Body>

</soap:Envelope>

Envelope

The root element. It defines the SOAP namespace and contains the entire message.

Header (Optional)

Contains metadata: authentication tokens, transaction IDs, routing information. Not every SOAP message needs a header.

Body (Required)

Contains the actual request or response data. This is the payload.

Fault (Error Response)

When an error occurs, the body contains a <soap:Fault> element:

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
    <soap:Body>
        <soap:Fault>
            <faultcode>soap:Client</faultcode>
            <faultstring>Invalid product ID: BOOK-999</faultstring>
            <detail>
                <error xmlns="http://doda.tech/errors">
                    <code>PRODUCT_NOT_FOUND</code>
                    <message>No product found with ID: BOOK-999</message>
                </error>
            </detail>
        </soap:Fault>
    </soap:Body>
</soap:Envelope>

WSDL — Web Services Description Language

WSDL is an XML document that describes a SOAP service — its operations, inputs, outputs, and endpoint locations.

<?xml version="1.0" encoding="UTF-8"?>
<definitions name="PriceService"
    targetNamespace="http://doda.tech/price-service"
    xmlns="http://schemas.xmlsoap.org/wsdl/"
    xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
    xmlns:tns="http://doda.tech/price-service"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema">

    <!-- Data types used in messages -->
    <types>
        <xsd:schema targetNamespace="http://doda.tech/price-service">
            <xsd:element name="GetPriceRequest">
                <xsd:complexType>
                    <xsd:sequence>
                        <xsd:element name="ProductId" type="xsd:string"/>
                        <xsd:element name="Currency" type="xsd:string"/>
                    </xsd:sequence>
                </xsd:complexType>
            </xsd:element>
            <xsd:element name="GetPriceResponse">
                <xsd:complexType>
                    <xsd:sequence>
                        <xsd:element name="Price" type="xsd:decimal"/>
                        <xsd:element name="Currency" type="xsd:string"/>
                    </xsd:sequence>
                </xsd:complexType>
            </xsd:element>
        </xsd:schema>
    </types>

    <!-- Messages: what flows in and out -->
    <message name="GetPriceInput">
        <part name="parameters" element="tns:GetPriceRequest"/>
    </message>
    <message name="GetPriceOutput">
        <part name="parameters" element="tns:GetPriceResponse"/>
    </message>

    <!-- Port type: the operations -->
    <portType name="PricePortType">
        <operation name="GetPrice">
            <input message="tns:GetPriceInput"/>
            <output message="tns:GetPriceOutput"/>
            <fault name="InvalidProduct" message="tns:InvalidProductFault"/>
        </operation>
    </portType>

    <!-- Binding: protocol and format -->
    <binding name="PriceBinding" type="tns:PricePortType">
        <soap:binding style="document"
            transport="http://schemas.xmlsoap.org/soap/http"/>
        <operation name="GetPrice">
            <soap:operation soapAction="getPrice"/>
            <input>
                <soap:body use="literal"/>
            </input>
            <output>
                <soap:body use="literal"/>
            </output>
        </operation>
    </binding>

    <!-- Service: where it lives -->
    <service name="PriceService">
        <port name="PricePort" binding="tns:PriceBinding">
            <soap:address location="http://api.doda.tech/soap/price"/>
        </port>
    </service>
</definitions>

Building a SOAP Service in Python

Using the zeep library (the most popular Python SOAP client):

Server (using spyne):

from spyne import Application, rpc, ServiceBase, Decimal, Unicode
from spyne.protocol.soap import Soap11
from spyne.server.wsgi import WsgiApplication

class PriceService(ServiceBase):
    @rpc(Unicode, Unicode, _returns=Decimal)
    def get_price(ctx, product_id, currency):
        """Get product price. Mock implementation."""
        prices = {
            "BOOK-001": 12.99,
            "BOOK-002": 14.99,
            "BOOK-003": 9.99,
        }
        if product_id not in prices:
            raise ValueError(f"Product {product_id} not found")
        
        # Currency conversion (simplified)
        rate = {"USD": 1.0, "GBP": 0.79, "EUR": 0.92}
        return round(prices[product_id] * rate.get(currency, 1.0), 2)

# Create SOAP application
application = Application(
    [PriceService],
    tns="http://doda.tech/price-service",
    in_protocol=Soap11(),
    out_protocol=Soap11(),
)

if __name__ == "__main__":
    from wsgiref.simple_server import make_server
    server = make_server("0.0.0.0", 8000, WsgiApplication(application))
    print("SOAP service running at http://localhost:8000?wsdl")
    print("WSDL available at http://localhost:8000/?wsdl")
    server.serve_forever()

Client (using zeep):

from zeep import Client

def call_soap_service(product_id, currency="USD"):
    """Call a SOAP web service."""
    try:
        client = Client("http://localhost:8000/?wsdl")
        result = client.service.get_price(product_id, currency)
        print(f"Price for {product_id} in {currency}: ${result}")
        return result
    except Exception as e:
        print(f"SOAP Error: {e}")
        return None

# Test
call_soap_service("BOOK-001", "USD")
call_soap_service("BOOK-002", "GBP")
call_soap_service("BOOK-999", "USD")

Expected output:

Price for BOOK-001 in USD: $12.99
Price for BOOK-002 in GBP: $11.84
SOAP Error: Product BOOK-999 not found

Building a SOAP Service in Java (JAX-WS)

import javax.jws.WebService;
import javax.jws.WebMethod;
import javax.jws.soap.SOAPBinding;
import javax.xml.ws.Endpoint;

@WebService
@SOAPBinding(style = SOAPBinding.Style.DOCUMENT)
public class PriceService {

    @WebMethod
    public double getPrice(String productId, String currency) {
        java.util.Map<String, Double> prices = new java.util.HashMap<>();
        prices.put("BOOK-001", 12.99);
        prices.put("BOOK-002", 14.99);
        prices.put("BOOK-003", 9.99);

        if (!prices.containsKey(productId)) {
            throw new RuntimeException("Product " + productId + " not found");
        }

        java.util.Map<String, Double> rates = new java.util.HashMap<>();
        rates.put("USD", 1.0);
        rates.put("GBP", 0.79);
        rates.put("EUR", 0.92);

        double price = prices.get(productId);
        double rate = rates.getOrDefault(currency, 1.0);
        return Math.round(price * rate * 100.0) / 100.0;
    }

    public static void main(String[] args) {
        Endpoint.publish("http://localhost:8080/price", new PriceService());
        System.out.println("SOAP service running at http://localhost:8080/price?wsdl");
    }
}

SOAP vs REST

AspectSOAPREST
ProtocolXML-based protocolArchitectural style (uses HTTP)
Message FormatXML onlyJSON, XML, YAML, plain text
StateStateless or statefulAlways stateless
SecurityWS-Security (built-in)HTTPS, OAuth, API keys
Error HandlingSOAP Faults (standardized)HTTP status codes (200, 400, 500)
CachingNot cacheable by defaultCacheable via HTTP headers
PerformanceSlower (XML parsing overhead)Faster (lightweight, JSON)
ToolingWSDL, code generation from contractOpenAPI, Swagger, code-first
When to UseEnterprise, banking, healthcare, governmentPublic APIs, mobile, microservices

Tools for SOAP Development

SoapUI

The industry-standard SOAP testing tool:

# Install SoapUI (or use the free version)
# Create a new SOAP project from WSDL
# Generate test requests
# Run load tests
# Mock services for testing

Postman SOAP Support

Postman supports SOAP since version 8.0:

  1. Create a new request
  2. Set the URL to the SOAP endpoint
  3. Set method to POST
  4. Set Content-Type: text/xml; charset=utf-8
  5. Add SOAP action header: SOAPAction: getPrice
  6. Paste the SOAP envelope as raw XML body
  7. Send and see the response

cURL for SOAP

curl -X POST http://api.doda.tech/soap/price \
  -H "Content-Type: text/xml; charset=utf-8" \
  -H "SOAPAction: getPrice" \
  -d '<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <GetPrice xmlns="http://doda.tech/price-service">
      <ProductId>BOOK-001</ProductId>
      <Currency>USD</Currency>
    </GetPrice>
  </soap:Body>
</soap:Envelope>'

Expected output:

<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <GetPriceResponse xmlns="http://doda.tech/price-service">
      <Price>12.99</Price>
      <Currency>USD</Currency>
    </GetPriceResponse>
  </soap:Body>
</soap:Envelope>

SOAP Error Handling (Faults)

SOAP defines a standardized error format:

from zeep import Client
from zeep.exceptions import Fault

def robust_soap_call():
    """Handle SOAP faults gracefully."""
    try:
        client = Client("http://localhost:8000/?wsdl")
        result = client.service.get_price("INVALID", "USD")
        return result
    except Fault as fault:
        print(f"SOAP Fault Code: {fault.code}")
        print(f"SOAP Fault String: {fault.message}")
        if fault.detail:
            print(f"Detail: {fault.detail}")
    except Exception as e:
        print(f"Connection or parsing error: {e}")

robust_soap_call()

Expected output:

SOAP Fault Code: soap:Server
SOAP Fault String: Product INVALID not found
Detail: None

Common SOAP Errors

  1. WSDL not accessible — The client can’t download the WSDL. Ensure the ?wsdl endpoint is accessible and returns valid XML. Firewall rules often block WSDL access.
  2. SOAPAction header missing or wrong — The SOAPAction HTTP header tells the server which operation to invoke. An empty or wrong header returns a 500 error. Check the WSDL’s soapAction attribute.
  3. Namespace mismatch — The XML namespaces in the request must exactly match those in the WSDL. A typo in xmlns="http://doda.tech/price-service" causes a validation error.
  4. Content-Type header wrong — SOAP requires Content-Type: text/xml; charset=utf-8. Using application/json or text/plain causes the server to reject the request.
  5. Complex type serialization errors — When the WSDL defines complex types, the XML structure must match exactly. Missing elements or wrong element order causes deserialization failures.
  6. WS-Security misconfiguration — Enterprise SOAP services often require WS-Security headers. Missing or expired authentication tokens result in authentication faults.
  7. Connection timeout — SOAP can be slow (XML parsing overhead). Client timeout settings should be generous (30-60 seconds). Tight timeouts cause intermittent failures.

Practice Questions

1. What are the three main parts of a SOAP envelope? Header (optional metadata), Body (required payload), and Fault (error response). These are wrapped in the Envelope root element.

2. What is WSDL and why is it important? WSDL (Web Services Description Language) is an XML document that describes the service — its operations, input/output types, and endpoint URL. It enables code generation for clients without manual parsing.

3. How does SOAP error handling differ from REST? SOAP uses a standardized <soap:Fault> element with faultcode, faultstring, and detail. REST uses HTTP status codes (400, 404, 500) with custom error JSON/XML in the body.

4. When should you choose SOAP over REST? For enterprise systems requiring built-in security (WS-Security), reliable messaging (WS-ReliableMessaging), and formal contracts (WSDL). Also for systems where XML is the standard format (banking, healthcare).

5. Challenge: Build a SOAP calculator Create a SOAP service with operations for add, subtract, multiply, and divide. Include proper fault handling for division by zero. Write both the server (using spyne or similar) and a client that demonstrates all operations.

Mini Project: SOAP Client Tester

from zeep import Client, Settings
from zeep.exceptions import Fault, TransportError
import json

class SOAPClientTester:
    """Test SOAP services with configurable parameters."""
    
    def __init__(self, wsdl_url):
        settings = Settings(strict=False, xml_huge_tree=True)
        self.client = Client(wsdl_url, settings=settings)
        self.service = self.client.service
        print(f"Connected to: {wsdl_url}")
        print(f"Services: {self.client.wsdl.services}")
        print(f"Operations: {list(self.service._operations.keys())}")
    
    def call(self, operation, **kwargs):
        """Call a SOAP operation and handle errors."""
        try:
            result = getattr(self.service, operation)(**kwargs)
            print(f"✓ {operation}{kwargs} = {result}")
            return result
        except Fault as f:
            print(f"✗ {operation}{kwargs} = FAULT [{f.code}]: {f.message}")
        except TransportError as e:
            print(f"✗ {operation}{kwargs} = TRANSPORT ERROR: {e}")
        return None
    
    def get_operation_details(self, operation):
        """Print operation input/output types."""
        op = self.client.wsdl.services[0].ports[0].operations[operation]
        print(f"\nOperation: {operation}")
        print(f"  Input: {op.input.signature()}")
        print(f"  Output: {op.output.signature()}")

# Test with public SOAP service
tester = SOAPClientTester("http://www.dneonline.com/calculator.asmx?wsdl")
tester.get_operation_details("Add")
tester.call("Add", intA=10, intB=5)
tester.call("Subtract", intA=10, intB=5)
tester.call("Multiply", intA=10, intB=5)
tester.call("Divide", intA=10, intB=0)  # Should trigger fault

FAQ

Is SOAP still used in 2026?
Yes. SOAP powers critical enterprise systems — banking transactions, healthcare data exchange (HL7 v3), payment gateways (PayPal), travel booking (Sabre, Amadeus), and government systems. It’s not going away.
What’s the difference between SOAP 1.1 and 1.2?
SOAP 1.2 uses a different namespace (http://www.w3.org/2003/05/soap-envelope), dropped some restrictions (like the body not being a single element), and better integrated with HTTP extensions.
Can SOAP use JSON?
Standard SOAP uses XML. Some extensions (SOAP-over-JSON) exist but aren’t widely adopted. If JSON is required, REST is the better choice.
Is WSDL auto-generated?
Yes. Modern frameworks (JAX-WS in Java, spyne/wrapt in Python, WCF in .NET) generate WSDL automatically from code. You typically don’t write WSDL by hand.
How does SOAP handle binary data?
SOAP uses MTOM (Message Transmission Optimization Mechanism) or SwA (SOAP with Attachments) to send binary data efficiently as MIME attachments rather than base64-encoded text.

Related Tutorials


Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro. Updated 2026-06-20.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro