The Creator Update - Live tags, Streamer Mode, partner tags and $1,000 for streamers

REST API

Player names, UUIDs and textures from laby.net, read straight from your visitors' browsers.
Getting started

Two ways in#

Pages read public player data straight from their visitors' browsers. Everything that runs on a server uses a token of your organization, and its scopes decide what it may do.

Quickstart#

Once browser access is on for your domain, a plain fetch() on your page is all it takes. Responses are JSON, every route is a public GET.
// On https://your-domain.com, a page on one of your verified domains
const response = await fetch("https://laby.net/api/v3/user/LabyStudio/uuid");

if (response.status === 429) {
  // This visitor asked too often; Retry-After says how many seconds to wait
  const retryAfter = Number(response.headers.get("Retry-After"));
}

const { uuid, name } = await response.json();
https://laby.net/api/v3/user/LabyStudio/uuid

Press Send to call the live API for the test player.

Basics

Browser access#

The way to show Laby data on your site. laby.net answers your pages with an Access-Control-Allow-Origin for your domain, so the browser lets your script read the response.

Browser access has to be requested

It is switched on per organization by the Laby team. Verify your domain in the console first, then tell us which site it is and what you want to show on it.

Set it up

  1. 1
    Create an organization in the console, or use the one you already have.
  2. 2
    Verify your domain under Domains with a DNS record. Subdomains are included, only https pages count.
  3. 3
    Request browser access from the Laby team, with the site and what it shows. We switch it on for your verified domains.
  4. 4
    Call the endpoints from your pages. The console shows the requests per domain, so you see what your visitors use.

The rules

  • Plain GET requests without custom headers or credentials. Anything else needs a preflight, and laby.net does not answer one.
  • Every visitor spends their own budget, exactly as on laby.net.
  • A 429 means this visitor asked too often. Retry-After says how many seconds to wait.
  • Load the data when a page is viewed instead of storing it, and link to laby.net where you show it.
GET /api/v3/user/34e57efa-5783-46c7-a9fc-890296aaba1f/names HTTP/2
Host: laby.net
Origin: https://stats.your-domain.com

HTTP/2 200
Access-Control-Allow-Origin: https://stats.your-domain.com
Content-Type: application/json

Why the browser and not your server

A request from the visitor's browser counts against the visitor's connection, the same way laby.net itself loads a profile. A server that asks on behalf of everyone puts thousands of visitors behind one address, and that address runs into the per-network budget within minutes. With browser access the limits grow with your audience; backend access is the exception.

Per visitor and route

RouteLimit
…/{name}/uuid10 per 30 s per visitor
…/{uuid}/names10 per 30 s per visitor
…/{uuid}/textures10 per 30 s per visitor

Responses carry X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset; past the limit the answer is 429 with Retry-After in seconds.

Per network and day

On top, laby.net keeps one budget for everything a network reads anonymously, and one per account for signed-in players. A busy page with browser access never gets near it: each visitor reads from their own network.

A server or script calling without a token shares its network's budget with everything else behind that address. That is the limit backend access, a token scope for partners, lifts.

Basics

Errors#

Failures answer with an HTTP status and a JSON message.
400Malformed input, for example something that is neither a name nor a UUID.
401Unknown, revoked or expired token.
403The token lacks the scope, or the organization lacks the feature.
404laby.net does not know this player.
409Conflict, for example a cashback reference reported twice.
429Rate limit reached - wait for Retry-After.
Endpoints

Player data#

Open to browser access, and to tokens with backend access.

Name to UUID#

GET/api/v3/user/{name}/uuidBrowser access

Resolves a Minecraft name to the player's UUID and current name. A UUID works as input too, which makes this the quickest way to normalize whatever a visitor typed.

name
Current Minecraft name or UUID, with or without dashes.
Rate limit
10 per 30 s per visitor
Cache
60 s

Names change hands, UUIDs never do. Keep the UUID and call the routes below with it.

Response
{
  "uuid": "34e57efa-5783-46c7-a9fc-890296aaba1f",
  "name": "LabyStudio",
  "lastUpdated": "2026-11-07T08:35:02+00:00"
}
https://laby.net/api/v3/user/LabyStudio/uuid

Press Send to call the live API for the test player.

Name history#

GET/api/v3/user/{uuid}/namesBrowser access

Every name the player has had, oldest first. The first entry carries the account's creation date where it is known.

uuid
Minecraft UUID (a name works too, but resolves first).
Rate limit
10 per 30 s per visitor
Cache
Not cached

accurate: false means laby.net saw the new name first on that day; the change itself may be older.

last_seen_at is when laby.net last saw the name in use. Names a player hid on laby.net stay hidden here too, and changed_at is null where the date is not known or the player hides it.

