How to Send POST Request in Java: Complete Guide with Examples - comprehensive 2026 data and analysis

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


View 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


View on Udemy →

Frequently Asked Questions


Related tool: Try our free calculator

Similar Posts