Skip to main content

Http module

The Http module is an HTTP client: it makes GET and POST requests to an address and returns what the server answers. Use it to talk to REST APIs, web services or devices on your network. The headers you set apply to every following request, and status() and error() tell how the last one went.

use Http

JARU HTTP module screenshot for making web requests from connected ESP32 projects

Platforms​

WindowsESP32 / ESP32-S3ESP32-P4ESP32-S31
The networkThe PC'sThe WiFi in the board profileSameSame
HTTPSYes, and Windows checks the server's certificateYes, but without checking the server's certificateYes, and it checks the server's certificateSame
Redirects (301, 302…)Follows them and returns the final pageDoesn't follow them: returns the redirect's responseSameSame
Http.status() when there's no response0, or a negative number from -1 to -5A negative number, from -1 to -11 (0 if the address can't be understood)0, or -1 if the response doesn't arrive0, or -1 if the response doesn't arrive
With Content-Type in setHeader, a post sendsThe one from setHeaderThe one from postThe one from setHeaderThe one from setHeader

There's no WiFi module: the board connects by itself when it starts, with the network in its profile in ESP32 Tools. It's all in Display, sound, SD and network.

Functions​

FunctionWhat it doesReturns
Http.get(url)Makes a GET requestThe body of the response, or nil if there's no response
Http.post(url, body) / Http.post(url, body, type)Makes a POST requestThe body of the response, or nil if there's no response
Http.setHeader(name, value)Sets a header for the following requeststrue
Http.clearHeaders()Removes every headertrue
Http.status()The status code of the last requestAn integer
Http.error()The error of the last requestA string; empty if there was no error
Http.timeout() / Http.timeout(ms)Reads or changes the waiting timeThe milliseconds, or true when changing it

While it waits for the response, the program stops: the screen isn't painted and the callbacks of Timer, MQTT or GPIO don't run.

If you pass them arguments that aren't the ones they expect, none of them throws an exception: they return an error value, which try doesn't catch (see Errors and exceptions).

get(url)​

Requests the address url, which starts with http:// or https://, and returns the body of the response as a string.

  • If the server answers, it returns what it sends, also if it answers with an error such as 404 or 500. Check Http.status() to know whether it went well.
  • If there's no response (no network, the server can't be found, time runs out…), it returns nil, and Http.error() says what happened.
use Http

var response = Http.get("https://api.example.com/data")

if (response == nil) then
println("No response: ", Http.error())
elsif (Http.status() == 200) then
println("Response: ", response)
else
println("The server answers ", Http.status())
end

post(url, body, type)​

Sends body, a string, to the address url with a POST request. type is optional: the type of the content, sent in the Content-Type header; if you leave it out, "application/json". It returns the same as get.

use Http

var data = '{"temperature": 25.5, "humidity": 60}'
var response = Http.post("https://api.example.com/sensors", data)

if (response != nil) then
println("The server answers ", Http.status(), ": ", response)
else
println("POST error: ", Http.error())
end

With another content type, for instance a form:

use Http

var form = "user=admin&password=secret"
var response = Http.post("https://api.example.com/login", form, "application/x-www-form-urlencoded")

If you've set a Content-Type header with setHeader, on Windows, the P4 and the S31 that one is sent, and on the ESP32 and ESP32-S3, the one from post.

setHeader(name, value)​

Sets the header name to value on every following request, until you call clearHeaders(). If there was already one with that name, it changes its value. It returns true.

use Http

Http.setHeader("Authorization", "Bearer my_token")
Http.setHeader("X-API-Key", "secret_key")

// Both headers go in this request and in the following ones
var response = Http.get("https://api.example.com/private")

clearHeaders()​

Removes every header you set with setHeader and returns true. Use it when you move from one API to another.

use Http

Http.setHeader("Authorization", "Bearer api_1_token")
Http.get("https://api1.example.com/data")

Http.clearHeaders()
Http.setHeader("X-Custom-Header", "another_value")
Http.get("https://api2.example.com/other-data")

status()​

Returns the HTTP status code of the last request: the number the server answers with.

