reference

API

Everything on this page is public and needs no account. Base URL for every endpoint ishttps://api.spawnlist.gg unless stated otherwise. Responses are JSON except where noted.

Basics

No versioned auth scheme, no OAuth, no rate-limit headers. The read endpoints are open and cached at the edge; the vote endpoints are authenticated by a per-server key you generate yourself. CORS is open for reads, so you can call them from a browser.

Errors come back as {"error":"..."} with a matching HTTP status. The one deliberate exception is the vote API, which answers in plain text — see below for why.

Vote rewards

This is the endpoint your reward plugin talks to. It deliberately copies the shape the older server lists settled on, so plugins like EasyVote and most uMod reward plugins work by changing the URL in their config and nothing else. That's why it returns a bare character instead of JSON, and why the key travels in the query string: it's their contract, not ours.

Get your key from your server's edit page under Vote rewards API. Treat it like a password — anyone holding it can mark votes as claimed. Rotating it invalidates the old one immediately.

Check a vote

A pure read. Ask as often as you like; it never consumes anything.

GET https://api.spawnlist.gg/api/?object=votes&element=claim&key=YOUR_KEY&steamid=76561198000000000

Claim the reward

Returns 1 exactly once and 2 from then on. This is the call that pays out, so trigger your reward on it.

GET https://api.spawnlist.gg/api/?object=plugin&element=reward&key=YOUR_KEY&steamid=76561198000000000

If you'd rather keep checking and claiming separate, POST to the first URL with&action=post and it claims instead of reading.

Responses

BodyMeaning
0No vote found in the last 24 hours
1Voted, reward not claimed yet
2Voted, reward already claimed

Parameters

NameRequiredNotes
keyyesYour server key
steamidone ofSteamID64, 17 digits. For Rust, Palworld, Valheim
usernameone ofPlayer name, up to 32 chars. For Minecraft

discordid is accepted by the sites we mirror but not by us — we don't collect a Discord identity on votes, so it would always answer 0.

Behaviour worth knowing before you go live

For Minecraft: Java you probably want Votifierinstead, which pushes to your server the moment the vote lands. Full walkthrough in thevote rewards guide.

Discord vote webhook

Outbound, not inbound: you give us a webhook URL on your server's edit page and wePOST to it each time someone votes. Opt-in, and disabled unless you set it.

Only Discord webhook URLs are accepted. The URL must matchhttps://discord.com/api/webhooks/<id>/<token> (or the legacydiscordapp.com host). This is an anti-SSRF measure — an arbitrary URL would let anyone use us to fire requests at hosts they don't control — so you can't currently point this at your own backend. If you need that, say so and we'll look at signed generic webhooks.

What we send:

POST https://discord.com/api/webhooks/<id>/<token>
Content-Type: application/json

{
  "embeds": [
    {
      "title": "🗳️ New vote for Your Server",
      "description": "**Notch** just voted — **42** votes this month.",
      "url": "https://spawnlist.gg/servers/your-server",
      "color": 2257915,
      "footer": { "text": "spawnlist.gg — where players spawn next" }
    }
  ]
}

Reading data

Open, CORS-enabled, cached at the edge. The cache TTL is in each response'sCache-Control, and it's the honest number — don't poll faster, you'll get the same bytes.

GET /v1/servers

Live listings. Cached 60s.

curl "https://api.spawnlist.gg/v1/servers?game=minecraft_java&tags=survival,pvp&sort=players"
ParamValues
gameminecraft_java · minecraft_bedrock · hytale · palworld · rust · valheim
qFull-text search over name and description
sorttop (default) · new · rated · players
tagsComma-separated, AND semantics. Max 5. tag is accepted as an alias
countryISO 3166-1 alpha-2, e.g. US
regionContinent slug, e.g. europe, n-america
versionSubstring match against the reported version
featured1 for paid placements only. Pair with placement=home|game

Paging

Returns a plain array. X-Total-Count carries the full number of matches for your filters, which is the only way to tell a full page from the last one — it's exposed through CORS, so a browser can read it.

