Phase 4: Connectivity 14 min read

ESP32 HTTP Client — GET and POST Requests

Send HTTP GET and POST requests from ESP32 using the HTTPClient library. Includes JSON payloads, response parsing, HTTPS with certificate validation, and error handling.

Updated June 18, 2026

Why HTTP on ESP32?

HTTP is the language of the web. With ESP32’s built-in Wi-Fi and the HTTPClient library, your microcontroller can talk directly to REST APIs, IoT platforms, home automation hubs, and your own backend servers. This guide covers everything from a simple GET request to authenticated HTTPS POST with JSON.

Prerequisites

  • ESP32 connected to Wi-Fi (Wi-Fi Basics guide)
  • HTTPClient.h — built into arduino-esp32, no install needed
  • Optional: ArduinoJson library for parsing responses

Simple HTTP GET Request

http_get.ino
#include <WiFi.h>
#include <HTTPClient.h>

const char* ssid     = "YourSSID";
const char* password = "YourPassword";

void setup() {
  Serial.begin(115200);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); }
  Serial.println("\nConnected: " + WiFi.localIP().toString());
}

void loop() {
  if (WiFi.status() == WL_CONNECTED) {
    HTTPClient http;

    http.begin("http://api.open-meteo.com/v1/forecast?latitude=51.5&longitude=-0.1¤t_weather=true");
    http.setTimeout(10000);

    int httpCode = http.GET();

    if (httpCode > 0) {
      Serial.printf("HTTP %d\n", httpCode);
      if (httpCode == HTTP_CODE_OK) {
        String payload = http.getString();
        Serial.println(payload.substring(0, 200)); // first 200 chars
      }
    } else {
      Serial.printf("GET failed: %s\n", http.errorToString(httpCode).c_str());
    }

    http.end();
  }
  delay(30000);
}

Parsing a JSON Response with ArduinoJson

http_get_json.ino
#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>

// ... WiFi setup ...

void fetchWeather() {
  HTTPClient http;
  http.begin("http://api.open-meteo.com/v1/forecast"
             "?latitude=51.5&longitude=-0.1¤t_weather=true");

  if (http.GET() == HTTP_CODE_OK) {
    String body = http.getString();

    JsonDocument doc;
    DeserializationError err = deserializeJson(doc, body);

    if (!err) {
      float temp      = doc["current_weather"]["temperature"];
      int   windspeed = doc["current_weather"]["windspeed"];
      Serial.printf("Temp: %.1f°C  Wind: %d km/h\n", temp, windspeed);
    } else {
      Serial.println("JSON parse error: " + String(err.c_str()));
    }
  }
  http.end();
}

HTTP POST — JSON Body

http_post_json.ino
#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>

void postSensorData(float temperature, float humidity) {
  HTTPClient http;
  http.begin("https://your-api.example.com/sensors");
  http.addHeader("Content-Type", "application/json");
  http.addHeader("Authorization", "Bearer YOUR_API_TOKEN");

  JsonDocument doc;
  doc["device_id"]   = "esp32-living-room";
  doc["temperature"] = temperature;
  doc["humidity"]    = humidity;
  doc["timestamp"]   = millis();

  String body;
  serializeJson(doc, body);

  int httpCode = http.POST(body);
  Serial.printf("POST status: %d\n", httpCode);

  if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_CREATED) {
    Serial.println("Data sent: " + http.getString());
  }
  http.end();
}

HTTP POST — Form-Encoded Data

http_post_form.ino
HTTPClient http;
http.begin("http://192.168.1.100/api/control");
http.addHeader("Content-Type", "application/x-www-form-urlencoded");

String payload = "relay=on&duration=5000&device=esp32";
int code = http.POST(payload);
Serial.printf("Response: %d — %s\n", code, http.getString().c_str());
http.end();

HTTPS with Certificate Validation

For production code, always validate the server’s TLS certificate. You need the root CA certificate of the server (download it from your browser’s certificate inspector or use openssl s_client -connect host:443 -showcerts).

https_secure.ino
#include <WiFi.h>
#include <HTTPClient.h>
#include <WiFiClientSecure.h>

// Root CA for your server (get from browser or openssl)
const char* root_ca = R"(
-----BEGIN CERTIFICATE-----
MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh
...
-----END CERTIFICATE-----
)";

void setup() {
  // ... WiFi connect ...

  WiFiClientSecure client;
  client.setCACert(root_ca);         // validates server cert
  // client.setInsecure();           // TESTING ONLY — skip validation

  HTTPClient https;
  https.begin(client, "https://api.example.com/data");
  int code = https.GET();
  Serial.printf("HTTPS %d: %s\n", code, https.getString().c_str());
  https.end();
}

Syncing Time for HTTPS

TLS handshakes require the ESP32 clock to be within a few minutes of real time. Sync via NTP before any HTTPS call:

ntp_sync.ino
#include <time.h>

void syncTime() {
  configTime(0, 0, "pool.ntp.org", "time.nist.gov");
  Serial.print("Syncing NTP");
  time_t now = time(nullptr);
  while (now < 8 * 3600 * 2) {
    delay(500);
    Serial.print(".");
    now = time(nullptr);
  }
  Serial.println(" done");
}

Error Handling Reference

Code Constant Meaning
-1 HTTPC_ERROR_CONNECTION_REFUSED Server port closed or firewall blocked
-4 HTTPC_ERROR_CONNECTION_LOST TCP connection dropped mid-transfer
-11 HTTPC_ERROR_READ_TIMEOUT Server did not respond within timeout
401 HTTP_CODE_UNAUTHORIZED Missing or wrong Authorization header
429 Too Many Requests Rate limit — add exponential backoff

Frequently Asked Questions

The built-in HTTPClient library (included with arduino-esp32) handles both HTTP and HTTPS. Include it with #include . No additional installation is needed.
Use the ArduinoJson library (install via Library Manager: search "ArduinoJson" by Benoit Blanchon). Deserialize the response string with deserializeJson(doc, payload) and access fields with doc["key"].as().
Yes. Use WiFiClientSecure instead of WiFiClient. For full certificate validation call client.setCACert(root_ca_cert). For testing only you can call client.setInsecure() to skip validation (not safe for production).
http.GET() returns a negative error code. HTTPC_ERROR_CONNECTION_REFUSED is -1, HTTPC_ERROR_SEND_HEADER_FAILED is -2, HTTPC_ERROR_CONNECTION_LOST is -4. Always check for httpCode > 0 before reading the response.
Call http.addHeader("Content-Type", "application/x-www-form-urlencoded") then http.POST("key1=value1&key2=value2"). URL-encode special characters in the values.
Yes. Use http.POST(uint8_t* payload, size_t size) for binary payloads. Set Content-Type to application/octet-stream or the appropriate MIME type for your data.
The root CA certificate for the server has likely changed or expired, or the ESP32 clock is not set (TLS requires a valid time). Call configTime() to sync NTP time before making HTTPS requests.
Call http.setTimeout(milliseconds) before http.begin(). The default is 5000 ms (5 seconds). Set it to something like 10000 for slow APIs and 3000 for local network calls.
Yes. Call http.addHeader("Header-Name", "value") as many times as needed before calling http.GET() or http.POST(). Common uses include Authorization, Accept, and User-Agent headers.
Use http.getStream() to get a Stream reference and read it in chunks: while (stream->available()) { uint8_t buf[128]; stream->readBytes(buf, sizeof(buf)); process(buf); }. This avoids loading the full response into a String.

Projects to Build

Put this knowledge to work — try one of these hands-on projects.