geode.utils.web exposes Geode async web requests to Lua.
Requests run on the web worker and callbacks run on the main thread.
This binding does not expose openLinkUnsafe or any *Sync method, and file writes stay inside mod sandbox roots.
For signatures, use editor autocomplete from type stubs. Threading and Security are covered below.
geode.utils.web.openLinkInBrowser(url) opens a URL in the system browser.
newRequest() returns a chainable WebRequest builder.
Shortcut helpers get, post, put, patch, and fetch accept a URL, optional RequestOptions, and a callback.
The shortcut request functions also accept callback as the second argument when no options are needed.
See globals Error shapes.
fetch has three forms:
fetch(url, callback)- GET tourl.fetch(url, options, callback)- applyoptions, then send.methoddefaults toGET.fetch(options, callback)- readurlfromoptions(required).methoddefaults toGET.
multipart() returns a MultipartForm builder for file uploads.
Userdata types include WebRequest, WebResponse, WebHandle, MultipartForm, and WebListenerHandle.
Request timings are in milliseconds.
See Security for certVerification and TLS behavior.
WebRequest is a chainable builder.
Set headers, params, method, URL, body, proxy, timeout, progress callback, and other options through fluent methods.
Send methods are async and return a WebHandle.
Drop the handle or call :cancel() to cancel a pending request.
local req = geode.utils.web.newRequest()
req:header("Accept", "application/json")
:timeout(30)
:get("https://example.com/data", function(response)
print(response:code())
end)Status checks:
:info()- 1xx response.:ok()- 2xx response.:redirected()- followed a redirect.:badClient()- 4xx response.:badServer()- 5xx response.:error()- network or transfer error.:cancelled()- request was cancelled.
Read the body with :text(), :json(), or :bytes().
:saveTo(root, path) writes to a sandbox root ("save", "config", or "persistent").
:timings() returns per-phase timing in milliseconds.
Response bodies are capped in the Lua binding after download completes.
Geode does not expose a pre-download or streaming size cap to LuauAPI, so an oversized body may be fully transferred before Luau rejects it.
:text(), :bytes(), :json(), and :saveTo() return nil and an error when the body is too large.
Async callbacks and response listeners receive (response?, err?).
:cancel() returns true if the request was still pending.
:id() returns the Geode request id, or nil if there is no active task.
Build multipart bodies with :param, :file, and :fileFrom.
fileFrom uses sandbox roots only. Raw paths are not allowed.
local form = geode.utils.web.multipart()
form:param("name", "value")
form:file("upload", bytes, "upload.bin", "application/octet-stream")
form:fileFrom("config", "config", "data.json", "application/json")Register intercept and response listeners with onRequestIntercept, onResponse, and the For / ById variants.
Each registrar takes an optional priority (higher runs first).
Return true to stop other listeners on the main thread.
See Threading below for off-thread behavior.
onResponse* callbacks receive (response?, err?), or (modID, response?, err?) for the global listener.
Disconnect with WebListenerHandle:disconnect().
local handle = geode.utils.web.onRequestIntercept(function(modID, request)
return false
end)
handle:disconnect()HTTP work runs on Geode's web worker. Lua callbacks run on the main thread. See Getting started for the shared runtime threading rule.
Web callbacks share the hook callback budget. See Limits and errors. Use tasks and time to schedule work around callbacks.
Request intercept:
onRequestIntercept*runs on the main thread so Lua can change the request before send.- Off the main thread, a registered intercept cannot run and the request is blocked. Geode logs a one-time warning.
- If an intercept callback errors, the request is blocked.
Response listeners:
onResponse*may fire on the web worker. Lua runs later on the main thread.- Off the main thread, listeners are side effects only. They cannot stop other listeners.
- On the main thread, return
trueto stop the next listeners. - If a response listener callback errors, LuauAPI logs it and keeps response handling best-effort.
Progress:
:onProgressruns on the main thread. Worker events are queued first.
Request and response bodies are capped.
These body sources count toward the request cap: RequestOptions body fields, WebRequest body methods, and MultipartForm builders.
Over the cap, failure follows globals Error shapes.
See Limits and errors for body, request, and concurrency caps.
HTTP requests are not sandboxed. LuauAPI is not an egress firewall. Any script can call any URL the Geode web stack accepts, including public hosts, private IPs, and localhost. LuauAPI does not block schemes beyond what Geode allows. There is no URL or host allowlist or blocklist in LuauAPI.
File I/O stays inside mod sandbox roots:
:saveToonWebResponse:fileFromonMultipartForm
TLS:
- Setting
certVerificationto false disables certificate verification for that request. - Scripts may disable TLS verification for development, for example self-signed certificates.
- Disabling verification is unsafe for production traffic and should not be used outside trusted dev setups.
Listeners:
onRequestInterceptandonResponsesee traffic from every loaded mod.- Callbacks get the mod id as the first argument.
onRequestInterceptFor,onResponseFor, and theByIdvariants filter to one mod or request.- Any mod can still register a global listener.
- Do not put secrets in URLs, headers, or bodies when other mods may be loaded.
For WebSocket LAN exposure with host = "0.0.0.0", see websocket.
See LuauAPI mod guidelines for loadstring and network abuse rules.
The authoritative argument lists live in the generated type stubs, surfaced as editor autocomplete.
See type stubs.
Handwritten extras are in tools/luau_codegen/extra_bindings/web.dluau.
- geode.utils
- type stubs
- websocket
- fs
- Getting started
- Examples
- globals
- tasks and time
- LuauAPI mod guidelines
- Limits and errors
src/bindings/geode/web/GeodeWebCore.cppsrc/bindings/geode/web/GeodeWebApi.cppsrc/bindings/geode/web/GeodeWebListeners.cpptools/luau_codegen/extra_bindings/web.dluau