Skip to content

About

A lightweight, low-allocation, high-performance HTTP client library for ESP32.

Topics

Resources

Contributing

Security policy

Stars

165 stars

Watchers

1 watching

Forks

Repository files navigation

HTTP for ESP32, the easy way, with a low memory footprint.
A lightweight, low-allocation HTTP client with direct stream-to-variable binding.

Latest release GitHub stars License

Arduino Library PlatformIO Registry Primary language Coverage: 97.76% Downloads Repository visits

Documentation • Quick Start • API Reference • Examples • Support

Arduino Libraries • Registry PlatformIo

ESP32 HTTP Client on Trendshift

Contents

Why this library?

Writing HTTP requests on embedded systems shouldn't feel like a chore. The standard approach forces you to manage connection states, handle string buffers manually, and allocate large chunks of RAM just to parse a simple JSON response or construct complex XML envelopes.

ESP32-HTTP-Client acts as a bridge between your variables and web services. Built on top of a low-overhead, persistent HTTP/HTTPS networking core, it provides dedicated, fluent builders for common web communication standards (such as REST APIs and SOAP 1.1 / 1.2 Web Services) while extracting response fields directly into your C++ variables on-the-fly.

The problem: the standard approach

A typical request with the Arduino SDK looks like this:

  1. Initialize HTTPClient.
  2. Make the request.
  3. Check error codes.
  4. Call http.getString(), allocating a large String on the heap.
  5. Create a DynamicJsonDocument (or heavy XML DOM tree), allocating even more RAM.
  6. Call deserialization functions.
  7. Extract values manually.
  8. Hope you didn't run out of heap.

The solution

// REST: One line. Zero intermediate strings. Direct memory binding.
client.get("/sensor").getBody("temperature", &myFloatVariable);

// SOAP: Automatic envelopes, streaming XML extraction, zero DOM allocations.
client.soap("/ws").soapAction("GetPrice").body("<m:GetPrice/>").getBody("Price", &myPrice);

// GraphQL: Fluent queries, mutations, variables, and partial data preservation.
client.graphql("/graphql").query("query { user { name } }").getData("user.name", &myName);

// JSON-RPC 2.0: Positional/named parameters, notifications, and streaming.
client.jsonRpc("/rpc").method("add").param(15).param(27).getResult(&mySum);

Performance & Comparison

The following data is the result of a benchmark running 100 consecutive HTTP GET requests with JSON payloads on an ESP32 using the public JSONPlaceholder /users endpoint as the test source. JSONPlaceholder /users endpoint

Metric / Feature Standard (HTTPClient + ArduinoJson) ESP32-HTTP-Client Comparison
Memory Usage (Heap per req) ~58.2 KB ~0.0 KB (15 bytes) ⬇ ~99.9% less RAM per request
Avg. RAM Footprint (Estimate) 34.2% 24.3% ⬇ ~29% less overall RAM used
Absolute Min. Free Heap 114.3 KB 128.6 KB ⬆ Safer for large applications
Execution Time (Average) ~750 ms ~59 ms 🚀 ~12x faster (Native Keep-Alive)
Code Verbosity High (~15 lines of boilerplate) Low (1 fluent chain) ⬇ Clean & maintainable code
JSON Parsing Requires deserializeJson() Automatic, direct binding ⬆ No JSON document allocation

Note

Execution Time & Keep-Alive: Because ESP32-HTTP-Client safely reuses the underlying TLS connection and parses the response directly from the network stream (with native Transfer-Encoding: chunked decoding), it avoids the massive penalty of repeatedly establishing TLS handshakes. This makes it over 10x faster than the traditional approach while keeping the memory footprint exceptionally low.

ESP32 HTTP Client performance benchmark

Key Features

