REST and SOAP are two approaches for building web APIs. REST dominates modern development, but SOAP still powers millions of enterprise integrations. This guide explains every meaningful difference with examples.
At a Glance
| Dimension | REST | SOAP |
|---|---|---|
| Full name | Representational State Transfer | Simple Object Access Protocol |
| Protocol | Architectural style (HTTP) | Protocol (HTTP, SMTP, TCP, …) |
| Message format | JSON, XML, HTML, plain text | XML only |
| WSDL/contract | Optional (OpenAPI) | Required (WSDL) |
| Statefulness | Stateless | Stateful or stateless |
| Standards | Loose (HTTP verbs + URLs) | Strict (WS-* specs) |
| Error handling | HTTP status codes | Fault element in envelope |
| Built-in security | HTTPS (+ OAuth, JWT) | WS-Security |
| Performance | Fast (lightweight payloads) | Slower (verbose XML) |
| Tooling | Postman, curl, any HTTP client | WSDL-aware tools (SoapUI) |
| Best for | Web/mobile APIs, microservices | Enterprise, banking, legacy |
What Is REST?
REST (Representational State Transfer) is an architectural style defined by Roy Fielding in 2000. It uses HTTP as the transport and has six constraints:
- Client–server — UI and data storage are separated
- Stateless — each request contains all context; no session state on server
- Cacheable — responses declare whether they can be cached
- Uniform interface — resources identified by URLs, manipulated via representations
- Layered system — client doesn't know if it's talking to origin or proxy
- Code on demand (optional) — server can send executable code (e.g., JavaScript)
REST Request Example
GET /users/42 HTTP/1.1
Host: api.example.com
Authorization: Bearer eyJhbGci...
Accept: application/json
HTTP/1.1 200 OK
Content-Type: application/json
{
"id": 42,
"name": "Ana Kovač",
"email": "ana@example.com"
}
REST CRUD Mapping
| Operation | HTTP Method | URL | Status Code |
|---|---|---|---|
| Create | POST | /users | 201 Created |
| Read (list) | GET | /users | 200 OK |
| Read (single) | GET | /users/42 | 200 OK |
| Update (full) | PUT | /users/42 | 200 OK |
| Update (partial) | PATCH | /users/42 | 200 OK |
| Delete | DELETE | /users/42 | 204 No Content |
What Is SOAP?
SOAP (Simple Object Access Protocol) is a protocol with a strict XML envelope structure. It was designed for enterprise systems requiring formal contracts, built-in transactions, and advanced security.
SOAP Message Structure
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:usr="http://example.com/users">
<soapenv:Header>
<wsse:Security>
<!-- WS-Security token here -->
</wsse:Security>
</soapenv:Header>
<soapenv:Body>
<usr:GetUserRequest>
<usr:UserId>42</usr:UserId>
</usr:GetUserRequest>
</soapenv:Body>
</soapenv:Envelope>
SOAP Response
<soapenv:Envelope ...>
<soapenv:Body>
<usr:GetUserResponse>
<usr:Id>42</usr:Id>
<usr:Name>Ana Kovač</usr:Name>
<usr:Email>ana@example.com</usr:Email>
</usr:GetUserResponse>
</soapenv:Body>
</soapenv:Envelope>
SOAP Fault (Error Response)
<soapenv:Body>
<soapenv:Fault>
<faultcode>soapenv:Client</faultcode>
<faultstring>User not found: 42</faultstring>
<detail>
<usr:ErrorCode>USER_NOT_FOUND</usr:ErrorCode>
</detail>
</soapenv:Fault>
</soapenv:Body>
WSDL vs OpenAPI
SOAP requires a WSDL (Web Services Description Language) contract. REST typically uses OpenAPI (formerly Swagger), though it's optional.
| Feature | WSDL (SOAP) | OpenAPI (REST) |
|---|---|---|
| Format | XML | YAML or JSON |
| Required | Yes | No (optional) |
| Code gen | Auto-generates client stubs | Auto-generates client SDKs |
| Discovery | ?wsdl endpoint | /openapi.json or /docs |
| Tooling | SoapUI, wsdl2java | Swagger UI, Postman, Redoc |
| Human-readable | Verbose, hard to read | Readable YAML |
WSDL snippet:
<wsdl:operation name="GetUser">
<wsdl:input message="tns:GetUserRequest"/>
<wsdl:output message="tns:GetUserResponse"/>
</wsdl:operation>
OpenAPI equivalent:
/users/{id}:
get:
summary: Get user by ID
parameters:
- name: id
in: path
required: true
schema:
type: integer
responses:
'200':
description: User found
content:
application/json:
schema:
$ref: '#/components/schemas/User'
Security Comparison
| Security Aspect | REST | SOAP |
|---|---|---|
| Transport security | TLS/HTTPS | TLS/HTTPS |
| Message-level security | Manual (JWE, application layer) | WS-Security (built into protocol) |
| Authentication | OAuth 2.0, JWT, API keys, Basic | WS-Security UsernameToken, SAML |
| Authorization | Custom (JWT claims, scopes) | XACML, WS-Trust |
| Integrity | HTTPS + HMAC signatures | XML Digital Signature |
| Non-repudiation | Hard to guarantee | WS-Security provides this |
| End-to-end encryption | HTTPS only (TLS terminates at proxy) | XML Encryption (survives routing) |
Key SOAP security advantage: WS-Security provides message-level security — the payload stays encrypted even if it passes through intermediaries (message brokers, ESBs). REST's HTTPS only protects the transport channel.
<!-- WS-Security header example -->
<wsse:Security>
<wsse:UsernameToken>
<wsse:Username>api-user</wsse:Username>
<wsse:Password Type="PasswordDigest">dGVzdA==</wsse:Password>
<wsse:Nonce>abc123</wsse:Nonce>
<wsu:Created>2025-07-16T10:00:00Z</wsu:Created>
</wsse:UsernameToken>
</wsse:Security>
Performance Comparison
REST is generally faster because:
- Smaller payloads — JSON is ~30–50% smaller than equivalent XML
- HTTP caching — GET responses can be cached by browsers, CDNs, proxies
- Less parsing overhead — JSON parsing is faster than XML DOM parsing
- No envelope overhead — no mandatory header/body wrapping
# Payload size comparison (same user object)
# REST JSON: 68 bytes
{"id": 42, "name": "Ana Kovač", "email": "ana@example.com"}
# SOAP XML: ~450 bytes (with envelope)
# <?xml version="1.0"?><soapenv:Envelope ...><soapenv:Body>
# <GetUserResponse><Id>42</Id><Name>Ana Kovač</Name>
# <Email>ana@example.com</Email></GetUserResponse>
# </soapenv:Body></soapenv:Envelope>
Caveat: Performance difference matters less at the network layer for large payloads. CPU cost of XML validation and signing is the bigger SOAP bottleneck.
Code Examples: Calling Both APIs
Calling a REST API (Python)
import httpx
# GET user
response = httpx.get(
"https://api.example.com/users/42",
headers={"Authorization": "Bearer eyJhbGci..."}
)
user = response.json()
print(user["name"]) # Ana Kovač
# POST create user
new_user = httpx.post(
"https://api.example.com/users",
json={"name": "Marko", "email": "marko@example.com"},
headers={"Authorization": "Bearer eyJhbGci..."}
)
print(new_user.status_code) # 201
Calling a SOAP API (Python with zeep)
from zeep import Client
from zeep.wsse.username import UsernameToken
client = Client(
wsdl="https://api.example.com/UserService?wsdl",
wsse=UsernameToken("api-user", "secret", use_digest=True)
)
# Call GetUser operation
response = client.service.GetUser(UserId=42)
print(response.Name) # Ana Kovač
# Call CreateUser operation
result = client.service.CreateUser(
Name="Marko",
Email="marko@example.com"
)
print(result.Success) # True
Calling a SOAP API (Java with JAX-WS)
// Auto-generated stub from WSDL (wsimport or wsdl2java)
UserService service = new UserService();
UserPortType port = service.getUserPort();
// Set WS-Security credentials
BindingProvider bp = (BindingProvider) port;
bp.getRequestContext().put(BindingProvider.USERNAME_PROPERTY, "api-user");
bp.getRequestContext().put(BindingProvider.PASSWORD_PROPERTY, "secret");
GetUserRequest request = new GetUserRequest();
request.setUserId(42);
GetUserResponse response = port.getUser(request);
System.out.println(response.getName()); // Ana Kovač
WS-* Standards (SOAP Extensions)
SOAP has an ecosystem of WS-* (Web Services) specifications:
| Standard | Purpose |
|---|---|
| WS-Security | Message-level auth, encryption, signing |
| WS-ReliableMessaging | Guaranteed message delivery |
| WS-AtomicTransaction | Distributed ACID transactions |
| WS-Addressing | Endpoint routing, async reply-to |
| WS-Policy | Machine-readable service capabilities |
| WS-Trust | Token issuance (STS) |
| WS-Federation | Identity federation across domains |
| WS-BPEL | Business process orchestration |
REST has no equivalent built-in standards — you implement these at the application layer.
Where REST Wins
| Scenario | Why REST |
|---|---|
| Public APIs (Twitter, GitHub, Stripe) | Simple HTTP, any client language |
| Mobile and web apps | Low payload = faster on mobile networks |
| Microservices | Lightweight, easy to deploy, HTTP-native |
| Developer experience | Easy to test with curl, Postman, browser |
| Caching | HTTP GET responses cache natively |
| Serverless / edge functions | No heavy SOAP client library needed |
| Rapid prototyping | No WSDL to write before coding |
| JavaScript frontends | JSON parses natively |
Where SOAP Wins
| Scenario | Why SOAP |
|---|---|
| Banking / financial systems | WS-Security, non-repudiation, atomicity |
| Healthcare (HL7, HIPAA) | Required audit trail, WS-ReliableMessaging |
| Enterprise ERP/SAP integration | SAP SOAP APIs are standard |
| Distributed transactions | WS-AtomicTransaction across services |
| Message-level encryption | Payload encrypted through intermediaries |
| Formal contracts required | WSDL forces strict API contract upfront |
| Legacy system integration | Existing SOAP services everywhere |
| Government/regulated industries | Compliance often mandates SOAP |
Full Comparison Table
| Feature | REST | SOAP |
|---|---|---|
| Protocol | HTTP (style) | HTTP, SMTP, TCP, others |
| Message format | JSON, XML, text | XML only |
| Contract | Optional (OpenAPI) | Required (WSDL) |
| Statefulness | Stateless | Can be stateful |
| Standards | Informal | WS-* formal specs |
| Security | HTTPS + app-layer | WS-Security (protocol-level) |
| Transactions | None built-in | WS-AtomicTransaction |
| Reliable messaging | None built-in | WS-ReliableMessaging |
| Caching | HTTP caching | Not supported |
| Payload size | Small (JSON) | Large (XML + envelope) |
| Performance | Higher | Lower |
| Learning curve | Low | High |
| Tooling | Any HTTP client | WSDL-aware clients |
| Code generation | OpenAPI generators | wsdl2java, wsimport |
| Error handling | HTTP status codes | SOAP Fault XML |
| Versioning | URL (/v1, /v2) or headers | WSDL namespace |
| Browser support | Native | Requires SOAP client library |
| Adoption (new projects) | ~95% | ~5% |
| Legacy systems | Rare | Common |
REST vs SOAP vs GraphQL vs gRPC
| REST | SOAP | GraphQL | gRPC | |
|---|---|---|---|---|
| Protocol | HTTP | HTTP/XML | HTTP | HTTP/2 + Protobuf |
| Format | JSON | XML | JSON | Binary |
| Contract | OpenAPI | WSDL | Schema | .proto file |
| Query flexibility | Fixed endpoints | Fixed operations | Flexible queries | Fixed methods |
| Streaming | No (SSE workaround) | No | Subscriptions | Bidirectional |
| Performance | Good | Slow | Medium | Excellent |
| Best for | Public APIs | Enterprise/legacy | Complex frontends | Microservices |
| Browser native | Yes | No | Yes | No (grpc-web) |
| Learning curve | Low | High | Medium | Medium |
Decision Guide
Need to integrate with existing SOAP/WSDL system?
→ SOAP
Need distributed transactions across services?
→ SOAP (WS-AtomicTransaction)
Building a public API (web or mobile)?
→ REST
Building a microservices backend?
→ REST or gRPC
Frontend needs flexible data fetching?
→ GraphQL
Performance-critical internal services?
→ gRPC
Everything else?
→ REST
Migrating from SOAP to REST
If you're modernizing a SOAP service, a common approach is the anti-corruption layer pattern:
# REST façade in front of legacy SOAP service
from fastapi import FastAPI
from zeep import Client
app = FastAPI()
soap_client = Client(wsdl="https://legacy.example.com/UserService?wsdl")
@app.get("/users/{user_id}")
def get_user(user_id: int):
# Translate REST request to SOAP call
soap_response = soap_client.service.GetUser(UserId=user_id)
# Translate SOAP response to REST JSON
return {
"id": user_id,
"name": soap_response.Name,
"email": soap_response.Email
}
This lets new clients use REST while the SOAP backend stays unchanged.
Common Mistakes
| Mistake | REST | SOAP |
|---|---|---|
| Wrong HTTP verb | Using POST for reads | — |
| Ignoring status codes | Always returning 200 | — |
| Not versioning API | Breaking clients on change | — |
| Skipping WS-Security | — | Sending sensitive data unencrypted |
| Parsing WSDL manually | — | Always use code generation |
| No caching headers | Losing performance gains | — |
| Treating REST as CRUD only | Missing resource modeling | — |
| SOAP for public API | — | Poor developer experience |
REST vs SOAP vs Related Terms
| Term | Relationship to REST/SOAP |
|---|---|
| HTTP | Transport protocol REST is built on |
| JSON | Most common REST response format |
| XML | Required SOAP format; optional in REST |
| OpenAPI / Swagger | REST API documentation standard |
| WSDL | SOAP API contract format |
| GraphQL | Alternative to REST for query flexibility |
| gRPC | Alternative to REST for performance |
| ESB | Enterprise Service Bus — routes SOAP messages |
| SOA | Service-Oriented Architecture — SOAP ecosystem |
| Microservices | Modern architecture style; uses REST/gRPC |
| API Gateway | Sits in front of REST APIs (Kong, AWS API GW) |
FAQ
Q: Is SOAP dead?
No. Millions of SOAP services run in banking, healthcare, government, and enterprise ERP systems. New development rarely chooses SOAP, but existing SOAP integrations will run for decades.
Q: Can REST be as secure as SOAP?
Yes, with HTTPS + OAuth 2.0 + JWT + proper implementation, REST is secure for most use cases. SOAP's WS-Security advantage matters when you need message-level encryption that survives intermediaries, which is rare outside highly regulated industries.
Q: Which is faster: REST or SOAP?
REST is typically 30–50% faster in practice because JSON payloads are smaller and cheaper to parse than XML. The difference matters most at high request volumes (millions/day).
Q: Does REST require JSON?
No. REST can use XML, HTML, plain text, or any format. JSON is used because it's lightweight and JavaScript-native, not because REST requires it.
Q: Can SOAP use HTTP GET?
Technically yes, but SOAP almost always uses HTTP POST because the entire request is in the XML body. There's no concept of URL-based resources.
Q: Should I learn SOAP in 2025?
Learn REST first — you'll use it in almost every job. Learn SOAP only if you work in finance, healthcare, enterprise integration, or need to maintain legacy systems. Basic SOAP knowledge is useful; deep WS-* expertise is only needed for specialist roles.