API reference
A read-only HTTPS API for Steam ban data. No key, no signup. Responses are JSON.
Your first request
Pick your shell and paste this in. You should get JSON back.
curl "https://rustbanned.net/api/lookup?steamId=76561198012345678" \
-H "User-Agent: YourAppName/1.0 (contact@example.com)"curl ships with macOS, most Linux distributions, and Windows 10 build 1803 and later, so there is nothing to install. The three commands differ only in how each shell continues a line: \ in bash and zsh, a backtick in PowerShell, and no continuation in cmd. In Windows PowerShell 5.1 curl is an alias for Invoke-WebRequest, which does not accept -H, so call curl.exe by name.
steamId takes a SteamID64, a vanity name like Karaxo, or a profile URL. User-Agent should name your app and give a contact address; /api/lookup returns 403 if it is missing or shorter than 5 characters. Call this from a server, bot, or plugin. Browser JavaScript cannot set its own User-Agent.
The essentials
- Base URL
https://rustbanned.net- Authentication
- None.
- Required header
User-Agent, at least 5 characters. Enforced on/api/lookup; send it everywhere anyway.- Response format
- JSON.
/api/friendsstreams Server-Sent Events.
All endpoints
Making requests
Requests are checked against a schema at the edge. A request that fails validation never reaches the API.
Input in the query string
For single lookups. Use your language's URL helper so vanity names and profile URLs get encoded.
GET /api/lookup?steamId=Karaxo
User-Agent: YourAppName/1.0 (contact@example.com)Input in a JSON body
Required for batch and friends, and accepted for single lookups. Send Content-Type: application/json.
POST /api/lookup
Content-Type: application/json
User-Agent: YourAppName/1.0 (contact@example.com)
{
"input": "Karaxo"
}Parameter names are case-sensitive
Names must match the spelling on this page exactly, capitals included. Get the casing wrong and validation reports the parameter as missing, not as invalid. A request carrying a valid SteamID64 still comes back 400.
JSON field names work the same way. Most reports of this come from hand-typed cURL.
/api/lookup?steamId=76561198012345678Accepted/api/lookup?steamid=76561198012345678400{ "steamId": "76561198012345678" }Accepted{ "steamid": "76561198012345678" }400Request requirements
- Use
https://rustbanned.net. HSTS is on, so an HTTP fallback will not work. - Set a
User-Agentof at least 5 characters, naming your app and a contact address. - Send only the fields listed for the endpoint.
- Set a timeout. The examples here use 10 seconds.
If a request is refused
Check the method, path, parameter casing, JSON body, and Content-Type. The response may come from the API or from Cloudflare, so check the response Content-Type before parsing it as JSON. Cloudflare pages carry a Ray ID. Quote it if you email.
Errors
Standard HTTP meanings. Every endpoint can return 429 or a 5xx. A 5xx may come from the API or from Cloudflare in front of it; the second kind never reaches the API and arrives as an HTML error page rather than JSON.
| Code | Status | Meaning |
|---|---|---|
200 | OK | The request succeeded. |
400 | Bad request | A parameter or field is missing, misspelled, or fails validation. |
403 | Forbidden | Refused by security rules, the User-Agent requirement, or a privacy setting. |
404 | Not found | The account or the requested data does not exist. |
429 | Too many requests | Rate limited. Back off before retrying. |
500 | Internal server error | The endpoint failed unexpectedly. |
502 | Bad gateway | An upstream fetch failed, or the edge could not reach the origin. Any endpoint can return this, not just the image proxy. |
503 | Service unavailable | Steam or another required service is down. |
Retrying after 429
Back off exponentially. Use Retry-After when the response carries it. Tight retry loops keep the limit active and can get the IP blocked. The same backoff is worth applying to 502 and 503, which are usually transient; the snippet below only retries 429, so widen the condition if you want that.
async function fetchWithBackoff(
url: string,
options: RequestInit,
retries = 4
): Promise<Response> {
for (let attempt = 0; attempt <= retries; attempt++) {
const response = await fetch(url, options);
if (response.status !== 429) return response;
const retryAfter = Number(response.headers.get("Retry-After"));
const waitMs = Number.isFinite(retryAfter) && retryAfter > 0
? retryAfter * 1000
: 2 ** attempt * 500; // 500ms, 1s, 2s, 4s...
if (attempt === retries) return response; // give up, let the caller handle it
await new Promise((resolve) => setTimeout(resolve, waitMs));
}
throw new Error("unreachable");
}/api/lookupLook up an account
Returns the ban record for one Steam account.
Takes a SteamID64, a vanity name, or a profile URL. Vanity names and URLs cost an extra call to resolve first, so pass a SteamID64 when you already have one. This is the only endpoint that checks the User-Agent header, and every response carries a Server-Timing header with the Steam lookup duration.
Parameters
steamIdstringin queryrequired- GET requests only. Maximum 256 characters.
inputstringin bodyrequired- POST only. Same accepted formats.
Responses
200Lookup completed.400Missing or malformed parameter or body.403The User-Agent header is absent or shorter than 5 characters.404No Steam account matched.500Unexpected failure.503Steam is unavailable.
Returns one account object.
curl "https://rustbanned.net/api/lookup?steamId=76561198012345678" \
-H "User-Agent: YourAppName/1.0 (contact@example.com)"{
"steamId": "76561198012345678",
"profile": {
"personaname": "PlayerName",
"profileurl": "https://steamcommunity.com/profiles/76561198012345678",
"avatarfull": "https://avatars.steamstatic.com/...",
"timecreated": 1234567890
},
"bans": {
"CommunityBanned": false,
"VACBanned": true,
"NumberOfVACBans": 1,
"DaysSinceLastBan": 365,
"NumberOfGameBans": 0,
"EconomyBan": "none"
},
"rustHours": 1234.5,
"friendsPrivate": false
}The account object
Fields may be added over time. Do not assume the shape is fixed.
| Field | Type | Description |
|---|---|---|
steamId | string | Resolved SteamID64. |
profile.personaname | string | Current Steam display name. |
profile.profileurl | string | Canonical Steam profile URL. |
profile.avatarfull | string | Full-size avatar URL on the Steam CDN. |
profile.timecreated | number | Account creation time, in Unix seconds. |
bans.VACBanned | boolean | Whether the account carries one or more VAC bans. |
bans.NumberOfVACBans | number | Total VAC ban count across all games. |
bans.NumberOfGameBans | number | Total developer-issued game ban count. |
bans.DaysSinceLastBan | number | Days since the most recent reported ban. |
bans.CommunityBanned | boolean | Whether Steam reports a Community ban. |
bans.EconomyBan | string | Trade and market standing, such as "none". |
rustHours | number | null | Hours played in Rust. Null when private or unavailable. |
friendsPrivate | boolean | Whether the friend list was hidden from the lookup. |
/api/lookup/batchBatch lookup
Looks up as many as 50 accounts in one request.
Entries resolve independently and accept the same formats as a single lookup. One batch is faster than 50 sequential calls and far less likely to hit the rate limit. Entries that fail to resolve are left out of results and counted in failed, so check resolved and failed rather than assuming the array lines up with what you sent.
Parameters
steamIdsstring[]in bodyrequired- 256 characters each. Anything past the 50th entry is dropped rather than rejected.
Responses
200Batch completed. Individual entries may still have failed to resolve.400steamIds is missing, not an array, or empty.404No entry resolved to a Steam account.500Unexpected failure.503Steam is unavailable.
Returns results for the entries that resolved, plus resolved and failed counts.
curl -X POST "https://rustbanned.net/api/lookup/batch" \
-H "User-Agent: YourAppName/1.0 (contact@example.com)" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"steamIds": [
"76561198012345678",
"Karaxo",
"https://steamcommunity.com/id/example"
]
}'{
"results": [
{ "steamId": "76561198012345678", "profile": { ... }, "bans": { ... } },
{ "steamId": "76561198087654321", "profile": { ... }, "bans": { ... } }
],
"resolved": 2,
"failed": 1
}/api/friendsScan a friend list
Checks every account on a player's friend list.
Friend lists run to hundreds of accounts, so results arrive as Server-Sent Events. At most 100 friends are checked; totalFriends still reports the real size of the list. Progress events arrive during the scan and the results arrive as one final event, so read each data: line rather than waiting for the connection to close. Friends whose profile or ban record is unavailable are left out. A private or unavailable friend list is reported as an error event, not as an empty result.
Parameters
steamIdstringin bodyrequired- A resolved SteamID64, 17 digits. Vanity names and profile URLs are rejected.
Responses
200Always. Failures are delivered as an error event inside the stream, so check each event rather than the status code.
Returns progress events, then one event carrying the whole friends array.
curl -N -X POST "https://rustbanned.net/api/friends" \
-H "User-Agent: YourAppName/1.0 (contact@example.com)" \
-H "Content-Type: application/json" \
-d '{ "steamId": "76561198012345678" }'
# This endpoint streams Server-Sent Events (SSE).
# Read each "data:" event as it arrives instead of waiting for one JSON object.data: {"progress":{"current":0,"total":100,"totalFriends":214}}
data: {"progress":{"current":90,"total":100,"totalFriends":214}}
data: {"friends":[
{
"steamId": "76561198012345678",
"personaname": "PlayerName",
"avatar": "https://avatars.steamstatic.com/abc123.jpg",
"profileurl": "https://steamcommunity.com/id/example",
"banned": true,
"rustHours": 1234.5,
"banDetails": {
"VACBanned": true,
"NumberOfVACBans": 1,
"NumberOfGameBans": 0,
"CommunityBanned": false,
"EconomyBan": "none",
"DaysSinceLastBan": 365
}
}
],"totalFriends":214}/api/rust-statsRust statistics
Returns in-game Rust statistics for one account.
Steam only exposes these when the player's game details are public. Expect 403 often, and don't retry it. Statistics come back in seven groups: combat, survival, building, resources, npc, items, and misc. Responses are cached for five minutes.
Parameters
steamIdstringin queryrequired- A resolved SteamID64, 17 digits.
Responses
200Statistics returned.400Missing or malformed SteamID64.403Statistics are private.404No statistics for this account.4xx / 5xxSteam's own status code, passed through when the upstream fetch fails.500Statistics fetch failed, or the server has no Steam API key configured.
Returns the account's Rust statistics, grouped by category.
curl "https://rustbanned.net/api/rust-stats?steamId=76561198012345678" \
-H "User-Agent: YourAppName/1.0 (contact@example.com)"{
"steamId": "76561198012345678",
"stats": {
"combat": { "kills": 4821, "deaths": 3190, "kd": 1.51, "headshots": 1204,
"headshotRate": 22.4, "bulletsFired": 90210, "bulletsHit": 5372,
"accuracy": 5.9, "wounded": 88, "woundedAssisted": 31,
"beenPickedUp": 12 },
"survival": { "deaths": 3190, "suicides": 74, "fallingDeaths": 33, ... },
"building": { "structuresBuilt": 9042, "upgradesBuilt": 2210, "repairsDone": 118 },
"resources": { "woodHarvested": 1204553, "stoneHarvested": 880210, ... },
"npc": { "scientistsKilled": 512, "boarsKilled": 233, ... },
"items": { "itemsDropped": 902, "itemsPickedUp": 15522, "itemsCrafted": 88 },
"misc": { "barrelsBroken": 4410, "heliHits": 219, "arrowsFired": 1200, ... }
}
}/api/inventoryRust inventory
Returns the Rust inventory for one account.
A private inventory is not an error: it comes back as 200 with private set to true and an empty items array. Results are capped at the first 50 items, and each icon is a relative /api/image URL you can use directly in an img tag. Responses are cached for five minutes.
Parameters
steamIdstringin queryrequired- A resolved SteamID64, 17 digits.
Responses
200Inventory returned, or the inventory is private.400steamId is missing.4xx / 5xxSteam's own status code, passed through when the upstream fetch fails.500Inventory fetch failed.
Returns a private flag, up to 50 items, and the total item count.
curl "https://rustbanned.net/api/inventory?steamId=76561198012345678" \
-H "User-Agent: YourAppName/1.0 (contact@example.com)"{
"private": false,
"items": [
{
"name": "Metal Facemask",
"icon": "/api/image?url=https%3A%2F%2Fcommunity.cloudflare...",
"tradable": true,
"marketable": true,
"type": ""
}
],
"total": 128
}/api/imageImage proxy
Serves a Steam CDN image through rustbanned.net.
For embedding Steam avatars where third-party image hosts are blocked. Only the four steamstatic.com hostnames listed below are accepted.
Parameters
urlstringin queryrequired- An https URL on community.cloudflare.steamstatic.com, community.akamai.steamstatic.com, cdn.akamai.steamstatic.com, or avatars.steamstatic.com. 2048 characters max.
Responses
200Image returned.400Missing or malformed url.403Hostname is not an approved Steam CDN.502Upstream fetch failed.
Returns the image bytes, with the upstream content type.
curl "https://rustbanned.net/api/image?url=https%3A%2F%2Favatars.steamstatic.com%2Fabc123_full.jpg" \
-H "User-Agent: YourAppName/1.0 (contact@example.com)" \
--output avatar.jpgDiscord bot
A /check slash command that returns a player's ban record as an embed.
Defer the interaction first. Discord kills an unacknowledged slash command after about three seconds, and a slow Steam response takes longer than that. The examples below all defer.
// discord.js v14 command handler
// npm install discord.js
import { EmbedBuilder } from "discord.js";
export async function checkCommand(interaction) {
if (!interaction.isChatInputCommand() || interaction.commandName !== "check") return;
const steam = interaction.options.getString("steam", true);
// Discord needs an acknowledgement quickly. Defer before the API call.
await interaction.deferReply();
try {
const url = new URL("https://rustbanned.net/api/lookup");
url.searchParams.set("steamId", steam);
const response = await fetch(url, {
headers: {
"User-Agent": "MyRustDiscordBot/1.0 (you@example.com)",
"Accept": "application/json"
},
signal: AbortSignal.timeout(10_000)
});
const contentType = response.headers.get("content-type") ?? "";
if (!contentType.includes("application/json")) {
throw new Error(`Non-JSON response (HTTP ${response.status})`);
}
const data = await response.json();
if (!response.ok) {
throw new Error(data.error ?? `HTTP ${response.status}`);
}
const embed = new EmbedBuilder()
.setTitle(data.profile?.personaname ?? data.steamId)
.setURL(data.profile?.profileurl ?? "https://rustbanned.net")
.addFields(
{ name: "SteamID64", value: data.steamId, inline: false },
{ name: "VAC bans", value: String(data.bans.NumberOfVACBans), inline: true },
{ name: "Game bans", value: String(data.bans.NumberOfGameBans), inline: true },
{ name: "Rust hours", value: data.rustHours == null ? "Private" : String(data.rustHours), inline: true },
{ name: "Last ban", value: `${data.bans.DaysSinceLastBan} days ago`, inline: true }
);
if (data.profile?.avatarfull) {
embed.setThumbnail(data.profile.avatarfull);
}
await interaction.editReply({ embeds: [embed] });
} catch (error) {
console.error(error);
await interaction.editReply(
"I couldn't check that account right now. Verify the Steam ID and try again."
);
}
}
// Register a slash command named /check with one required STRING option:
// name: "steam"
// description: "SteamID64, vanity name, or Steam profile URL"The discord.js and Discord.Net examples are command handlers for an existing bot. The discord.py one is closer to a full minimal bot. Keep your token in an environment variable.
Rust server plugin
Call the API from a uMod / Oxide plugin to get an in-game admin command. uMod's webrequest timeout is in milliseconds, not seconds.
/check 76561198012345678 → PlayerName | VAC: 1 | Game bans: 0 | Rust hours: 1234.5using Newtonsoft.Json.Linq;
using Oxide.Core.Libraries;
using System;
using System.Collections.Generic;
namespace Oxide.Plugins
{
[Info("RustBannedLookup", "YourName", "1.0.0")]
[Description("Adds /check <steam> using the RustBanned public API")]
public class RustBannedLookup : RustPlugin
{
[ChatCommand("check")]
private void CheckPlayer(BasePlayer player, string command, string[] args)
{
if (args.Length == 0)
{
SendReply(player, "Usage: /check <SteamID64, vanity name, or profile URL>");
return;
}
var input = Uri.EscapeDataString(string.Join(" ", args));
var url = $"https://rustbanned.net/api/lookup?steamId={input}";
var headers = new Dictionary<string, string>
{
["User-Agent"] = "MyRustServerPlugin/1.0 (you@example.com)",
["Accept"] = "application/json"
};
// uMod timeout is in milliseconds.
webrequest.Enqueue(url, null, (code, response) =>
{
if (code != 200 || string.IsNullOrWhiteSpace(response))
{
SendReply(player, $"RustBanned lookup failed (HTTP {code}).");
return;
}
try
{
var data = JObject.Parse(response);
var name = data["profile"]?["personaname"]?.ToString() ?? "Unknown";
var vac = data["bans"]?["NumberOfVACBans"]?.Value<int>() ?? 0;
var game = data["bans"]?["NumberOfGameBans"]?.Value<int>() ?? 0;
var hours = data["rustHours"]?.Type == JTokenType.Null
? "Private"
: data["rustHours"]?.ToString() ?? "Unknown";
SendReply(
player,
$"{name} | VAC: {vac} | Game bans: {game} | Rust hours: {hours}"
);
}
catch
{
SendReply(player, "RustBanned returned an unexpected response.");
}
}, this, RequestMethod.GET, headers, 10000f);
}
}
}AI assistant prompt
Paste this into a coding assistant and swap in your language and framework. Test the result against a real Steam account before you deploy it.
Attaching your existing bot or plugin file and asking for the integration to be added to it works better than asking for a new file.
Build me a complete RustBanned integration in [LANGUAGE / FRAMEWORK].
Goal:
- Add a command called /check that accepts a SteamID64, Steam vanity name, or Steam profile URL.
- Call GET https://rustbanned.net/api/lookup?steamId=INPUT
- Set this header on the request:
User-Agent: MyApp/1.0 (my-email@example.com)
- Set Accept: application/json.
- URL-encode the user's input.
- Use a 10-second timeout.
- Check Content-Type before parsing JSON because a security-layer response can be HTML.
- Handle HTTP 400, 403, 404, 429, 500/502, and 503 cleanly.
- If HTTP 429 includes Retry-After, respect it rather than retrying immediately.
- Never spam retries.
On success show:
- player name
- SteamID64
- profile URL / avatar if supported
- VAC ban count
- game ban count
- days since last ban
- Rust hours, or "Private" when null
If this is a Discord bot:
- use a slash command
- defer/acknowledge the interaction before calling the API
- edit the deferred response with an embed/result
- use non-blocking HTTP for async frameworks
Return:
1. install commands
2. complete working code
3. environment variables / secrets I need
4. how to run it
5. a short explanation written for a beginner.Before you ship
Worth going through before this sits in front of users.
- Replace the example User-Agent with your app name and contact address.
- URL-encode user input.
- Set a timeout on every outbound request.
- Check Content-Type before parsing a response as JSON.
- Handle 400, 403, 404, 429, and 5xx without crashing.
- Back off on 429 and cap the retry count.
- Defer Discord interactions before calling the API.
- Use non-blocking HTTP in async Python.
- Keep tokens in environment variables.
- Log failures without logging secrets.
- Cache repeated lookups where stale data is fine.
- Ban data supports a moderation call. It should not be the only thing behind a permanent ban.
Support
Email contact@rustbanned.net about blocked requests or unexpected errors. Include your public IP, the User-Agent you sent, the endpoint and method, the HTTP status, roughly when it happened with the timezone, and the Cloudflare Ray ID if you got a Cloudflare page.