Stream responses into your variables and keep request code short.

  • Fluent chaining — build requests naturally: .get().query().getBody(), .soap().soapAction().body().getBody(), or .graphql().query().getData().
  • Direct injection — JSON, XML, and GraphQL values are written straight into standard C types (int, float, bool, char*) or C++ structs.
  • Zero buffering — the response stream is parsed in place; the full payload is never stored in memory.
  • Native GraphQL support — queries, mutations, variables (primitives and structs), operation selection, error locations/paths/extensions, partial data preservation, and incremental streaming (multipart/mixed, @defer).
  • Native JSON-RPC 2.0 support — positional and named parameters, configurable request IDs, notifications, struct serialization/binding, and standard error handling.
  • Native SOAP 1.1 & 1.2 support — automatic envelopes, SOAPAction/Content-Type headers, streaming XML response parsing, and SOAP Fault handling.
  • Struct <-> JSON mapping — direct bidirectional struct serialization/deserialization without dynamic document allocations.
  • Comprehensive TLS / HTTPS Support — Root CA validation (setCACert), X.509 certificate bundles (setCACertBundle), mutual TLS (setClientCert), and opt-in insecure mode (setInsecure). Zero TLS heap overhead on HTTP, native Keep-Alive TLS session reuse, and granular mbedTLS error reporting (getTlsError). Fully compatible with arduino-esp32 2.x and 3.x.
  • Full REST support — GET, POST, PUT, PATCH, and DELETE are all first-class citizens.
  • IoT ready — designed for connecting ESP32 devices to cloud backends, Firebase, AWS API Gateway, GraphQL endpoints, SOAP web services, or custom servers.

Installation

Choose the package manager used by your ESP32 project.

PlatformIO

Add ESP32-HTTP-Client to the lib_deps section of your platformio.ini:

lib_deps =
    PedroFnseca/ESP32-HTTP-Client@^1.4.0

Arduino Library Manager

  1. Open Arduino IDE and go to Sketch → Include Library → Manage Libraries....
  2. Search for ESP32-HTTP-Client.
  3. Click Install.

Quick Start

Start with REST, or use the same client for SOAP, GraphQL, and JSON-RPC.

REST API (JSON)

#include <WiFi.h>
#include "ESP32HTTPClient.h"

ESP32HTTPClient client("https://jsonplaceholder.typicode.com");

void setup() {
    Serial.begin(115200);
    WiFi.begin("SSID", "PASS");

    while (WiFi.status() != WL_CONNECTED) delay(100);

    int userId = 0;

    // API returns: { "userId": 1, "id": 1, "title": "..." }
    client.get("/todos/1").getBody("userId", &userId);

    Serial.printf("User ID fetched from API: %d\n", userId);
}

void loop() {}

SOAP Web Service (XML)

#include <WiFi.h>
#include "ESP32HTTPClient.h"

ESP32HTTPClient client("https://www.dataaccess.com");

void setup() {
    Serial.begin(115200);
    WiFi.begin("SSID", "PASS");

    while (WiFi.status() != WL_CONNECTED) delay(100);

    char result[64] = {0};

    // Sends SOAP 1.1 request and extracts <NumberToWordsResult>
    client.soap("/webservicesserver/NumberConversion.wso")
          .soapAction("http://www.dataaccess.com/webservicesserver/NumberToWords")
          .body("<NumberToWords xmlns=\"http://www.dataaccess.com/webservicesserver/\">"
                "<ubiNum>500</ubiNum>"
                "</NumberToWords>")
          .getBody("NumberToWordsResult", result, sizeof(result));

    Serial.printf("Result: %s\n", result);
}

void loop() {}

GraphQL API

#include <WiFi.h>
#include "ESP32HTTPClient.h"

ESP32HTTPClient client("https://api.example.com");

void setup() {
    Serial.begin(115200);
    WiFi.begin("SSID", "PASS");
    while (WiFi.status() != WL_CONNECTED) delay(100);

    String userName;
    int userId = 0;

    // Fluent GraphQL query with variable and direct field binding
    client.graphql("/graphql")
          .query("query GetUser($id: ID!) { user(id: $id) { id name } }")
          .variable("id", "101")
          .getData("user.id", &userId)
          .getData("user.name", &userName);

    Serial.printf("User ID: %d, Name: %s\n", userId, userName.c_str());
}

void loop() {}

JSON-RPC 2.0 API

#include <WiFi.h>
#include "ESP32HTTPClient.h"

ESP32HTTPClient client("https://api.example.com");

