How to Send POST Request in Java: Complete Guide with Examples
Executive Summary
According to Stack Overflow surveys, ninety percent of Java developers regularly send HTTP POST requests, making this skill essential for modern backend development.
The modern Java HttpClient library offers superior performance and ease of use compared to its predecessors, supporting HTTP/2, timeouts, and cleaner async patterns. Most production applications today rely on either the standard HttpClient or battle-tested libraries like OkHttp and Retrofit, which abstract away connection pooling and error handling complexity.
Learn Java on Udemy
Main Data Table
| Approach | Availability | Ease of Use | Performance | Best For |
|---|---|---|---|---|
| HttpURLConnection | Java 1.0+ | Low | Adequate | Legacy systems, zero dependencies |
| HttpClient (java.net.http) | Java 11+ | High | Excellent | Modern Java apps, async operations |
| OkHttp | All versions | High | Excellent | Production apps, interceptors needed |
| Spring RestTemplate | Spring Framework | Very High | Good | Spring applications, serialization |
| Retrofit | Android/Java | Very High | Excellent | Type-safe clients, API integration |
Breakdown by Experience Level
Developers at different experience levels tend to gravitate toward different solutions. Beginners often start with HttpURLConnection because it requires no external dependencies, though they frequently run into resource leaks. Intermediate developers usually discover HttpClient (Java 11+) or OkHttp and appreciate the cleaner APIs. Senior architects often standardize on Retrofit for type safety or Spring’s RestTemplate for ecosystem consistency.
Beginner: HttpURLConnection (basic understanding) → HttpClient (recommended progression)
Intermediate: HttpClient, OkHttp, Spring RestTemplate (pick based on project context)
Advanced: Custom implementations with OkHttp interceptors, async patterns, circuit breakers
Step-by-Step: Sending a POST Request
Let’s walk through the most practical approaches, starting with the modern standard and working backward.
Method 1: Using HttpClient (Java 11+, Recommended)
The modern HttpClient API is clean, supports HTTP/2, and handles connection pooling automatically:
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
public class PostRequestExample {
public static void main(String[] args) throws Exception {
// Create a client (reuse this across requests for better performance)
HttpClient client = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(10))
.version(HttpClient.Version.HTTP_2)
.build();
// Prepare the request body
String jsonBody = "{\"name\":\"John\",\"email\":\"john@example.com\"}";
// Build the request
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com/users"))
.timeout(Duration.ofSeconds(5))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(jsonBody))
.build();
// Send the request
HttpResponse<String> response = client.send(request,
HttpResponse.BodyHandlers.ofString());
// Handle the response
System.out.println("Status Code: " + response.statusCode());
System.out.println("Response Body: " + response.body());
}
}
Why this works: HttpClient manages connection pooling internally, supports modern HTTP standards, and the fluent API makes code readable. Always reuse the same client instance across multiple requests—creating new clients defeats the pooling benefit.
Method 2: Using OkHttp (Production-Grade)
OkHttp excels in production environments because of its interceptor system, automatic retries, and connection pooling:
import okhttp3.*;
import java.io.IOException;
public class OkHttpPostExample {
private static final OkHttpClient client = new OkHttpClient();
public static void main(String[] args) throws IOException {
String jsonBody = "{\"name\":\"Jane\",\"email\":\"jane@example.com\"}";
RequestBody body = RequestBody.create(
jsonBody,
MediaType.parse("application/json; charset=utf-8")
);
Request request = new Request.Builder()
.url("https://api.example.com/users")
.post(body)
.addHeader("User-Agent", "MyApp/1.0")
.build();
try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful())
throw new IOException("Unexpected code " + response);
String responseBody = response.body().string();
System.out.println("Status: " + response.code());
System.out.println("Response: " + responseBody);
}
}
}
Key advantage: The try-with-resources statement ensures the response is properly closed. OkHttp also handles HTTP/2 connection multiplexing, which significantly reduces latency in high-throughput scenarios.
Method 3: Using HttpURLConnection (Legacy, Zero Dependencies)
Sometimes you need to send a POST request without external dependencies. This approach works but requires careful resource management:
import java.net.HttpURLConnection;
import java.net.URL;
import java.io.OutputStream;
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class HttpURLConnectionExample {
public static void main(String[] args) throws Exception {
String jsonBody = "{\"name\":\"Bob\",\"email\":\"bob@example.com\"}";
HttpURLConnection conn = null;
try {
URL url = new URL("https://api.example.com/users");
conn = (HttpURLConnection) url.openConnection();
// Configure the request
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "application/json");
conn.setConnectTimeout(5000);
conn.setReadTimeout(5000);
conn.setDoOutput(true);
// Send the request body
try (OutputStream os = conn.getOutputStream()) {
byte[] input = jsonBody.getBytes("utf-8");
os.write(input, 0, input.length);
}
// Read the response
int statusCode = conn.getResponseCode();
BufferedReader br = new BufferedReader(
new InputStreamReader(conn.getInputStream(), "utf-8")
);
StringBuilder response = new StringBuilder();
String responseLine;
while ((responseLine = br.readLine()) != null) {
response.append(responseLine.trim());
}
System.out.println("Status Code: " + statusCode);
System.out.println("Response: " + response.toString());
} finally {
if (conn != null) {
conn.disconnect();
}
}
}
}
⚠️ Critical pitfall: This approach leaks connections if you don’t call disconnect() or properly close streams. Notice we use try-with-resources and a finally block for safety.
Comparison Section
| Feature | HttpClient | OkHttp | RestTemplate | Retrofit |
|---|---|---|---|---|
| Connection pooling | ✓ Built-in | ✓ Built-in | ✓ Built-in | ✓ Built-in |
| Async support | ✓ CompletableFuture | ✓ Callback | ✓ Async | ✓ RxJava |
| Interceptors | ✗ Limited | ✓ Powerful | ✓ Via Spring | ✓ Via OkHttp |
| JSON serialization | Manual | Manual | ✓ Auto | ✓ Auto |
| Java version | 11+ | 8+ | Spring req. | 8+ |
| External deps | None | One | Spring | One |
Key Factors When Choosing Your Approach
1. Java Version Compatibility
If you’re stuck on Java 8 or 10, HttpClient isn’t an option—you’ll need OkHttp or Spring RestTemplate. If you can upgrade to Java 11+, the built-in HttpClient eliminates a dependency and integrates with the standard library.
2. Performance Requirements
HttpClient and OkHttp both achieve roughly equivalent throughput in benchmarks, typically 10-15% faster than HttpURLConnection due to connection pooling. For high-volume APIs (>1000 req/sec), connection pooling becomes non-negotiable. Interestingly, the performance difference narrows when you properly implement pooling yourself, but it’s easy to get wrong.
3. Error Handling and Resilience
OkHttp excels here with built-in retry logic, connection interceptors, and timeout granularity. HttpClient requires manual implementation of retries. If you need circuit breakers or advanced resilience patterns, Retrofit with OkHttp gives you the cleanest integration path.
4. Serialization Complexity
Manual JSON serialization gets tedious fast. RestTemplate and Retrofit handle this automatically with Jackson or Gson. If your POST payloads involve nested objects, consider using a framework-backed solution. This is where many developers waste time—pre-serializing JSON strings is error-prone.
5. Request/Response Interception
OkHttp’s interceptor system is unmatched. It lets you inject logging, auth headers, request signing, and metrics collection across all requests. If your API requires custom headers or request transformation, OkHttp interceptors pay for themselves in code clarity and maintainability.
Historical Trends
The Java HTTP ecosystem has matured significantly over the past decade. HttpURLConnection dominated pre-2014 codebases but suffered from poor defaults and verbose APIs. OkHttp’s introduction (2013) brought production-grade reliability and became the de facto standard for Android and many Java backends. The release of HttpClient in Java 11 (2018) changed the landscape—it finally provided a modern, standard alternative that closed the gap with OkHttp. By 2024-2026, new projects increasingly adopt HttpClient unless they specifically need OkHttp’s interceptor capabilities or must support Java 8.
Expert Tips
1. Always Reuse HTTP Clients
Creating a new HttpClient or OkHttpClient for each request defeats connection pooling and burns through threads. Create one client instance at application startup and inject it as a singleton. This is the single most impactful optimization.
2. Set Appropriate Timeouts
Timeouts prevent threads from hanging indefinitely. Use connection timeouts (how long to establish a connection) and read timeouts (how long to wait for a response). A typical safe default: 10 seconds connect, 30 seconds read. Adjust based on your SLAs.
3. Handle Non-2xx Status Codes Properly
Just because a request “succeeds” (gets a response) doesn’t mean it worked. Check the status code. A 400 or 500 isn’t an exception in HttpClient—it’s a valid response. Only treat network failures and timeouts as exceptional.
4. Use Try-With-Resources for Resource Management
Response bodies hold open resources. Always use try-with-resources (Java 7+) or explicitly close responses. OkHttp enforces this better than HttpURLConnection.
5. Consider Request/Response Logging for Debugging
OkHttp’s HttpLoggingInterceptor and Spring’s LoggingFilter are invaluable during development. Log the full request/response at DEBUG level only—never log sensitive headers in production.
Learn Java on Udemy
Frequently Asked Questions
Related tool: Try our free calculator