Skip to content
Development
Skill

/web-services

Use when calling web APIs or backends from Godot — HTTPRequest, REST/JSON clients, auth headers, downloads, TLS, WebSocketPeer clients, retries, and keeping secrets out of the build

BOOST
From plugin
godot-prompter
86261 skills9 agents1 hook
Install
$ npx -y skills add jame581/GodotPrompter --skill web-services --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/web-services

Context preview

The summary Claude sees to decide when to auto-load this skill.

Use when calling web APIs or backends from Godot — HTTPRequest, REST/JSON clients, auth headers, downloads, TLS, WebSocketPeer clients, retries, and keeping secrets out of the build

SKILL.md

web-services.SKILL.md
name: web-services
description: Use when calling web APIs or backends from Godot — HTTPRequest, REST/JSON clients, auth headers, downloads, TLS, WebSocketPeer clients, retries, and keeping secrets out of the build

Web Services in Godot 4.3+

Talk to REST APIs, leaderboards, analytics, login services, and other HTTP/WebSocket backends. All examples target Godot 4.3+ with no deprecated APIs. GDScript is shown first, then C#.

> **Related skills:** **multiplayer-basics** for real-time game networking with WebSocketMultiplayerPeer, **save-load** for persisting tokens and cached responses, **web-export** for CORS and browser limits, **mobile-development** for Android export permissions, **multithreading** for heavy response parsing off the main thread.

---

1. Picking the Tool

| Need | Use | Why | |---|---|---| | Ordinary REST call, file or image download | `HTTPRequest` node | Handles connect, redirects, gzip, timeout; emits one signal when done | | Streaming a response, custom keep-alive, many requests to one host | `HTTPClient` | Low-level; you call `poll()` and drive the state machine yourself | | Persistent two-way channel (chat, live events, lobby service) | `WebSocketPeer` | Client socket; call `poll()` every frame | | Real-time gameplay sync with RPCs | `WebSocketMultiplayerPeer` | Plugs into `MultiplayerAPI` — see **multiplayer-basics** |

**One `HTTPRequest` node runs one request at a time.** A second `request()` while one is in flight returns `ERR_BUSY`. Create one node per concurrent request (Section 3) rather than queueing on a shared node.

---

2. A Single Request with `await`

`request()` only *starts* the request; its return value says whether it could be sent, not whether the server answered. The result arrives on `request_completed(result, response_code, headers, body)`.

func fetch_motd() -> void:
    var http := HTTPRequest.new()
    http.timeout = 10.0 # Default 0.0 means wait forever.
    add_child(http)

    var err := http.request("https://api.example.com/v1/motd")
    if err != OK:
        push_error("Could not start request: %s" % error_string(err))
        http.queue_free()
        return

    var res: Array = await http.request_completed
    http.queue_free()

    var result: int = res[0]
    var code: int = res[1]
    var body: PackedByteArray = res[3]
    if result != HTTPRequest.RESULT_SUCCESS:
        push_warning("Network failure (HTTPRequest.Result %d)" % result)
    elif code != 200:
        push_warning("Server answered HTTP %d" % code)
    else:
        print(body.get_string_from_utf8())
public async void FetchMotd()
{
    var http = new HttpRequest { Timeout = 10.0 }; // Default 0.0 means wait forever.
    AddChild(http);

    Error err = http.Request("https://api.example.com/v1/motd");
    if (err != Error.Ok)
    {
        GD.PushError($"Could not start request: {err}");
        http.QueueFree();
        return;
    }

    Variant[] res = await ToSignal(http, HttpRequest.SignalName.RequestCompleted);
    http.QueueFree();

    long result = res[0].AsInt64();
    long code = res[1].AsInt64();
    byte[] body = res[3].AsByteArray();
    if (result != (long)HttpRequest.Result.Success)
        GD.PushWarning($"Network failure (HttpRequest.Result {result})");
    else if (code != 200)
        GD.PushWarning($"Server answered HTTP {code}");
    else
        GD.Print(body.GetStringFromUtf8());
}

Check **both** values: `result` is the transport outcome (DNS, TLS, timeout — `RESULT_CANT_RESOLVE`, `RESULT_TLS_HANDSHAKE_ERROR`, `RESULT_TIMEOUT`, …), `response_code` is the HTTP status. A 404 or 500 arrives with `RESULT_SUCCESS`.

---

3. A Reusable JSON API Client (Autoload)

Wrap the boilerplate once. Register this as an autoload named `Api`; every call spawns its own `HTTPRequest`, so calls can overlap.

# api.gd — autoload "Api"
extends Node

const BASE_URL := "https://api.example.com/v1"
const TIMEOUT_SEC := 10.0

var auth_token: String = ""


## Returns {ok: bool, code: int, data: Variant, error: String}.
func request_json(path: String, method: HTTPClient.Method = HTTPClient.METHOD_GET, payload: Variant = null) -> Dictionary:
    var http := HTTPRequest.new()
    http.timeout = TIMEOUT_SEC
    add_child(http)

    var headers := PackedStringArray(["Accept: application/json"])
    var body := ""
    if payload != null:
        headers.append("Content-Type: application/json")
        body = JSON.stringify(payload)
    if not auth_token.is_empty():
        headers.append("Authorization: Bearer " + auth_token)

    var err := http.request(BASE_URL + path, headers, method, body)
    if err != OK:
        http.queue_free()
        return {"ok": false, "code": 0, "data": null, "error": "request() failed: " + error_string(err)}

    var res: Array = await http.request_completed
    http.queue_free()
    if res[0] != HTTPRequest.RESULT_SUCCESS:
        return {"ok": false, "code": 0, "data": null, "error": "transport error %d" % res[0]}

    var code: int = res[1]
    var text := (res[3] as PackedByteArray).get_string_from_utf8()
    var data: Variant = JSON.parse_string(text) if not text.is_empty() else null
    return {"ok": code >= 200 and code < 300, "code": code, "data": data, "error": "" if code < 400 else text}
// Api.cs — autoload "Api"
using System.Collections.Generic;
using System.Threading.Tasks;
using Godot;

public partial class Api : Node
{
    private const string BaseUrl = "https://api.example.com/v1";
    private const double TimeoutSec = 10.0;

    public string AuthToken { get; set; } = "";

    public readonly record struct ApiResult(bool Ok, long Code, Variant Data, string Error);

    public async Task<ApiResult> RequestJson(string path, HttpClient.Method method = HttpClient.Method.Get, Variant payload = default)
    {
        var http = new HttpRequest { Timeout = TimeoutSec };
        AddChild(http);

        var headers = new List<string> { "Accept: application/js
Read more
Ships withgodot-prompter

Agentic skills framework for Godot 4.x game development. Gives AI coding agents domain-specific expertise for GDScript and C# projects.

Get the whole plugin

Other skills on godot-prompter.