CodeMeaning
200OK
201Created
301, 302The page has moved (on the boards, which don't follow redirects)
400The request is malformed
401You need to authenticate
403You don't have permission
404It doesn't exist
500Server error

If there was no response, it's 0 or a negative number, depending on the platform (see Platforms). Before the first request, it's 0.

use Http

var response = Http.get("https://api.example.com/user/123")
var code = Http.status()

switch (code)
case 200:
println("User: ", response)
case 404:
println("That user doesn't exist")
case 401:
println("Not authorized: check the token")
default:
println("Response code: ", code)
end

error()​

Returns the error of the last request, or an empty string if there was none. A response with an error code (404, 500…) isn't an error for error(): the server has answered.

MessageWhereWhen
Invalid URLWindowsThe address can't be understood.
Connection errorWindowsIt can't connect to the server.
Network errorWindowsThe response doesn't arrive in time, or another network failure.
Protocol error, Read error, Write error, Generic errorWindowsThe conversation with the server fails.
begin() failedESP32 and ESP32-S3The address can't be understood.
GET failed, POST failedEvery boardThe request can't be made: no network, the server can't be found, time runs out… On the ESP32 and ESP32-S3, Http.status() gives the reason with its negative number.
init() failed, write() shortESP32-P4 and ESP32-S31The request can't be prepared, or the whole body can't be sent.
The name of the chip's error (ESP_ERR_…)ESP32-P4 and ESP32-S31The connection can't be opened.
use Http

var response = Http.get("https://server-that-does-not-exist.example/api")
if (response == nil) then
println("The request failed: ", Http.error())
end

timeout(ms)​

Without arguments, it returns the longest time each request waits, in milliseconds: by default, 10000 (10 seconds). With ms, an integer, it changes it and returns true; a negative value counts as 0.

On Windows, the request is given up once that time and one more second have gone by.

use Http

println("Wait: ", Http.timeout(), " ms") // Wait: 10000 ms
Http.timeout(30000) // 30 seconds, for a slow API

Complete example​

A sensor that sends its reading to an API every 30 seconds:

use Http, GPIO

Http.timeout(15000)
Http.setHeader("X-Device-ID", "esp32-sensor-001")

var sensorPin = 34
GPIO.pinmode(sensorPin, GPIO.INPUT)

while (true)
var reading = GPIO.aread(sensorPin)
var temperature = reading * 0.1 // your sensor's conversion

var body = '{"device": "esp32-001", "temp": ' + temperature.toString() + '}'
var response = Http.post("https://api.myserver.com/telemetry", body)

if (response == nil) then
println("No response: ", Http.error())
elsif (Http.status() == 200 or Http.status() == 201) then
println("Sent")
else
println("The server answers ", Http.status())
end

pause(30000)
end

With authentication​

First it asks for a token, and then it sends it in the Authorization header. It reads the response with JSON:

use Http, JSON

var login = '{"email": "user@example.com", "password": "pass123"}'
var response = Http.post("https://api.example.com/auth/login", login)

if (response == nil or Http.status() != 200) then
println("I couldn't log in: ", Http.status(), " ", Http.error())
exit(1)
end

var session = JSON.parse(response)
Http.setHeader("Authorization", "Bearer " + session["token"])

var profile = Http.get("https://api.example.com/users/profile")
if (profile != nil and Http.status() == 200) then
var data = JSON.parse(profile)
println("Hello, ", data["name"])
end

With retries​

It retries if there's no response or if the server fails (codes 500 and up), but not if the error is in the request:

use Http

func fetch(url)
for (var attempt = 1; attempt <= 3; attempt++)
var response = Http.get(url)
if (response == nil) then
println("No response (", Http.error(), "), attempt ", attempt)
elsif (Http.status() >= 500) then
println("Server error (", Http.status(), "), attempt ", attempt)
elsif (Http.status() >= 400) then
println("Request error: ", Http.status())
return nil
else
return response
end
pause(2000)
end
return nil
end

var data = fetch("https://api.example.com/data")
if (data != nil) then
println("Data: ", data)
end

Pitfalls and known issues​

  • A 404 doesn't give nil. get and post return whatever the server answers, whatever the code: always check Http.status().
  • Argument errors aren't caught. An argument of another type, such as Http.get(5), doesn't throw an exception: it returns an error value.
  • The program waits. A request stops the program until the response arrives or time runs out: in a game, the screen freezes for that long.
  • Headers stay. What you set with setHeader goes in every following request, to other servers too. Use clearHeaders() when you change API.
  • HTTPS on the ESP32 and ESP32-S3 doesn't check the certificate. The connection is encrypted, but the board doesn't check that the server is who it says it is. Don't send data that must be protected this way.
  • Redirects on the boards. If the address redirects to another one (for instance, from http:// to https://), on a board you get code 301 or 302: put the final address in the program.

All the known issues of JARU, together, are in Known issues.