void setup() {
    Serial.begin(115200);
    WiFi.begin("SSID", "PASS");
    while (WiFi.status() != WL_CONNECTED) delay(100);

    int sum = 0;

    // Calls JSON-RPC 2.0 method with positional params and binds result
    client.jsonRpc("/rpc")
          .method("add")
          .param(15)
          .param(27)
          .id(1)
          .getResult(&sum);

    Serial.printf("Result: %d\n", sum);
}

void loop() {}

Initialization

Default port (80 for HTTP, 443 for HTTPS)

ESP32HTTPClient client("https://api.example.com");

Custom port

Specify the port as the second argument if your API runs on a non-standard port.

ESP32HTTPClient client("http://my-local-server.local", 8080);

Usage

Build requests, bind response fields, and control connections with the fluent API.

Query Parameters

// Produces: GET /users?page=2&limit=20&search=pedro
client.get("/users")
      .query("page", 2)
      .query("limit", 20)
      .query("search", "pedro");

Path Parameters

Replace {placeholder} segments dynamically in the request path:

// Produces: GET /users/15
client.get("/users/{id}")
      .path("id", 15);

POST JSON data

int newId;

// Body: { "name": "Pedro", "role": "admin", "age": 21 }
client.post("/users")
      .body("name", "Pedro")
      .body("role", "admin")
      .body("age", 21)
      .getBody("id", &newId);

Extracting nested fields

Use dot notation to navigate nested objects.

char val[32];

// Response: { "level0": { "level1": "val2" } }
client.get("/nested")
      .getBody("level0.level1", val, sizeof(val));

Extracting from arrays

Use a numeric index as a path segment to address array elements.

char city[32];

// Response: [ { "address": { "city": "Gwenborough" } }, { "address": { "city": "Wisokyburgh" } } ]
client.get("/users")
      .getBody("1.address.city", city, sizeof(city)); // resolves the second element

Extracting complete raw objects or arrays

Bind to an Arduino String to capture an entire object or sub-array for manual processing.

String entireArray;
String specificUser;

client.get("/users")
      .getBody("", &entireArray)    // captures the root-level array
      .getBody("1", &specificUser); // captures the second user object

Warning

Pulling complete objects or arrays into an Arduino String causes dynamic memory reallocation as the raw JSON is copied character by character. Avoid this pattern with large payloads, as it can fragment or exhaust the device heap.

Note

If a key is missing, misspelled, or the path does not exist in the response, the target variable is left unchanged. The library will not crash.

Managing Connections (Keep-Alive)

By default, the client automatically maintains a persistent TCP/TLS connection across requests (HTTP Keep-Alive). This drastically improves performance for subsequent requests to the same server, but it holds onto the connection memory buffers (e.g., ~45KB for an active TLS tunnel). To manually close the connection and free this memory when you are done making requests, call end():

client.end(); // Closes the connection and frees TLS RAM

TLS / HTTPS Configuration & Security

The library supports robust TLS/HTTPS with certificate validation, client certificates (mTLS), custom CA bundles, fail-closed enforcement, and actionable mbedTLS diagnostics:

// 1. Root CA Certificate Validation (PEM format)
// Pointers are stored, not copied: buffer must remain in memory (static/const/PROGMEM)
static const char* root_ca = "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----\n";
client.setCACert(root_ca);

// 2. Certificate Bundle (ESP-IDF esp_crt_bundle format)
// client.setCACertBundle(bundle_data, bundle_size);

// 3. Fail-Closed Mode: refuse HTTPS if neither setCACert() nor setCACertBundle() is configured
// (Can also be enabled globally via build flag: -DESP32HTTPCLIENT_REQUIRE_VALIDATED_TLS=1)
client.requireValidatedTls(true);

// 4. Mutual TLS (mTLS) Client Certificate & Private Key
// client.setClientCert(client_cert_pem, client_key_pem);

// 5. Insecure Mode (WARNING: Disables certificate and hostname validation! Logs a security warning)
// client.setInsecure();

// 6. Inspect mbedTLS error and actionable diagnostic hint on failure
if (client.hasError()) {
    // getErrorMessage() automatically includes the mbedTLS code + hint (e.g., clock before 2023 / NTP,
    // expired certificate, wrong Root CA, or hostname mismatch)
    Serial.printf("Request Error (%d): %s\n",
                  client.getStatusCode(), client.getErrorMessage().c_str());

    if (client.getTlsError() != 0) {
        Serial.printf("TLS Hint: %s\n", client.getTlsErrorHint().c_str());
    }
}

