HTTP
The http library provides asynchronous HTTP GET and POST requests. Requests run on background threads and deliver responses via callbacks on the next tick, so they never block your script.
Useful for fetching player or Steam data, calling external APIs, integrating with AI services, or anything that needs network access.
Kill switch
HTTP can be turned off in Dev Studio (the bug icon in the navbar), General page, Security panel, Allow Lua HTTP Requests (on by default). When off, every http.get and http.post fails immediately with error = "http requests are disabled". A script cannot bypass it.
http.get(url, opts, callback)
Performs an asynchronous HTTP GET request.
Signatures:
http.get(url, opts, callback)
http.get(url, callback)The two-argument form omits opts and uses all defaults (no custom headers, default timeout, default user-agent).
Parameters:
url(string). The URL to request.opts(table, optional). Configuration:headers(table). Key-value pairs of request headers.user_agent(string). Custom User-Agent string. Default:"TsukiLua/1.0".timeout(number). Request timeout in milliseconds. Default:5000.
callback(function). Called with a response table when the request completes.
Examples:
-- With opts
http.get("https://api.example.com/data", {
headers = {
["Authorization"] = "Bearer my_token",
["Accept"] = "application/json"
},
timeout = 3000
}, function(res)
if res.ok then
print("Got:", res.body)
else
print("Failed:", res.status, res.error)
end
end)-- Without opts (two-argument form)
http.get("https://api.example.com/data", function(res)
if res.ok then
print("Got:", res.body)
end
end)http.post(url, opts, callback)
Performs an asynchronous HTTP POST request.
Signatures:
http.post(url, opts, callback)
http.post(url, callback)The two-argument form omits opts and sends no body with all defaults.
Parameters:
url(string). The URL to request.opts(table, optional). Configuration:headers(table). Key-value pairs of request headers.body(string). The request body content.user_agent(string). Custom User-Agent string. Default:"TsukiLua/1.0".timeout(number). Request timeout in milliseconds. Default:5000.
callback(function). Called with a response table when the request completes.
Examples:
-- With opts
http.post("https://api.example.com/submit", {
headers = {
["Content-Type"] = "application/json"
},
body = '{"username": "player1", "score": 100}'
}, function(res)
if res.ok then
print("Submitted successfully")
else
print("Error:", res.status, res.error)
end
end)-- Without opts (two-argument form)
http.post("https://api.example.com/ping", function(res)
print("Status:", res.status)
end)Response table
The callback receives a single table with the following fields:
| Field | Type | Description |
|---|---|---|
status | number | HTTP status code (200, 404, etc.). 0 if the connection failed entirely. |
body | string | Response body as a string. Truncated to 1 MB if larger. |
ok | boolean | true if status is in the 200-299 range. |
error | string | Transport-level error message (DNS failure, connection refused, timeout, TLS failure). Empty whenever the server answered at all, including non-2xx. A 404 comes back as status = 404, ok = false, error = "". Branch on res.ok or res.status to detect an HTTP error status. res.error ~= "" only catches requests that never reached the server. |
Limits
- At most 4 requests in flight, counted across every loaded script. Past that the callback fires immediately, inside the
http.getorhttp.postcall, withstatus = 0,body = "",ok = falseanderror = "too many concurrent requests". - Default timeout is 5 seconds. Configurable per-request via
opts.timeout. - Response bodies are capped at 1 MB.
- Requests are fully asynchronous. They never block
on_tick. - SSL certificate verification is always on and cannot be turned off.
optsaccepts onlyheaders,body,user_agentandtimeout, and there is noverify_sslflag. A host with a bad or self-signed certificate fails with a TLS error inresponse.error.
Notes
Callbacks are delivered at the start of the next tick after the request completes, not during
on_tick.Callbacks fire on the Lua thread, but not inside your script's context. The engine drains them at the top of the tick, before any script runs, and does not restore the owning script's identity first. In practice:
config.get_*always returns the default you passed, andconfig.set_*is a silent no-op.storage.get/storage.set/storage.keysread and write whichever script ticked last, not necessarily yours. With more than one script enabled that is the wrong file.printstill works, but its[script]prefix names that same last-ticked script. The engine creditsui.*calls to it too.
Everything else is live:
input.*,memory.*,draw.*and the player/entity API all behave normally inside a callback. If the response needs to touch config or storage, stash it in a Lua variable and do the work fromon_tick.Reloading (the Reload button, or saving a file) tears the Lua VM down and discards any callback still in flight. Disabling a script does not: its requests still complete and their callbacks run against the old environment, so if you re-enable before a response lands, whatever the callback stores is invisible to the new instance.
Use
http.postwithContent-Type: application/jsonfor JSON APIs. Thebodyfield is sent as-is, format it as needed.