ParamDefaultNotes
limit36Max 100 per request
offset0No upper bound — iterate to walk the whole directory
# every Minecraft: Java server, 100 at a time
offset=0
while :; do
  page=$(curl -s "https://api.spawnlist.gg/v1/servers?game=minecraft_java&limit=100&offset=$offset")
  [ "$(echo "$page" | jq length)" -eq 0 ] && break
  echo "$page" | jq -c '.[]'
  offset=$((offset + 100))
done

One exception: sort=players orders by the live player count, which lives in KV and not in the database, so it can't page in SQL. It pages inside a pool of the top 250 by score and reports that pool as the total. Every other sort pages over the full set.

GET /v1/servers/:slug

One listing, same object shape as the array above plus announcement, screenshots and unclaimed. Cached 60s. 404 if the slug isn't live.

{
  "id": "c9256956-3e27-465f-9b0b-0fefea92aa39",
  "slug": "hypixel",
  "name": "Hypixel",
  "game": "minecraft_java",
  "tags": ["minigames", "skyblock"],
  "ip": "mc.hypixel.net",
  "version": "1.8.x-1.21.x",
  "country": "US",
  "regions": ["US", "DE"],
  "players": 37303,
  "maxPlayers": 200000,
  "votes": 0,
  "rank": 1,
  "rating": null,
  "ratingCount": 0,
  "addedDays": 5,
  "uptime": 100,
  "featured": false,
  "premium": false,
  "bannerUrl": null,
  "iconUrl": "https://api.spawnlist.gg/media/..."
}

players is the live count from our pinger, not a stored snapshot.uptime is a percentage measured over 25 days, or null before we have enough data.

GET /v1/servers/:slug/metrics

Time series. days holds 30 days of uptime and player averages;hours holds a 24-slot average by UTC hour; hourlyis the raw per-(UTC day, UTC hour) matrix. Cached 300s.

If you render an hour-of-day chart, aggregate hourly in your viewer's timezone rather than shifting the hours aggregate — the offset changes with daylight saving, so shifting a finished average smears the peak across two hours.hasHourly is false until at least 7 distinct days exist, which is our way of not presenting one day of noise as a weekly pattern.

Other read endpoints

EndpointReturnsCache
GET /v1/gamesCatalogue with live counts and players per game60s
GET /v1/tags/topMost-used tags with counts60s
GET /v1/tags/suggest?q=Tag autocomplete60s
GET /v1/trendingServers above their weekly average right now120s
GET /v1/servers/:slug/reviews{aggregate, reviews[], totalReviews} — 30 per page, limit (max 100) and offset
GET /v1/pricingFeatured slot and Premium prices in cents3600s
GET /health{"ok":true}

Badges

Live images you can embed anywhere. They show players online, votes and uptime, and update themselves. Cached 300s, CORS open, X-Robots-Tag: noindex.

https://api.spawnlist.gg/badge/<slug>.png            PNG, 300×72, rendered at 2x
https://api.spawnlist.gg/badge/<slug>.png?style=light
https://api.spawnlist.gg/badge/<slug>.png?style=blue
https://spawnlist.gg/badge/<slug>.svg   SVG, crisper, but blocked by some forums

Use the PNG for forums and Discord — many boards strip SVG from [img] because an SVG can carry script, and Discord embeds don't render it. An unknownstyle falls back to dark.

Server status check

The endpoint behind the status checker. It asks our pinger to reach a server from the outside and report what it finds.

POST /v1/tools/check
Content-Type: application/json

{ "game": "minecraft_java", "address": "play.example.com", "port": 25565, "token": "<turnstile>" }

It needs a Cloudflare Turnstile token solved with action tools, and allows 15 checks per IP per day. Both exist so this can't be used to scan networks, which is also why private and link-local addresses are rejected outright. In practice that makes it a page to use, not an API to integrate — if you want programmatic checks for servers you own, tell us and we'll wire it to your server key instead.

Response: { available, verdict, players?, version?, description? } whereverdict is verified, unreachable,wrong_game or unsupported. available:false meansour checker didn't answer and says nothing about the server.