Practical TLS Security Guide

  1. How to obtain the right Root CA (openssl s_client): Do not pin the server's leaf certificate (it rotates every 60–90 days). Instead, inspect the full certificate chain presented by the server and extract the Root CA (the issuer of the last certificate in the chain):

    openssl s_client -showcerts -servername api.example.com -connect api.example.com:443 </dev/null

    Copy the root certificate PEM block (-----BEGIN CERTIFICATE----- ... -----END CERTIFICATE-----) or download the corresponding Root CA PEM from the CA's official repository (e.g., ISRG Root X1 for Let's Encrypt, DigiCert Global Root G2, Amazon Root CA 1).

  2. Single Root CA (setCACert) vs. Certificate Bundle (setCACertBundle):

    • Single Root CA (setCACert): Best when connecting to a single backend or cloud provider whose PKI you control or know in advance. Uses minimal Flash (~1.5–2 KB per PEM) and zero extra build configuration.
    • CA Bundle (setCACertBundle): Best when connecting to arbitrary user-configured URLs, third-party webhooks, or CDNs (like Cloudflare) that may switch between different Certificate Authorities (Let's Encrypt, Google Trust Services, DigiCert). Costs ~25–65 KB of Flash.
  3. Certificate & Root CA Expiration (Firmware Updates / OTA):

    • Leaf certificates expire frequently (e.g., Let's Encrypt every 90 days) — pinning a Root CA handles leaf renewals automatically without firmware changes.
    • Root CAs also expire (typically 5–20 years) or can be cross-signed/revoked, and servers may migrate to a different CA. Always include an OTA (Over-The-Air) firmware update mechanism (or load CA certificates from LittleFS/NVS) in production devices so you can rotate trust anchors before they expire.

Important

NTP Synchronization: TLS certificate validation requires an accurate system clock to verify certificate validity dates (notBefore and notAfter). If the ESP32 clock defaults to 1970, validation will fail (and getTlsErrorHint() / getErrorMessage() will explicitly warn that the system clock is before 2023). Synchronize time via NTP using configTime(0, 0, "pool.ntp.org") before making validated HTTPS requests. See examples/HttpsTls.

Authentication Helpers

Easily authenticate requests using built-in helpers for Bearer tokens, Basic Auth, or API keys:

// Bearer / JWT Token
client.bearer("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...");

// HTTP Basic Auth (automatically Base64 encoded)
client.basic("admin", "secret123");

// API Key header
client.apiKey("x-api-key", "my-secret-api-key");

Struct <-> JSON Mapping

Direct bidirectional mapping between C++ structs and JSON payloads:

struct User {
    int id = 0;
    char name[32] = {0};
    bool active = false;

    REST_JSON_MAP(
        REST_FIELD(id),
        REST_FIELD(name),
        REST_FIELD(active)
    )
};

// Send struct as JSON body
User newUser = {1, "Pedro", true};
client.post("/users").body(newUser);

// Receive response directly into struct
User fetchedUser;
client.get("/users/1").getBody(&fetchedUser);

PUT and DELETE

// Update a resource
client.update("/lights/1").body("state", "OFF");

// Delete a resource
client.del("/logs/system_error.log");

SOAP 1.1 & SOAP 1.2 Web Services

Consume SOAP web services with automatic envelope generation and streaming XML response extraction:

// SOAP 1.1 request
String result;
client.soap("/ws")
      .soapAction("http://example.org/GetPrice")
      .body("<m:GetPrice xmlns:m=\"http://example.org\"><m:Item>ESP32</m:Item></m:GetPrice>")
      .getBody("Price", &result);

// SOAP 1.2 request with SOAP Fault handling
SoapFault fault;
float price = 0.0f;
client.soap("/ws")
      .version(SOAP_1_2)
      .action("http://example.org/GetPrice")
      .body("<m:GetPrice xmlns:m=\"http://example.org\"><m:Item>ESP32</m:Item></m:GetPrice>")
      .getFault(&fault)
      .getBody("Price", &price)
      .onFault([](const SoapFault& f) {
          Serial.printf("SOAP Fault: [%s] %s\n", f.faultCode.c_str(), f.faultString.c_str());
      });

JSON-RPC 2.0 Requests

Consume JSON-RPC 2.0 services over HTTP/HTTPS with automatic envelope serialization, positional or named parameters, struct mapping, and notifications:

// 1. Positional parameters (array: [15, 27])
int sum = 0;
client.jsonRpc("/rpc")
      .method("add")
      .param(15)
      .param(27)
      .id(1)
      .getResult(&sum);

// 2. Named parameters (object: {"minuend": 42, "subtrahend": 23})
int subResult = 0;
client.jsonRpc("/rpc")
      .method("subtract")
      .param("minuend", 42)
      .param("subtrahend", 23)
      .id("req-sub")
      .getResult(&subResult)
      .onJsonRpcError([](const JsonRpcError& err) {
          Serial.printf("JSON-RPC Error %d: %s\n", err.code, err.message.c_str());
      });

// 3. Mapped structs in params and result
struct SensorReading {
    char device[32] = {0};
    float temp = 0.0f;
    REST_JSON_MAP(REST_FIELD(device), REST_FIELD(temp))
};

SensorReading input = {"DHT22", 23.5f};
SensorReading output;
client.jsonRpc("/rpc")
      .method("processReading")
      .setParams(input)
      .getResult(&output);

// 4. Nested result fields and raw extraction
char city[32] = {0};
String rawJson;
client.jsonRpc("/rpc")
      .method("getUserProfile")
      .param("id", 101)
      .getResult("address.city", city, sizeof(city))
      .getRawResult(&rawJson);

// 5. Notifications (no "id" property, no response parsed)
client.jsonRpc("/rpc")
      .method("telemetryPing")
      .asNotification()
      .param("device", "esp32-node-1");

Examples

Runnable sketches are available in the examples/ directory:

Sketch Description
JsonRpcBasic JSON-RPC 2.0 requests with positional/named parameters, notifications, and struct mapping.
JsonRpcErrorHandling Handling standard JSON-RPC 2.0 error responses, inspection, and error callbacks.
SoapBasic Consuming SOAP 1.1 and SOAP 1.2 web services with automatic envelopes and streaming XML responses.
SoapFaultHandling Detecting and handling SOAP 1.1 and SOAP 1.2 faults, inspecting codes, reasons, and callbacks.
RestCrud Full suite of REST CRUD operations (GET, POST, PUT, PATCH, DELETE) in a single sketch.
StructJson Bidirectional C++ Struct <-> JSON serialization and HTTP transfer.
UrlParameters Path parameters (/users/{id}) and query parameters (?page=2).
AuthHelpers Bearer token, Basic Auth, and API Key authentication headers.
CallbacksAndErrors Callbacks, timeouts, automatic retries, error handling, and runtime URL change.
PortSelection Connecting to a server on a custom port.
NestedJSON Extracting fields from deeply nested objects.
ArrayJSON Addressing array elements by index.
RawArrayJSON Capturing raw arrays or objects into Arduino Strings.
UnixTimestamp Fetching the current Unix Timestamp from an API.
HttpsTls TLS / HTTPS requests with Root CA certificate validation, NTP time sync, session reuse, and insecure mode.

API Reference

Methods and examples for the client and its request builders.

ESP32HTTPClient — Client class

The main entry point. Instantiate once with your base URL and reuse across requests.

Constructor

Signature Description Example
ESP32HTTPClient(baseUrl) Creates a client. Port defaults to 80 (HTTP) or 443 (HTTPS). ESP32HTTPClient client("https://api.example.com");
ESP32HTTPClient(baseUrl, port) Creates a client targeting a custom port. ESP32HTTPClient client("http://192.168.1.100", 8080);

HTTP request methods

Each method returns a RestRequest that can be chained with .query(), .body(), and .getBody().

Method Description Example
get(path) Sends a GET request to baseUrl + path. client.get("/todos/1")
post(path) Sends a POST request to baseUrl + path. client.post("/users")
put(path) Sends a PUT request to baseUrl + path. client.put("/users/1")
update(path) Alias for put(). client.update("/lights/1")
patch(path) Sends a PATCH request to baseUrl + path. client.patch("/config/wifi")
del(path) Sends a DELETE request to baseUrl + path. client.del("/logs/old.log")
soap(path) Initiates a SOAP 1.1 or 1.2 request. Returns SoapRequest. client.soap("/ws")
graphql(path) Initiates a GraphQL POST request. Returns GraphQLRequest. client.graphql("/graphql")
jsonRpc(path) Initiates a JSON-RPC 2.0 request. Returns JsonRpcRequest. client.jsonRpc("/rpc")

Configuration & Authentication methods

Method Description Example
bearer(token) Sets persistent Bearer token authorization header. client.bearer("my-token");
basic(user, password) Sets persistent Basic authorization header (auto Base64 encoded). client.basic("admin", "123456");
apiKey(name, key) Sets persistent API key header. client.apiKey("x-api-key", "my-key");
setHeader(name, value) Registers a custom HTTP header that is sent with every subsequent request. client.setHeader("Authorization", "Bearer mytoken123");
setBaseUrl(url, port) Changes the base URL and target port at runtime. client.setBaseUrl("https://api.v2.com", 443);
setUrl(url) Changes the base URL at runtime. client.setUrl("http://192.168.1.100");
setPort(port) Changes the target TCP port at runtime. client.setPort(8080);
setTimeout(timeoutMs) Sets default request timeout in milliseconds (default: 60000, 1 min). client.setTimeout(10000);
setMaxRetry(maxRetry) Sets default max retries on network failure (default: 1). client.setMaxRetry(3);
setContentType(contentType) Overrides the Content-Type header used for request bodies. Defaults to application/json. client.setContentType("application/x-www-form-urlencoded");
setCACert(rootCA) Configures Root CA certificate in PEM format for TLS validation (stored by pointer). client.setCACert(root_ca);
setCACertBundle(bundle, size) Configures an X.509 certificate bundle (ESP-IDF format). client.setCACertBundle(bundle);
setClientCert(cert, key) Configures client certificate and private key for mutual TLS (mTLS). client.setClientCert(cert, key);
setInsecure() Disables TLS certificate and hostname validation (logs a warning; use only for testing). client.setInsecure();
requireValidatedTls(require) Enables/disables fail-closed mode: refuses HTTPS requests (-12) unless a Root CA or CA bundle is configured. client.requireValidatedTls(true);
isValidatedTlsRequired() Returns true if fail-closed TLS validation mode is active. bool req = client.isValidatedTlsRequired();
hasValidatedTlsConfig() Returns true if a non-empty Root CA or CA bundle is configured and setInsecure() is not active. if (client.hasValidatedTlsConfig()) { ... }
getTlsError() Returns last mbedTLS error code (int), or 0 if none. int err = client.getTlsError();
getTlsErrorMessage() Returns descriptive message and hex code for last mbedTLS error. String msg = client.getTlsErrorMessage();
getTlsErrorHint() Returns actionable troubleshooting hint for last TLS failure (checks NTP clock < 2023, CA, expiry, hostname). String hint = client.getTlsErrorHint();
tlsErrorToHint(err, epoch) Static helper mapping an mbedTLS error code and Unix epoch to an actionable hint. String h = ESP32HTTPClient::tlsErrorToHint(err);
getStatusCode() Returns the HTTP status code of the last completed request. int code = client.getStatusCode();
isSuccess() Returns true if last request status was 2xx (200 <= code < 300). if (client.isSuccess()) { ... }
hasError() Returns true if last request had network error or HTTP error (code >= 400). if (client.hasError()) { ... }
getErrorMessage() Returns descriptive error string (enriched with mbedTLS code and diagnostic hint on TLS failures). String err = client.getErrorMessage();
onSuccess(cb) Registers client-level callback for successful requests (2xx). client.onSuccess([](int code){ ... });
onError(cb) Registers client-level callback for failed requests. client.onError([](int code, const char* msg){ ... });
onResponse(cb) Registers client-level callback executed on every completed request. client.onResponse([](int code){ ... });
toJson(struct) Static utility to serialize a mapped struct into JSON string. String json = ESP32HTTPClient::toJson(user);
fromJson(json, struct) Static utility to populate a struct from a JSON string. ESP32HTTPClient::fromJson(json, &user);
end() Closes the persistent TCP/TLS connection and frees its memory buffers. Useful after a burst of requests. client.end();

RestRequest — Fluent request builder

Returned by every HTTP method on ESP32HTTPClient. All builder methods return RestRequest&, enabling fluent chaining. The underlying HTTP request is dispatched on the first call to .getBody(), or automatically when the object goes out of scope.

Building the request

Method Description Example
path(key, value) Replaces a {placeholder} in the URL path. Supports String, const char*, int, long, float, double, and bool. Chainable. client.get("/users/{id}").path("id", 15)
query(key, value) Appends a URL query parameter. Supports String, const char*, int, long, float, double, and bool. Chainable. client.get("/users").query("page", 2).query("limit", 20)
body(key, value) Adds a field to the JSON request body. Supports the same types as query(). Chainable. client.post("/users").body("name", "Pedro").body("age", 21)
body(struct) Sets full JSON request body serialized from a mapped struct. Chainable. client.post("/users").body(user)
timeout(ms) Overrides timeout for this specific request in milliseconds. Chainable. client.get("/data").timeout(2000)
retry(maxRetry) Overrides max retry attempts for this specific request. Chainable. client.get("/data").retry(3)
onSuccess(cb) Per-request success callback (2xx). Chainable. client.get("/users").onSuccess([](int c){ ... })
onError(cb) Per-request error callback (code < 200 || code >= 400). Chainable. client.get("/users").onError([](int c, const char* m){ ... })
onResponse(cb) Per-request callback executed on completion. Chainable. client.get("/users").onResponse([](int c){ ... })

Extracting the response

getBody() is overloaded for each supported C type. It registers a binding between a JSON key path and a target variable. Use dot notation for nested fields and numeric segments for array indices.

getHeader() is overloaded for each supported C type to extract HTTP response headers directly (e.g., token, Content-Type, Date). Header lookups are case-insensitive.

Method Description Example
getBody(key, int* target) Binds a JSON integer to *target. client.get("/data").getBody("count", &myInt)
getBody(key, float* target) Binds a JSON number to a float. client.get("/sensor").getBody("temp", &myFloat)
getBody(key, double* target) Binds a JSON number to a double. client.get("/sensor").getBody("voltage", &myDouble)
getBody(key, bool* target) Binds a JSON boolean to *target. client.get("/status").getBody("active", &myBool)
getBody(key, long* target) Binds a JSON integer to a long. client.get("/stats").getBody("timestamp", &myLong)
getBody(key, char* target, size_t maxLen) Copies a JSON string into a char buffer, up to maxLen bytes. client.get("/user").getBody("name", myChar, sizeof(myChar))
getBody(key, String* target) Copies a raw JSON object or array into an Arduino String. Pass "" to capture the entire response. client.get("/users").getBody("", &entireJson)
getBody(struct* target) Binds and populates a mapped struct directly from root JSON response. client.get("/users/1").getBody(&user)
getBody(key, struct* target) Binds and populates a mapped struct from nested JSON object path. client.get("/profile").getBody("data.user", &user)
getHeader(name, target) Extracts an HTTP response header into target (String*, char*/char[N], int*, long*, float*, double*, bool*). Case-insensitive. client.get("/auth").getHeader("token", &token)

Note

If a key or header is missing, the target variable is left unchanged. No exception is thrown and no crash occurs.

Full chaining example

int userId;
float temperature;
char city[32];
String token;

client.post("/report")
      .body("device", "esp32-cam")
      .body("floor", 3)
      .timeout(3000)
      .retry(2)
      .onSuccess([](int code) { Serial.printf("OK: %d\n", code); })
      .onError([](int code, const char* msg) { Serial.printf("Fail (%d): %s\n", code, msg); })
      .getHeader("token", &token)              // String — response header
      .getBody("userId", &userId)            // int — root field
      .getBody("sensor.temp", &temperature)  // float — nested object
      .getBody("0.address.city", city, sizeof(city)); // char* — array index + nested

JsonRpcRequest — JSON-RPC 2.0 request builder

Returned by client.jsonRpc(path). Provides a fluent builder for JSON-RPC 2.0 single calls and notifications with streaming response binding.

Building the request

Method Description Example
method(name) Sets the JSON-RPC 2.0 method name. client.jsonRpc("/rpc").method("subtract")
param(value) Appends a positional parameter (array element). req.param(15).param("test")
param(key, value) Sets a named parameter (object key-value pair). req.param("id", 42)
addParam(value) Explicit alias for appending a positional parameter. req.addParam(100)
setParam(key, value) Explicit alias for setting a named parameter. req.setParam("subtrahend", 23)
setParams(struct) Serializes a mapped struct (REST_JSON_MAP) as named parameters. req.setParams(sensorData)
rawParams(json) Sets the raw JSON string for params (object or array). req.rawParams("{\"a\":1}")
id(value) Sets request ID (int, long, const char*, or nullptr for JSON null). req.id(1) or req.id("req-99")
notification() / asNotification() Marks request as notification (omits id, no response expected). req.asNotification()
header(name, value) Adds a custom HTTP header for this request. req.header("X-Custom", "val")
timeout(ms) Overrides timeout for this request in milliseconds. req.timeout(5000)
retry(maxRetry) Overrides max retry attempts for this request. req.retry(2)
onSuccess(cb) Callback invoked on HTTP 2xx response. req.onSuccess([](int c){ ... })
onError(cb) Callback invoked on network or HTTP error. req.onError([](int c, const char* m){ ... })
onJsonRpcError(cb) Callback invoked when the server returns a JSON-RPC error object. req.onJsonRpcError([](const JsonRpcError& e){ ... })

Extracting the response

Method Description Example
getResult(target) Binds primitive (int*, float*, bool*, String*, char*) or struct to the root "result". req.getResult(&myInt)
getResult(path, target) Binds a nested result field or array element via dot notation. req.getResult("user.name", &name)
getRawResult(String*) Captures the entire "result" payload as raw JSON. req.getRawResult(&rawJson)
getRawResponse(String*) Captures the complete HTTP response body as raw JSON. req.getRawResponse(&body)
getError(JsonRpcError*) Populates a JsonRpcError struct with code, message, and data. req.getError(&err)

Error Codes and HTTP Status Codes

Inspect the last request with getStatusCode(), isSuccess(), hasError(), or getErrorMessage().

Code Meaning Category
-1 Connection Refused Client error
-2 Send Header Failed Client error
-3 Send Payload Failed Client error
-4 Not Connected Client error
-5 Connection Lost Client error
-6 No Stream Client error
-7 No HTTP Server Client error
-8 Too Less RAM Client error
-9 Encoding Error Client error
-10 Stream Write Error Client error
-11 Read Timeout Client error
-12 TLS Validation Required Client error (fail-closed TLS)
200 OK HTTP success
201 Created HTTP success
202 Accepted HTTP success
204 No Content HTTP success
400 Bad Request HTTP client error
401 Unauthorized HTTP client error
403 Forbidden HTTP client error
404 Not Found HTTP client error
405 Method Not Allowed HTTP client error
408 Request Timeout HTTP client error
409 Conflict HTTP client error
429 Too Many Requests HTTP client error
500 Internal Server Error HTTP server error
501 Not Implemented HTTP server error
502 Bad Gateway HTTP server error
503 Service Unavailable HTTP server error
504 Gateway Timeout HTTP server error
0 Not Executed Internal state

Generic Fallback Behavior

  • Negative unknown codes → Unknown Client Error
  • 200–299 → Success
  • 300–399 → Redirection
  • 400–499 → Client Error
  • 500–599 → Server Error
  • Other values → Unknown HTTP Status

Example

A short usage example showing how applications can handle both transport errors and HTTP errors:

int status = client.get("/api/data").getStatusCode();

if (client.isSuccess()) {
    // Handle successful response
} else {
    Serial.println(client.getErrorMessage());
}

Support

Need help, found a bug, or want to contribute?


Built with the community.

ESP32 HTTP Client contributors

If this library saved you time, consider leaving a star ⭐

About

A lightweight, low-allocation, high-performance HTTP client library for ESP32.

Topics

Resources

Contributing

Security policy

Stars

165 stars

Watchers

1 watching

Forks

Releases

Used by

Contributors

Languages