Advanced Usage¶
Authentication Helpers¶
ESP32HTTPClient provides dedicated helper methods for common authentication schemes. Like setHeader(), these helpers configure persistent headers sent with every subsequent request.
Bearer Token (JWT / OAuth)¶
client.bearer("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...");
client.get("/api/profile").getBody("username", username, sizeof(username));
HTTP Basic Auth¶
Pass your username and password; the library automatically formats and Base64-encodes the credentials:
API Key¶
Pass the header name and your API key:
client.apiKey("x-api-key", "my-secret-api-key");
client.get("/v1/sensors").getBody("temperature", &temp);
Custom HTTP Headers¶
Use setHeader() to register any arbitrary persistent header that will be sent with every subsequent request from that client instance:
// Custom headers
client.setHeader("X-Device-ID", "ESP32-001");
client.setHeader("X-Custom-Header", "custom-value");
// Custom headers are sent on all subsequent requests
client.get("/protected/resource").getBody("data", &myVar);
Header persistence
Headers registered with setHeader(), bearer(), basic(), or apiKey() persist for the lifetime of the client object. They are sent on every request. To change a header, call the method again with the new value.
Reading response headers
setHeader() sets headers to send with the request. To read or collect headers returned by the server in the response (such as token, Content-Length, Date, etc.), use the .getHeader() method on RestRequest. See how to collect them in the Reading Response Headers guide or the RestRequest::getHeader API reference.
Custom Content-Type¶
By default, the Content-Type header is set to application/json for all requests with a body. Override it with setContentType():
client.setContentType("application/x-www-form-urlencoded");
client.post("/form").body("field", "value");
Keep-Alive Connection Management¶
By default, ESP32HTTPClient enables HTTP Keep-Alive, reusing the underlying TCP/TLS connection across requests. This is the primary reason the library is ~12x faster than the standard approach, since expensive TLS handshakes only happen once.
When to call end()¶
After a burst of requests, if your sketch enters a long idle period or you want to free the TLS memory buffers (~45KB for an active connection), call end():
// Make several requests
client.get("/data1").getBody("val", &v1);
client.get("/data2").getBody("val", &v2);
client.get("/data3").getBody("val", &v3);
// Done for now — free the TLS memory
client.end();
delay(60000); // sleep for 60 seconds
// The next request will re-establish the connection automatically
client.get("/data4").getBody("val", &v4);
You don't need to call end() between requests
Keep-Alive is automatic. Only call end() when you explicitly want to free the connection memory after a period of inactivity.
Changing Server URL at Runtime¶
You can change the target host URL or port on the fly without recreating the ESP32HTTPClient instance:
ESP32HTTPClient client("https://api.v1.example.com");
// Switch to v2 endpoint or a local staging server
client.setBaseUrl("https://api.v2.example.com", 443);
// Or change port independently
client.setUrl("http://192.168.1.100");
client.setPort(8080);
Timeout Configuration¶
By default, the client uses a timeout of 60000 ms (1 minute / 60 seconds). You can configure the global default or override it on individual requests:
Global Timeout¶
Per-Request Timeout¶
Retries & Network Recovery¶
ESP32HTTPClient automatically handles network drops and stale Keep-Alive connections by retrying failed attempts up to a configured threshold. The default is 1 retry.
Global Max Retry¶
Per-Request Retry¶
// Disable retry for non-idempotent operation
client.post("/payment/charge")
.retry(0)
.body("amount", 100);
Callbacks (onSuccess, onError, onResponse)¶
Callbacks allow you to attach clean, asynchronous-style handlers to your requests or at the client level.
Request-Level Callbacks¶
client.get("/sensors/temp")
.onSuccess([](int code) {
Serial.printf("Success: HTTP %d\n", code);
})
.onError([](int code, const char* message) {
Serial.printf("Request failed (%d): %s\n", code, message);
})
.onResponse([](int code) {
Serial.printf("Completed with code %d\n", code);
})
.getBody("temperature", &temp);
Client-Level Callbacks¶
Client-level callbacks are triggered on every request executed by that client instance:
client.onError([](int code, const char* message) {
Serial.printf("[Global Error Handler] Code %d: %s\n", code, message);
});
Error Handling & Inspection¶
You can inspect the result of any request using the built-in helper methods:
client.get("/users/1").getBody("name", name, sizeof(name));
if (client.isSuccess()) {
Serial.println("User loaded successfully");
} else if (client.hasError()) {
int code = client.getStatusCode();
String errorMsg = client.getErrorMessage();
Serial.printf("Failed with code %d: %s\n", code, errorMsg.c_str());
}
Error Code Reference Table¶
Network / Client Errors (code < 0)¶
| Code | Constant | Description |
|---|---|---|
-1 |
HTTPC_ERROR_CONNECTION_REFUSED |
Target host refused the connection. |
-2 |
HTTPC_ERROR_SEND_HEADER_FAILED |
Failed to write HTTP headers to the socket. |
-3 |
HTTPC_ERROR_SEND_PAYLOAD_FAILED |
Failed to send request body payload. |
-4 |
HTTPC_ERROR_NOT_CONNECTED |
Not connected to network or socket. |
-5 |
HTTPC_ERROR_CONNECTION_LOST |
TCP connection terminated unexpectedly. |
-6 |
HTTPC_ERROR_NO_STREAM |
No response stream available. |
-7 |
HTTPC_ERROR_NO_HTTP_SERVER |
Server did not respond with valid HTTP. |
-8 |
HTTPC_ERROR_TOO_LESS_RAM |
Insufficient free RAM on ESP32. |
-9 |
HTTPC_ERROR_ENCODING |
Transfer encoding error. |
-10 |
HTTPC_ERROR_STREAM_WRITE |
Stream write failed. |
-11 |
HTTPC_ERROR_READ_TIMEOUT |
Timed out waiting for response data from server. |
HTTP Status Codes (code > 0)¶
| Code | Status | Description |
|---|---|---|
200 |
OK | Request succeeded normally. |
201 |
Created | Resource created successfully. |
202 |
Accepted | Request accepted for processing. |
204 |
No Content | Success, server returned empty response. |
400 |
Bad Request | Invalid request parameters or payload. |
401 |
Unauthorized | Missing or invalid authentication credentials. |
403 |
Forbidden | Insufficient permissions for resource. |
404 |
Not Found | Requested endpoint path does not exist. |
408 |
Request Timeout | Server timed out waiting for request. |
429 |
Too Many Requests | Rate limit exceeded. |
500 |
Internal Server Error | Generic server-side error. |
502 |
Bad Gateway | Invalid response from upstream server. |
503 |
Service Unavailable | Server overloaded or down for maintenance. |
504 |
Gateway Timeout | Upstream gateway timed out. |
Long Unix Timestamps¶
For Unix timestamps and other large integer values (greater than 2^31 - 1), use a long binding:
long unixTimestamp = 0;
client.get("/api/v1/time/current/unix")
.getBody("unix_timestamp", &unixTimestamp);
Serial.printf("Unix time: %ld\n", unixTimestamp);
Struct <-> JSON Mapping¶
ESP32-HTTP-Client provides direct bidirectional mapping between C++ structs and JSON payloads with zero dynamic document allocations, reflection overhead, or external dependencies.
Declaring Mappable Structs¶
Use the REST_JSON_MAP macro inside your struct to define its JSON fields:
struct User {
int id = 0;
char name[32] = {0};
float score = 0.0f;
bool active = false;
String email = "";
REST_JSON_MAP(
REST_FIELD(id),
REST_FIELD(name),
REST_FIELD(score),
REST_FIELD(active),
REST_FIELD(email)
)
};
If your JSON keys differ from the C++ member names, use REST_FIELD_NAMED:
struct Profile {
int userId = 0;
char fullName[32] = {0};
REST_JSON_MAP(
REST_FIELD_NAMED("user_id", userId),
REST_FIELD_NAMED("full_name", fullName)
)
};
For structs defined in external or 3rd-party libraries, use REST_JSON_MAP_EXT:
struct ExternalDevice {
int deviceId;
String status;
};
REST_JSON_MAP_EXT(ExternalDevice,
REST_FIELD_EXT(deviceId),
REST_FIELD_EXT(status)
)
Sending Structs in Request Body¶
Pass the struct directly into .body():
User user;
user.id = 15;
strncpy(user.name, "Pedro", sizeof(user.name));
user.score = 9.8f;
user.active = true;
user.email = "pedro@example.com";
// Automatically serialized to JSON: {"id":15,"name":"Pedro","score":9.8,"active":true,"email":"pedro@example.com"}
client.post("/users").body(user);
Receiving Structs from Response Body¶
Populate your struct directly from the HTTP response stream:
User user;
// Binds and populates struct directly from the root JSON response
client.get("/users/15").getBody(&user);
Serial.printf("User: ID=%d, Name=%s, Active=%d\n", user.id, user.name, user.active);
You can also bind to nested objects within the response JSON:
User nestedUser;
// Binds fields to "data.user.id", "data.user.name", etc.
client.get("/profile").getBody("data.user", &nestedUser);
Missing and Null Fields Handling¶
- If the JSON response is missing fields that exist in the struct, those struct members keep their default values.
- If the JSON contains extra fields that are not in the struct, they are safely ignored.
- If a JSON field has a
nullvalue, primitive numbers are reset to0, booleans tofalse, char buffers to"", and Strings to"".
Standalone Serialization & Deserialization (RestJson / toJson / fromJson)¶
You can also convert structs to and from JSON strings independently of HTTP requests:
User user;
user.id = 42;
strncpy(user.name, "Ana", sizeof(user.name));
// Struct -> JSON String
String jsonStr = ESP32HTTPClient::toJson(user);
// Or: String jsonStr = RestJson::toJson(user);
// JSON String -> Struct
User parsedUser;
ESP32HTTPClient::fromJson(jsonStr, &parsedUser);
// Or: RestJson::fromJson(jsonStr, &parsedUser);