Response
[
  { "name": "FirstName", "changed_at": "2013-04-02", "accurate": true },
  {
    "name": "LabyStudio",
    "changed_at": "2016-01-11",
    "accurate": false,
    "last_seen_at": "2026-11-07T08:35:02+00:00"
  }
]
https://laby.net/api/v3/user/34e57efa-5783-46c7-a9fc-890296aaba1f/names

Press Send to call the live API for the test player.

Skins & capes#

GET/api/v3/user/{uuid}/texturesBrowser access

The skins and capes laby.net has seen on the player, grouped by type: SKIN, CAPE, and the LabyMod textures CLOAK and BANDANA. The one they wear right now carries active: true.

uuid
Minecraft UUID, with or without dashes.
Rate limit
10 per 30 s per visitor
Cache
60 s

image_hash is what the Render API takes: /api/v3/render/skin/{image_hash}.webp draws exactly this skin, forever.

Response
{
  "SKIN": [
    {
      "type": "SKIN",
      "image_hash": "670f9d318cefb8f7d61e21a5afb767e9",
      "file_hash": "…",
      "first_seen_at": "2024-02-18T09:12:03+00:00",
      "last_seen_at": "2026-11-07T08:35:02+00:00",
      "use_count": 1843,
      "slim_skin": false,
      "active": true
    }
  ],
  "CAPE": [ … ]
}
https://laby.net/api/v3/user/34e57efa-5783-46c7-a9fc-890296aaba1f/textures

Press Send to call the live API for the test player.

Organization tokens

Tokens#

Everything that calls laby.net from a server uses a token of your organization. It belongs to the organization, not to a person, and can only do what its scopes allow.

Creating one

  1. 1
    Open your organization in the console and go to API Tokens.
  2. 2
    Name it after the integration and pick only the scopes it needs. A scope shows up once your organization has access to it.
  3. 3
    Copy it right away: a token is shown once and cannot be retrieved again. Revoking it stops every integration that uses it immediately.

All endpoints live under https://laby.net/api/v3. Send the token with every request as a Bearer token. Tokens act for the organization, not for a person: they keep working when a team member leaves, and they can only do what their scopes allow.

Authentication
Authorization: Bearer laby_org_...

# Legacy header from the retired next.labymod.net cashback API, still accepted:
X-AUTH-TOKEN: <token>

Responses are JSON. Errors carry an HTTP status (400 invalid input, 401 unknown or revoked token, 403 missing scope or no serverpartner, 404 not found, 409 conflict, 429 rate limited) and a message.

Backend access#

organization.api.readPartners & trusted organizationsRead public player data from your servers, outside the anonymous limits.

Backend access has to be requested

It is for partners, server networks and services we work with closely, where browser access cannot do the job: a bot or a game server that needs player data without a browser in between. Tell us about your project, or ask your partner contact.

The same player endpoints a page with browser access reads, called from your backend. A token with this scope is a known caller: it leaves the per-network budgets and gets the rate limit agreed for your organization. The scope appears in the console once the Laby team opened the API for your organization.

GET/api/v3/user/{name}/uuid
GET/api/v3/user/{uuid}/names
GET/api/v3/user/{uuid}/textures
Read a player's textures
GET /api/v3/user/{uuid}/textures
Authorization: Bearer laby_org_...

All tokens of an organization share one rate limit window for this scope, so a second token does not double it. The scope only reads, and it stops working the moment API access is taken back, without touching a single token.

Shop cashback#

organization.partner.cashbackServer partnersReport purchases in your server shop so players receive LabyCoins.

Report a purchase once it is paid. The player receives 3 percent of the price as LabyCoins (1000 coins per currency unit) and a notification; the coins are claimed on the rewards page. reference is your own order id and doubles as idempotency key, reporting the same reference twice returns 409.

POST/api/v3/cashback
GET/api/v3/cashback/{id}
GET/api/v3/cashback?reference={your-order-id}
GET/api/v3/cashback?user={uuid}
Report a purchase
POST /api/v3/cashback
Authorization: Bearer laby_org_...
Content-Type: application/json

{
  "uuid": "<player-uuid>",
  "price": 4.99,
  "reference": "order-2026-000123",
  "item_name": "VIP Rank"
}

→ 200 { "reference": 39482, "amount": 150 }

price is the amount the player paid, as a number. item_name is optional and shown to the player. amount in the response is the coin value credited; it is 0 for very small prices, then no notification is sent.

Look a cashback up
GET /api/v3/cashback/39482
GET /api/v3/cashback?reference=order-2026-000123

