Skip to content

Instantly share code, notes, and snippets.

@BeepIsla
Last active January 24, 2026 03:32
Show Gist options
  • Select an option

  • Save BeepIsla/08007d9fe98f9baeb1f35a4714f54209 to your computer and use it in GitHub Desktop.

Select an option

Save BeepIsla/08007d9fe98f9baeb1f35a4714f54209 to your computer and use it in GitHub Desktop.
Dumps every community server into an SQLite database. Use something like TablePlus to view the database. Requires a recent NodeJS version to run using `node fetch_all_servers_from_steam.ts`. Fetching only CS2 (730 AppID) servers takes under 4 minutes.
import { DatabaseSync } from "node:sqlite";
const STEAM_API_KEY = "< STEAM API KEY FROM https://steamcommunity.com/dev/apikey >";
const TARGET_APP_ID = 730; // 0 = All
// Other configuration you probably don't want to touch
const SERVER_LIST_MAX_LIMIT = 10000;
const INCLUDE_VALVE_SERVERS = false;
const INVALID_CHARS = ["?".charCodeAt(0), "*".charCodeAt(0), "\\".charCodeAt(0)]; // These are used for filtering
const SQLITE_MAX_SERVERS_PER_INSERT = 2000;
const db = new DatabaseSync("db.sqlite");
let total = 0;
db.prepare(
`
CREATE TABLE IF NOT EXISTS servers (
addr TEXT PRIMARY KEY ON CONFLICT IGNORE,
steamid TEXT,
name TEXT,
appid INTEGER,
gamedir TEXT,
version TEXT,
players INTEGER,
max_players INTEGER,
bots INTEGER,
map TEXT,
gametype TEXT
) STRICT
`
).run();
async function getListInternal(filter: string): Promise<
{
addr: string;
steamid: string;
name: string;
appid: number;
gamedir: string;
version: string;
players: number;
max_players: number;
bots: number;
map: string;
gametype: string;
}[]
> {
const uri = new URL("https://api.steampowered.com/");
uri.pathname = "IGameServersService/GetServerList/v1";
uri.searchParams.set("key", STEAM_API_KEY);
uri.searchParams.set("limit", SERVER_LIST_MAX_LIMIT.toString());
uri.searchParams.set("filter", filter);
const res = await fetch(uri);
if (res.status !== 200) {
throw new Error(`Expected 200 status code but got ${res.status}`);
}
const obj = await res.json();
return obj?.response?.servers ?? [];
}
async function getList(filter: string): ReturnType<typeof getListInternal> {
filter = `\\name_match\\${filter}\\`;
if (!INCLUDE_VALVE_SERVERS) {
// Exclude Valve servers
filter += "nor\\1\\white\\1\\";
}
if (TARGET_APP_ID > 0) {
filter += `appid\\${TARGET_APP_ID}\\`;
}
let lastErr;
for (let i = 0; i < 5; i++) {
try {
return await getListInternal(filter);
} catch (err) {
lastErr = err;
await new Promise((p) => setTimeout(p, 5 * 1000));
}
}
throw lastErr;
}
async function recursiveScan(prefix: string) {
const currentFilter = prefix === "" ? "*" : `${prefix}*`;
console.log(Buffer.from(currentFilter, "ascii").toString("hex"));
const servers = await getList(currentFilter);
if (servers.length <= 0) {
return;
}
if (servers.length < SERVER_LIST_MAX_LIMIT - 100) {
for (let i = 0; i < servers.length; i += SQLITE_MAX_SERVERS_PER_INSERT) {
const chunk = servers.slice(i, i + SQLITE_MAX_SERVERS_PER_INSERT);
if (chunk.length <= 0) {
continue;
}
db.prepare(
`
INSERT INTO servers (
addr,
steamid,
name,
appid,
gamedir,
version,
players,
max_players,
bots,
map,
gametype
) VALUES ${new Array(chunk.length).fill("(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)").join(", ")}
`
).run(
...chunk
.map((s) => [
// I don't know which one of these can actually be optional
s?.addr ?? "0.0.0.0:0",
s?.steamid ?? "0",
s?.name ?? "",
s?.appid ?? 0,
s?.gamedir ?? "",
s?.version ?? "",
s?.players ?? 0,
s?.max_players ?? 0,
s?.bots ?? 0,
s?.map ?? "",
s?.gametype ?? ""
])
.flat()
);
}
total += servers.length;
console.log(`-> ${servers.length} (Total: ${total})`);
return;
}
// The server browser allows basically anything that is a valid C string
for (let i = 1; i <= 0xff; i++) {
if (INVALID_CHARS.includes(i)) {
continue;
}
await recursiveScan(prefix + String.fromCodePoint(i));
}
}
async function main() {
const start = Date.now();
await recursiveScan("");
const end = Date.now();
const secs = Math.floor((end - start) / 1000);
console.log(`Took ${secs}s`);
}
main().catch(console.error);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment