HTTP for ESP32, the easy way, with a low memory footprint.
A lightweight, low-allocation HTTP client with direct stream-to-variable binding.
Documentation • Quick Start • API Reference • Examples • Support
- Why this library?
- Performance & Comparison
- Key Features
- Installation
- Quick Start
- Initialization
- Usage
- Examples
- API Reference
- Error Codes and HTTP Status Codes
- Support
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.
A typical request with the Arduino SDK looks like this:
- Initialize
HTTPClient. - Make the request.
- Check error codes.
- Call
http.getString(), allocating a largeStringon the heap. - Create a
DynamicJsonDocument(or heavy XML DOM tree), allocating even more RAM. - Call deserialization functions.
- Extract values manually.
- Hope you didn't run out of heap.
// 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);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.
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 witharduino-esp322.x and 3.x. - Full REST support —
GET,POST,PUT,PATCH, andDELETEare 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.
Choose the package manager used by your ESP32 project.
Add ESP32-HTTP-Client to the lib_deps section of your platformio.ini:
lib_deps =
PedroFnseca/ESP32-HTTP-Client@^1.4.0- Open Arduino IDE and go to Sketch → Include Library → Manage Libraries....
- Search for
ESP32-HTTP-Client. - Click Install.
Start with REST, or use the same client for SOAP, GraphQL, and JSON-RPC.
#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() {}#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() {}#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() {}#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() {}ESP32HTTPClient client("https://api.example.com");Specify the port as the second argument if your API runs on a non-standard port.
ESP32HTTPClient client("http://my-local-server.local", 8080);Build requests, bind response fields, and control connections with the fluent API.
// Produces: GET /users?page=2&limit=20&search=pedro
client.get("/users")
.query("page", 2)
.query("limit", 20)
.query("search", "pedro");Replace {placeholder} segments dynamically in the request path:
// Produces: GET /users/15
client.get("/users/{id}")
.path("id", 15);int newId;
// Body: { "name": "Pedro", "role": "admin", "age": 21 }
client.post("/users")
.body("name", "Pedro")
.body("role", "admin")
.body("age", 21)
.getBody("id", &newId);Use dot notation to navigate nested objects.
char val[32];
// Response: { "level0": { "level1": "val2" } }
client.get("/nested")
.getBody("level0.level1", val, sizeof(val));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 elementBind 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 objectWarning
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.
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 RAMThe 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());
}
}-
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/nullCopy 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). -
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.
- Single Root CA (
-
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.
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");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);// Update a resource
client.update("/lights/1").body("state", "OFF");
// Delete a resource
client.del("/logs/system_error.log");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());
});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");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. |
Methods and examples for the client and its request builders.
The main entry point. Instantiate once with your base URL and reuse across requests.
| 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); |
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") |
| 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(); |
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.
| 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){ ... }) |
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.
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 + nestedReturned by client.jsonRpc(path). Provides a fluent builder for JSON-RPC 2.0 single calls and notifications with streaming response binding.
| 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){ ... }) |
| 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) |
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 |
- 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
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());
}Need help, found a bug, or want to contribute?
- Documentation — guides and API reference.
- GitHub Issues — bugs and feature requests.
- GitHub Discussions — questions and ideas.
- Email support —
support@esp32httpclient.com.