→ 200 {
  "reference": 39482,
  "added_at": "2026-09-06T10:15:00Z",
  "article_name": "VIP Rank",
  "amount": 150,
  "redeemed": false,
  "redeemed_at": null,
  "partner_reference": "order-2026-000123"
}

GET /cashback?user={uuid} lists every cashback you reported for one player, newest first, and returns 404 when there is none.

The paths of the retired next.labymod.net API (POST /cashback/create, GET /cashback/get/{id}, GET /cashback/get/{reference}?server_reference=true, GET /cashback/get/user/{uuid}) still work on laby.net with the same payloads, but are deprecated: switch to the routes above when you rotate your token.

Server achievements#

organization.achievements.grantOrganizations with a verified serverHand achievements to players on the servers your organization has claimed.

Achievements are defined and reviewed on laby.net; the API only hands them out. Every call addresses an achievement by the key you gave it and a server by its name, the one in the URL of its page on laby.net, so a plugin needs no internal ids. Granting is idempotent: replaying a queue never fails, it reports the repeat as a no-op.

GET/api/v3/achievements/servers
GET/api/v3/achievements/servers/{server}
POST/api/v3/achievements/servers/{server}/grant
POST/api/v3/achievements/servers/{server}/grant/bulk
DELETE/api/v3/achievements/servers/{server}/grant
GET/api/v3/achievements/servers/{server}/{key}/grants
Grant to one player
POST /api/v3/achievements/servers/labymod/grant
Authorization: Bearer laby_org_...
Content-Type: application/json

{
  "achievement": "first_win",
  "uuid": "<player-uuid>"
}

→ 200 { "granted": true, "reactivated": false }

granted is false when the player already holds it, so you can call this on every login without checking first. reactivated is true when a previously revoked grant came back. The player is notified only on a real grant, and only while the achievement is approved.

Grant to many players
POST /api/v3/achievements/servers/labymod/grant/bulk
Authorization: Bearer laby_org_...
Content-Type: application/json

{
  "achievement": "first_win",
  "uuids": ["<uuid-a>", "<uuid-b>"]
}

→ 200 {
  "results": [
    { "uuid": "<uuid-a>", "result": "granted" },
    { "uuid": "<uuid-b>", "result": "unknown_user" }
  ],
  "granted": 1,
  "total": 2
}

At most 100 players per request. A player without a Laby.net account does not fail the batch, they come back as unknown_user; a repeat is already_granted. Send the rest of your roster in further requests.

Take one back
DELETE /api/v3/achievements/servers/labymod/grant
Authorization: Bearer laby_org_...
Content-Type: application/json

{
  "achievement": "first_win",
  "uuid": "<player-uuid>"
}

→ 200 { "success": true }
Read your configuration and your grants
GET /api/v3/achievements/servers
→ 200 { "servers": [ { "id": 12, "name": "labymod", "nice_name": "LabyMod" } ] }

GET /api/v3/achievements/servers/labymod
→ 200 {
  "server": { "id": 12, "name": "labymod", "nice_name": "LabyMod" },
  "achievements": [
    { "key": "first_win", "name": "First win", "status": "ACTIVE",
      "grantable": true, "use_count": 1428 }
  ]
}

GET /api/v3/achievements/servers/labymod/first_win/grants?page=1&pageSize=100
→ 200 {
  "grants": [ { "uuid": "...", "username": "...",
                "granted_at": "2026-09-06T10:15:00Z", "source": "API" } ],
  "total": 1428, "page": 1, "page_size": 100
}

status is PENDING while a new achievement waits for review, ACTIVE once it is approved, and REJECTED, DISABLED or BLOCKED otherwise. Only grantable ones accept new grants, anything else answers 409, so check the list after changing your configuration. Grants survive a rejection, they are just hidden until the achievement is live again.

Every route allows 120 requests per minute and token. A server your organization has not claimed answers 403, an unknown key 404.

More

Sign in with Laby#

Need to act for a player rather than read public data? Register an OAuth application, and players grant it exactly the scopes it asks for.

What an application can do

Read and write on behalf of a signed-in player: their textures, notifications, friends, presence and chat, and a realtime connection to LabyConnect at wss://connect.laby.net, each behind a scope of its own.

Where it lives

Applications belong to an organization and are managed in the console. Each scope your application has been granted comes with its own reference there, endpoints and packets included.

Open the console
More

Fair use#

Rules for showing player data.

Live, not copied

Load player data when a page is viewed. Do not mirror it into a database of your own or crawl it in bulk.

Credit the source

Link to the player's laby.net profile wherever you show what came from it.

Respect privacy

What a player hides on laby.net stays hidden for you too. Do not try to work around it.

Missing an endpoint?

Tell us what you are building and which data you need.

Developer Discord