HTTP 207 Multi-Status

HTTP 207 Multi-Status is a WebDAV extension (RFC 4918) that conveys information about multiple resources in situations where multiple status codes might be appropriate. The response body is an XML document containing individual status codes for each sub-operation. This is used when a single request affects multiple resources — like batch operations — and some succeed while others fail. The overall 207 status means 'look inside the body for individual results.'

Debug HTTP 207 live
Analyze real 207 behavior — headers, caching, CORS, redirects
Open Inspector →

Try it (live endpoint)

Response includes the status code, standard headers (including Content-Type), and a small diagnostic JSON body describing the request and returned status.

Simulator URL (copy in the app after load — not a normal link):

https://httpstatus.com/api/status/207

Example request:

curl -i "https://httpstatus.com/api/status/207"
Try in playground

Meaning

Provides status for multiple independent operations (WebDAV). The response body contains XML with status information for each operation.

What it guarantees
  • The server accepted the request and produced a final response.
What it does NOT guarantee
  • The underlying business operation is correct across all downstream systems.
  • The response is cacheable unless headers explicitly allow it.

When to use this status

  • GET succeeds and returns a representation of the resource.
  • PUT/PATCH succeeds and returns an updated representation.
  • POST succeeds and returns an immediate result.

When NOT to use this status (common misuses)

Returning 200 for partial failures or errors embedded only in the body.
Clients and monitors treat it as success; failures become silent and harder to alert on.
Returning 200 for creation instead of 201 with Location.
Clients lose a reliable created-resource identifier; SDK behavior becomes inconsistent.
Returning 200 for async acceptance instead of 202.
Clients assume the work is complete and proceed incorrectly.

Critical headers that matter

Content-Type
Defines how clients parse the body.
Clients mis-parse payloads; SDKs and browsers apply wrong decoding.
Cache-Control
Controls cacheability and revalidation.
CDNs/browsers cache dynamic data or fail to cache static content.
ETag / Last-Modified
Enables conditional requests and revalidation.
Unnecessary bandwidth; poor cache consistency.

Tool interpretation

Browsers
Treats as success; caches/revalidates based on headers and validators.
API clients
Deserializes per Content-Type; conditional requests use validators when implemented.
Crawlers / SEO tools
Indexes depending on headers and canonical stability; caches behavior via validators and cache directives.
Uptime monitors
Typically marks success; advanced checks may flag header anomalies or latency.
CDNs / reverse proxies
Caches/revalidates based on Cache-Control, ETag, and Vary; compression and content-type affect behavior.

Inspector preview (read-only)

On this code, Inspector focuses on semantics, headers, and correctness warnings that commonly affect clients and caches.

Signals it will highlight
  • Status semantics vs method and body expectations
  • Header sanity (Content-Type, Cache-Control, Vary) and evidence completeness
Correctness warnings
No common correctness warnings are specific to this code.

Guided Lab outcome

  • Reproduce HTTP 207 Multi-Status using a controlled endpoint and capture the full exchange.
  • Practice distinguishing status semantics from transport issues (redirects, caching, proxies).

Technical deep dive

207 Multi-Status (RFC 4918 Section 13) uses an XML body with a <multistatus> root element containing <response> elements, each with an <href> (the affected resource) and a <status> (the HTTP status line for that resource). This allows a single PROPFIND, PROPPATCH, or COPY operation on a collection to report per-resource outcomes. While originating in WebDAV, modern REST APIs adopt the concept for batch endpoints using JSON: POST /api/batch returns 207 with an array of results, each containing its own status code. Key: the overall 207 status does NOT imply all sub-operations succeeded — the client MUST inspect each individual result.

Real-world examples

WebDAV directory listing
PROPFIND /documents/ requests properties of all files in a directory. The server returns 207 with a <response> for each file containing its size, modification date, and content-type. Each file's properties are returned with their own status (200 for accessible, 403 for restricted).
Batch API operations
POST /api/batch with 10 operations (create 3 users, update 5, delete 2). The server returns 207 with results for each: { results: [{ index: 0, status: 201, data: {...} }, { index: 3, status: 409, error: 'Conflict' }, ...] }. Three succeeded, one had a conflict, others completed normally.
Microsoft Graph batch requests
The Microsoft Graph API accepts batch requests at /$batch. Each request in the batch gets its own status in the 207 response. This reduces HTTP overhead for applications that need to make many Graph API calls.

Framework behavior

