Roblox GSB Quickstart
This guide walks you from zero to a working Roblox server script that saves player data, submits leaderboard scores, and manages virtual economy — all through Supercraft GSB, an external backend with no DataStore rate limits and built-in cross-experience support.
Everything in this guide runs from a server-side Script. HttpService is the only Roblox API you need.
What you'll have at the end: a Roblox server script that verifies players, reads and writes persistent data with optimistic locking, submits scores to a live leaderboard, and credits in-game currency — all backed by an external database your team can operate from a dashboard.
Prerequisites
- A Roblox experience with Allow HTTP Requests enabled (Game Settings → Security).
- A GSB account and a project with at least one environment. Create one here.
- A Server Token from your project's Tokens tab. This is what your Roblox server uses to authenticate with GSB. Keep it secret — never put it in a LocalScript.
Step 1 — Enable HttpService
In Roblox Studio, open Game Settings → Security and turn on Allow HTTP Requests. Without this, all external HTTP calls will silently fail.
Security note: HTTP requests made through HttpService happen on your Roblox game server, not on players' machines. The server token never leaves your server scripts.
Step 2 — Install the GSB Module
Download GSB.lua from the GSB SDK page and add it to your experience:
- In Studio, insert a ModuleScript inside ServerScriptService.
- Rename it to
GSB. - Paste the full contents of
GSB.luainto the script body.
The module is self-contained — no external packages, no toolchain. One paste and it is ready.
Step 3 — Initialize the Client
In any server-side Script (not a LocalScript), require the module and call GSB.init with your project credentials:
-- ServerScriptService/GameServer (Script)
local GSB = require(game.ServerScriptService.GSB)
local gsb = GSB.init(
"YOUR_PROJECT_ID", -- e.g. "proj_01abc..."
"YOUR_SERVER_TOKEN", -- e.g. "gsb_st_..."
"YOUR_ENVIRONMENT_ID" -- e.g. "env_01abc..." (use your "production" env)
)
You can find all three values in your project dashboard under Settings → Environments and Settings → Tokens.
Step 4 — Verify Players on Join
When a player joins, call gsb:VerifyPlayer with their Roblox UserId. This creates a GSB player record the first time, or returns the existing one on subsequent joins. The returned player_id is what you use for all subsequent API calls.
local Players = game:GetService("Players")
-- Maps Roblox UserId → GSB player_id for this session
local playerIds = {}
Players.PlayerAdded:Connect(function(player)
local ok, result = pcall(function()
return gsb:VerifyPlayer(player.UserId)
end)
if ok then
playerIds[player.UserId] = result.player_id
print("[GSB] Verified " .. player.Name .. " → " .. result.player_id)
else
warn("[GSB] Verify failed for " .. player.Name .. ": " .. tostring(result))
end
end)
Players.PlayerRemoving:Connect(function(player)
playerIds[player.UserId] = nil
end)
Step 5 — Save and Load Player Data
GSB exposes a DataStore-compatible API so the pattern looks familiar. Under the hood it stores versioned JSON documents without the 6-second cooldown or per-key rate limits of Roblox DataStores.
Basic get / set
-- Get a player's save data
local profileStore = gsb:GetDataStore("profile")
local function loadProfile(robloxUserId)
local playerId = playerIds[robloxUserId]
if not playerId then return end
local data, version = profileStore:GetAsync(playerId)
-- data is a Lua table (or nil if no save exists yet)
return data, version
end
local function saveProfile(robloxUserId, data)
local playerId = playerIds[robloxUserId]
if not playerId then return end
profileStore:SetAsync(playerId, data)
end
Safe updates with optimistic locking
For writes that must not overwrite concurrent changes — such as incrementing XP when multiple servers could write the same player — use UpdateAsync. It fetches the current version, passes the value to your transform function, and retries automatically if another server wrote first.
local function addXP(robloxUserId, amount)
local playerId = playerIds[robloxUserId]
if not playerId then return end
profileStore:UpdateAsync(playerId, function(current)
current = current or { xp = 0, level = 1 }
current.xp = current.xp + amount
-- level up every 1000 XP
current.level = math.floor(current.xp / 1000) + 1
return current
end)
end
Comparison: DataStore vs GSB
| Feature | Roblox DataStore | GSB |
|---|---|---|
| API shape | GetAsync / SetAsync / UpdateAsync | Same — drop-in compatible |
| Rate limits | 60 + playerCount × 10 req/min per key | Configurable per-project, 10–100× higher |
| Cross-experience | No — scoped to one experience | Yes — shared ProjectID across any experience |
| Dashboard / support tools | Roblox Open Cloud only | Built-in: search, view, edit, audit log |
| Versioned writes | Version history (limited retention) | ETag-based optimistic locking, always |
Step 6 — Leaderboards
First, create a leaderboard in your project dashboard (Leaderboards → New). Give it a name and set the sort order (descending for high-score, ascending for fastest time). Then use the GSB client to submit and read scores:
-- Submit a score when a round ends
local function onRoundEnd(robloxUserId, finalScore)
local playerId = playerIds[robloxUserId]
if not playerId then return end
gsb:SubmitScore("weekly-kills", playerId, finalScore, {
-- optional metadata attached to this entry
weapon = "shotgun",
mode = "tdm",
})
end
-- Show the top 10 at game start (e.g. to populate a lobby board)
local function showLeaderboard()
local entries = gsb:GetLeaderboardTop("weekly-kills", 10)
for _, entry in ipairs(entries) do
print(string.format("#%d %s %d", entry.rank, entry.player_id, entry.score))
end
end
-- Show where a specific player ranks
local function showPlayerRank(robloxUserId)
local playerId = playerIds[robloxUserId]
local standing = gsb:GetPlayerStanding("weekly-kills", playerId)
if standing then
print("You are rank " .. standing.rank .. " with " .. standing.score .. " kills")
end
end
Seasonal resets: configure a reset schedule (daily / weekly / monthly) in the dashboard. GSB archives the previous period automatically and starts a fresh board — no script changes needed.
Step 7 — Economy
GSB provides server-authoritative currency balances and item inventories. All adjustments are atomic — you can credit gold and add items in a single call without worrying about partial failures.
Read a player's economy
local function getEconomy(robloxUserId)
local playerId = playerIds[robloxUserId]
if not playerId then return end
local economy = gsb:GetPlayerEconomy(playerId)
-- economy.balances = list of { currency_id, currency_name, amount }
for _, bal in ipairs(economy.balances or {}) do
print(bal.currency_name .. ": " .. bal.amount)
end
-- economy.inventory = list of { item_id, item_name, quantity }
for _, item in ipairs(economy.inventory or {}) do
print(item.item_name .. " ×" .. item.quantity)
end
end
Award currency and items on a kill
-- Currency IDs and item IDs come from your project dashboard
local GOLD_CURRENCY_ID = "curr_gold_..."
local CRATE_ITEM_ID = "item_crate_..."
local function onKillReward(robloxUserId)
local playerId = playerIds[robloxUserId]
if not playerId then return end
gsb:AdjustEconomy(
playerId,
-- balance adjustments: {currency_id, amount}
{{ currency_id = GOLD_CURRENCY_ID, amount = 50 }},
-- inventory adjustments: {item_id, quantity}
{{ item_id = CRATE_ITEM_ID, quantity = 1 }}
)
end
Step 8 — Matchmaking (optional)
If you want to queue players across servers or experiences, GSB includes a matchmaking service. Typical usage: call JoinMatchmaking when a player clicks "Find Game", poll GetMatchStatus every few seconds, and start the match when all participants show status = "matched".
-- Queue a player
gsb:JoinMatchmaking(playerId, "ranked-2v2", "eu-west")
-- Poll until matched (run this in a separate coroutine or loop)
local function waitForMatch(playerId)
for _ = 1, 30 do -- 30 × 2s = 60s timeout
task.wait(2)
local status = gsb:GetMatchStatus(playerId)
if status and status.status == "matched" then
return status.match
end
end
gsb:LeaveMatchmaking(playerId) -- timed out — leave gracefully
return nil
end
Putting It All Together
A minimal but complete server script for a session-based game:
-- ServerScriptService/GameServer
local GSB = require(game.ServerScriptService.GSB)
local Players = game:GetService("Players")
local gsb = GSB.init("YOUR_PROJECT_ID", "YOUR_SERVER_TOKEN", "YOUR_ENVIRONMENT_ID")
local GOLD_ID = "curr_gold_..."
local playerIds = {}
Players.PlayerAdded:Connect(function(player)
local ok, result = pcall(gsb.VerifyPlayer, gsb, player.UserId)
if not ok then return end
local pid = result.player_id
playerIds[player.UserId] = pid
-- Load profile
local ds = gsb:GetDataStore("profile")
local profile = ds:GetAsync(pid) or { xp = 0, level = 1, kills = 0 }
-- Store in player attribute for easy access
player:SetAttribute("gsb_pid", pid)
player:SetAttribute("xp", profile.xp)
player:SetAttribute("level", profile.level)
player:SetAttribute("kills", profile.kills)
end)
Players.PlayerRemoving:Connect(function(player)
local pid = player:GetAttribute("gsb_pid")
if not pid then return end
-- Save profile on leave
local ds = gsb:GetDataStore("profile")
ds:SetAsync(pid, {
xp = player:GetAttribute("xp"),
level = player:GetAttribute("level"),
kills = player:GetAttribute("kills"),
})
playerIds[player.UserId] = nil
end)
-- Called by your combat system
local function onPlayerKill(killerUserId)
local pid = playerIds[killerUserId]
if not pid then return end
-- Award gold and increment kill counter
gsb:AdjustEconomy(pid, {{ currency_id = GOLD_ID, amount = 25 }}, {})
local killer = Players:GetPlayerByUserId(killerUserId)
if killer then
local kills = (killer:GetAttribute("kills") or 0) + 1
killer:SetAttribute("kills", kills)
gsb:SubmitScore("all-time-kills", pid, kills)
end
end
Cross-Experience Data Sharing
The same ProjectID and EnvironmentID work across any number of Roblox experiences you own. A player verified in experience A has the same player_id and can access the same documents, balances, and leaderboards from experience B — no DataStore bridging needed.
-- Experience A (e.g. your main game)
local gsbA = GSB.init("proj_shared", "st_main_game", "env_prod")
gsbA:VerifyPlayer(player.UserId) -- creates player record
-- Experience B (e.g. your hub / lobby game)
local gsbB = GSB.init("proj_shared", "st_hub", "env_prod")
-- Same proj + env → same player record → same documents and leaderboards
local profile = gsbB:GetDataStore("profile"):GetAsync(pid)
Troubleshooting
| Error | Likely Cause | Fix |
|---|---|---|
HttpService is not enabled |
HTTP requests disabled in game settings | Game Settings → Security → Allow HTTP Requests |
API Error (401) |
Wrong or expired server token | Check token in dashboard; make sure you're using the correct environment |
API Error (404) |
Wrong project ID, environment ID, or resource doesn't exist yet | Double-check IDs; create the leaderboard or currency in the dashboard first |
API Error (429) |
Rate limit hit (unlikely in normal usage) | The SDK retries automatically with backoff. Check your plan limits. |
No data returned from GetAsync |
Player not verified yet, or document doesn't exist | Call VerifyPlayer first; treat nil return as empty initial state |
Next Steps
- Dashboard player browser — search any player by GSB ID, view and edit their documents, see economy state, inspect audit logs. No script needed.
- Seasonal leaderboards — configure a reset schedule in the dashboard. GSB archives the previous period automatically.
- Social features —
gsb:SendFriendRequest,gsb:ListFriendsfor cross-experience friend lists that survive server restarts. - Config delivery — push a JSON bundle from the dashboard and read it in-experience with
gsb:GetActiveConfig(), so you can tune balance values without republishing.
Related in This Hub
- Roblox HttpService for External Backends
- Roblox DataStore vs External Database
- Roblox Cross-Experience Progression
- Seasonal Leaderboards and Game Economy
- Player Authentication for Dedicated-Server Games
- Game Server Backend hub
To create a project and grab your credentials, visit Supercraft Game Server Backend.