curl --request GET \
--url https://api.simpl.gg/v1/servers/{instance_id}/logs/stream \
--header 'api-key: <api-key>'import requests
url = "https://api.simpl.gg/v1/servers/{instance_id}/logs/stream"
headers = {"api-key": "<api-key>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {'api-key': '<api-key>'}};
fetch('https://api.simpl.gg/v1/servers/{instance_id}/logs/stream', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.simpl.gg/v1/servers/{instance_id}/logs/stream",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"api-key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.simpl.gg/v1/servers/{instance_id}/logs/stream"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("api-key", "<api-key>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.simpl.gg/v1/servers/{instance_id}/logs/stream")
.header("api-key", "<api-key>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.simpl.gg/v1/servers/{instance_id}/logs/stream")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["api-key"] = '<api-key>'
response = http.request(request)
puts response.read_body{
"id": "1042",
"timestamp": "2026-09-21T14:00:00.123456Z",
"level": "info",
"message": "Server started on port 7777",
"continuation": false
}Stream server logs (SSE)
A live tail over Server-Sent Events. Sends the last limit lines
(default 100) matching the filters, then each new line as it arrives,
usually a few seconds after it was printed.
Events
| event | payload | when |
|---|---|---|
line | a LogLine | each line the game prints; the SSE id: is the line’s id |
log_cap | a LogCap | a cap stopped storage (sent at the start if it already had, and when it happens) |
end | { "reason": "stopped" } | the server has stopped and its last lines have been sent; the stream closes |
error | { "message" } | the stream failed; reconnect to resume |
A comment line (: ping) is sent every 15 seconds. Reconnect with
Last-Event-ID to resume after the last line received, with no
backlog and no gap.
At most 10 streams per project may be open at once. Another is refused with 429.
curl --request GET \
--url https://api.simpl.gg/v1/servers/{instance_id}/logs/stream \
--header 'api-key: <api-key>'import requests
url = "https://api.simpl.gg/v1/servers/{instance_id}/logs/stream"
headers = {"api-key": "<api-key>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {'api-key': '<api-key>'}};
fetch('https://api.simpl.gg/v1/servers/{instance_id}/logs/stream', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.simpl.gg/v1/servers/{instance_id}/logs/stream",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"api-key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.simpl.gg/v1/servers/{instance_id}/logs/stream"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("api-key", "<api-key>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.simpl.gg/v1/servers/{instance_id}/logs/stream")
.header("api-key", "<api-key>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.simpl.gg/v1/servers/{instance_id}/logs/stream")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["api-key"] = '<api-key>'
response = http.request(request)
puts response.read_body{
"id": "1042",
"timestamp": "2026-09-21T14:00:00.123456Z",
"level": "info",
"message": "Server started on port 7777",
"continuation": false
}Authorizations
Per-project server key, prefixed sk_live_. Full access to every
endpoint. Keep it on your backend: never ship it in a game binary.
Headers
Resume after this line id following a reconnect.
Path Parameters
Server instance identifier.
"abc123"
Query Parameters
How many recent lines to send first. Ignored when resuming with Last-Event-ID.
1 <= x <= 1000Only return log lines at exactly this level.
info, warn, error Case-insensitive substring filter applied to the log message. % and _
are matched literally.
200"port 7777"
Response
An open event stream. It closes after end, or when the client disconnects.
- Option 1
- Option 2
- Option 3
- Option 4
The data: payload of a single event. Which schema applies is
determined by the event: name on the same message.
Opaque, unique per line. Also the SSE event id on the live stream.
"1042"
When the line was printed, to the microsecond.
"2026-09-21T14:00:00.123456Z"
A best guess from the words in the line (ERROR, WARNING,
SCRIPT ERROR, panic: and similar). Output has no level of its own.
info, warn, error The line, without its line ending, colour codes or control characters.
Lines longer than 2048 bytes are cut and end in ….
"Server started on port 7777"
This line continues the one before it: an indented line printed
within a second of a game error or warning, such as Godot's
at: push_error (...). It takes that line's level. Show it with the
line it continues.