Express.js (Node)
Express has no built-in 207 support. Implement manually: res.status(207).json({ results: operations.map(op => ({ id: op.id, status: op.success ? 200 : op.errorCode, data: op.result })) });
Django / DRF (Python)
Django/DRF: return Response(batch_results, status=207). For WebDAV, use django-webdav or construct the XML multistatus response manually with ElementTree.
Spring Boot (Java)
Spring: return ResponseEntity.status(207).body(batchResults). For WebDAV integration, use Milton or implement custom controllers with XML serialization for <multistatus> responses.
FastAPI (Python)
FastAPI: @app.post('/api/batch', status_code=207). Return a list of result objects each containing their own status code. Define response models for both success and error variants.

Debugging guide

  1. Never assume 207 means all operations succeeded — always parse the individual results
  2. For WebDAV, the body is XML — check the <status> element within each <response>
  3. For JSON batch APIs, iterate the results array and handle each status individually
  4. Check for partial failures: a 207 response may contain a mix of 200s, 400s, and 500s
  5. If your client library treats 207 as an error (2xx but unknown), configure it to accept 207 as success

Code snippets

Node.js
app.post('/api/batch', async (req, res) => {
  const results = await Promise.all(
    req.body.operations.map(async (op, index) => {
      try {
        const result = await processOperation(op);
        return { index, status: 200, data: result };
      } catch (err) {
        return { index, status: err.statusCode || 500,
                 error: err.message };
      }
    })
  );
  res.status(207).json({ results });
});
Python
@app.post('/api/batch', status_code=207)
async def batch_operations(ops: List[Operation]):
    results = []
    for i, op in enumerate(ops):
        try:
            data = await process_op(op)
            results.append({'index': i, 'status': 200, 'data': data})
        except AppError as e:
            results.append({'index': i, 'status': e.code, 'error': str(e)})
    return {'results': results}
Java (Spring)
@PostMapping("/api/batch")
public ResponseEntity<BatchResponse> batch(
        @RequestBody List<Operation> ops) {
    List<BatchResult> results = ops.stream()
        .map(op -> {
            try {
                Object data = processOp(op);
                return new BatchResult(200, data, null);
            } catch (AppException e) {
                return new BatchResult(e.getStatus(), null, e.getMessage());
            }
        }).toList();
    return ResponseEntity.status(207).body(new BatchResponse(results));
}
Go
func batchHandler(w http.ResponseWriter, r *http.Request) {
	var ops []Operation
	json.NewDecoder(r.Body).Decode(&ops)

	results := make([]BatchResult, len(ops))
	for i, op := range ops {
		data, err := processOp(op)
		if err != nil {
			results[i] = BatchResult{Status: 500, Error: err.Error()}
		} else {
			results[i] = BatchResult{Status: 200, Data: data}
		}
	}
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(207)
	json.NewEncoder(w).Encode(map[string]any{"results": results})
}

FAQ

Should I use 207 for batch REST APIs?
Yes, 207 is appropriate when a batch request has mixed results (some operations succeed, some fail). If all operations must succeed or fail together (atomic batch), use regular status codes: 200 for all success, 400/500 for any failure. 207 signals 'partial success is possible — check each result.'
Is 207 Multi-Status only for WebDAV?
It originated in WebDAV (RFC 4918) but is widely used in modern REST APIs for batch operations. Microsoft Graph, Google APIs, and many SaaS platforms use 207 with JSON bodies. The key semantic — multiple independent outcomes in one response — is universally useful.
How should clients handle a 207 response?
Clients must parse the response body and check each individual result's status. Do not treat 207 as blanket success. Typical pattern: iterate results, collect successes and failures separately, retry failed operations if appropriate, and report partial success to the user.
Can 207 include both 2xx and 5xx status codes for individual operations?
Yes, that's exactly the point. A 207 response can contain any mix of status codes in individual results: 200 for successes, 400 for validation errors, 404 for missing resources, 409 for conflicts, and 500 for server errors. Each sub-operation is independent.

Client expectation contract

Client can assume
  • A final HTTP response was produced and processed by the server.
Client must NOT assume
  • The change is durable across all downstream systems.
Retry behavior
Retries are generally unnecessary; treat as final unless domain rules require revalidation.
Monitoring classification
Success
Use payload and header checks to avoid false positives; cacheability depends on Cache-Control/ETag/Vary.

Related status codes

206 Partial Content
The server is delivering only part of the resource in response to a Range header (used for resumable downloads and video streaming).
208 Already Reported
The members of a DAV binding have already been enumerated in a previous reply to this request.

Explore more

Related tools
Related utilities