Swift
POST request
API integration
HTTP request
Swift programming

How to send a POST request with BODY in swift

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

Introduction

In Swift, sending a POST request with a body means creating a URLRequest, setting the HTTP method to POST, adding the body data, and then sending it with URLSession. The important details are the body encoding and the matching Content-Type header.

A Simple JSON POST Request

A common case is sending JSON to an API.

swift
1import Foundation
2
3struct LoginRequest: Codable {
4    let username: String
5    let password: String
6}
7
8func sendLogin() async throws {
9    let url = URL(string: "https://example.com/api/login")!
10    var request = URLRequest(url: url)
11    request.httpMethod = "POST"
12    request.setValue("application/json", forHTTPHeaderField: "Content-Type")
13
14    let payload = LoginRequest(username: "alice", password: "secret")
15    request.httpBody = try JSONEncoder().encode(payload)
16
17    let (data, response) = try await URLSession.shared.data(for: request)
18
19    if let httpResponse = response as? HTTPURLResponse {
20        print(httpResponse.statusCode)
21    }
22
23    print(String(data: data, encoding: .utf8) ?? "no body")
24}

This is the modern async and await form. The body is encoded as JSON, assigned to httpBody, and sent as part of the request.

Why Headers Matter

The server needs to know how to interpret the request body. That is why Content-Type matters.

Typical values are:

  • 'application/json for JSON APIs'
  • 'application/x-www-form-urlencoded for form-style payloads'
  • 'multipart/form-data for file uploads'

If the header does not match the body format, the server may reject the request or parse it incorrectly.

Form-Encoded Body Example

Not every API expects JSON. Some older endpoints still want form data.

swift
1import Foundation
2
3func sendForm() async throws {
4    let url = URL(string: "https://example.com/api/token")!
5    var request = URLRequest(url: url)
6    request.httpMethod = "POST"
7    request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
8
9    let form = "grant_type=client_credentials&scope=read"
10    request.httpBody = form.data(using: .utf8)
11
12    let (data, _) = try await URLSession.shared.data(for: request)
13    print(String(data: data, encoding: .utf8) ?? "no body")
14}

The transport mechanism is the same. Only the encoding and header change.

If You Are Not Using Async and Await

Older Swift code often uses a completion handler instead.

swift
1URLSession.shared.dataTask(with: request) { data, response, error in
2    if let error = error {
3        print(error)
4        return
5    }
6    print(String(data: data ?? Data(), encoding: .utf8) ?? "no body")
7}.resume()

That still works. Async and await is just easier to read when your deployment target allows it.

Validate the Response Too

A POST request is not complete just because the network call returned data. You should also inspect:

  • the status code
  • any server error payload
  • timeouts or transport errors

That is especially important for APIs that return JSON error bodies with useful diagnostics.

It is also common to decode the success body into a Swift type instead of printing raw text. Once the response format is known, JSONDecoder should usually be the next step after validating the HTTP status code.

That keeps the networking layer type-safe and easier to evolve.

It also simplifies testing.

Common Pitfalls

The most common mistake is forgetting to set the httpMethod to POST, which leaves the request as a GET.

Another mistake is sending JSON in httpBody but not setting the Content-Type header to application/json.

A third pitfall is building the request body as a plain string when JSON encoding would be safer and less error-prone.

Summary

  • Build a URLRequest, set httpMethod to POST, and assign data to httpBody.
  • Match the body encoding to the correct Content-Type header.
  • Use JSONEncoder for JSON payloads whenever possible.
  • Send the request with URLSession, ideally using async and await in modern Swift.
  • Check the HTTP response, not just whether the network call returned data.

Related reading
Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

All Rights Reserved.