Back to Tools
Developer reference

Mojang API URL Builder

Type a username or UUID and get every Mojang player endpoint filled in — with the request in cURL, JavaScript or Python, the response it returns, and the rate limit it enforces.

Player

A Java username or a UUID — the lookup fills in whichever half you did not type

Try

Public player endpoints

No token needed — 4 endpoints, filled in with what you typed

GET

Username → UUID

The starting point for almost everything: turns a name into the dashless UUID the rest of the API keys off.

https://api.mojang.com/users/profiles/minecraft/{name} Open
Request
curl -s -X GET \
  "https://api.mojang.com/users/profiles/minecraft/{name}"
+ Response, notes and alternate hosts

Example response

{
  "name": "jeb_",
  "id": "853c80ef3c3749fdaa49938b674adae6"
}

The name is matched case-insensitively and the response carries the real casing. A `legacy` flag appears for accounts never migrated to a Mojang account, and `demo` for accounts that do not own the game.

Same endpoint, other hosts

  • https://api.minecraftservices.com/minecraft/profile/lookup/name/{name} Same response, and not affected by the 403 misconfiguration.
  • https://api.mojang.com/minecraft/profile/lookup/name/{name}

Errors

  • 404 No account has that name.
  • 403 api.mojang.com returns these at random — a known misconfiguration (WEB-7591). Retry, or use the api.minecraftservices.com form.
GET

UUID → username

The reverse lookup — the current name on a UUID, with its real casing.

https://api.minecraftservices.com/minecraft/profile/lookup/{uuid} Open
Request
curl -s -X GET \
  "https://api.minecraftservices.com/minecraft/profile/lookup/{uuid}"
+ Response, notes and alternate hosts

Example response

{
  "name": "jeb_",
  "id": "853c80ef3c3749fdaa49938b674adae6"
}

Takes the UUID dashed or dashless. There is no name-history endpoint any more: Mojang removed /user/profiles/<uuid>/names in September 2022, so only the current name is available.

GET

UUID → skin and cape

About 400 requests per 10 seconds

The session server's profile: a base64 `textures` property holding the skin and cape URLs.

https://sessionserver.mojang.com/session/minecraft/profile/{uuid} Open
Request
curl -s -X GET \
  "https://sessionserver.mojang.com/session/minecraft/profile/{uuid}"
+ Response, notes and alternate hosts

Example response

{
  "id": "853c80ef3c3749fdaa49938b674adae6",
  "name": "jeb_",
  "properties": [
    {
      "name": "textures",
      "value": "ewogICJ0aW1lc3R..."
    }
  ]
}

Decode `properties[0].value` from base64 to get `textures.SKIN.url` and `textures.CAPE.url` on textures.minecraft.net. `SKIN.metadata.model` is only present — as `slim` — on Alex-model skins; a missing SKIN object means the account is on a default skin.

?unsigned=false — Adds Yggdrasil's base64 signature next to the texture value, which is what a server needs to verify the skin.

Errors

  • 204 The UUID is well formed but no player owns it — empty body.
  • 400 Not a valid UUID.
POST

Bulk username → UUID

Up to ten names in one POST, for resolving a whitelist or a leaderboard in one call.

https://api.mojang.com/profiles/minecraft
Request
curl -s -X POST \
  -H "Content-Type: application/json" \
  -d '["jeb_","Notch"]' \
  "https://api.mojang.com/profiles/minecraft"
+ Response, notes and alternate hosts

Payload

["jeb_","Notch"]

Example response

[
  {
    "id": "853c80ef3c3749fdaa49938b674adae6",
    "name": "jeb_"
  },
  {
    "id": "069a79f444e94726a5befca90e38aaf5",
    "name": "Notch"
  }
]

Names that do not exist are simply absent from the array rather than returned as null, so match the response back by name, not by index.

Same endpoint, other hosts

  • https://api.minecraftservices.com/minecraft/profile/lookup/bulk/byname
  • https://api.mojang.com/minecraft/profile/lookup/bulk/byname

Errors

  • 400 CONSTRAINT_VIOLATION — the array is empty, holds more than 10 names, or contains an empty string.
  • 415 The Content-Type header was not application/json.

Bulk username lookup

One POST resolves up to 10 names — the fastest way to fill a whitelist

3 names
curl -s -X POST \
  -H "Content-Type: application/json" \
  -d '["jeb_","Notch","Dinnerbone"]' \
  "https://api.mojang.com/profiles/minecraft"

Names that do not exist are left out of the array entirely rather than returned as null, so match the response back by name — the order is not guaranteed either.

Authenticated endpoints

13 routes that act on the account owning the token — never on another player

Every route below lives on api.minecraftservices.com and wants Authorization: Bearer <access token>, which comes out of the Microsoft → Xbox Live → XSTS → Minecraft handshake. A missing or stale token is a 401; a token for a different account simply cannot reach this data at all.

GET

Own profile

/minecraft/profile

The signed-in account's UUID, name, and every skin and cape it owns.

+ Request and response
Request
curl -s -X GET \
  -H "Authorization: Bearer $MC_ACCESS_TOKEN" \
  "https://api.minecraftservices.com/minecraft/profile"

Example response

{
  "id": "853c80ef3c3749fdaa49938b674adae6",
  "name": "jeb_",
  "skins": [
    {
      "id": "6a6e65e5-76dd-4c3c-a625-162924514568",
      "state": "ACTIVE",
      "url": "http://textures.minecraft.net/texture/...",
      "variant": "CLASSIC"
    }
  ],
  "capes": [
    {
      "id": "e7dfea16-dc83-4a01-9be4-51d8ffe4a9d9",
      "state": "ACTIVE",
      "url": "http://textures.minecraft.net/texture/...",
      "alias": "Migrator"
    }
  ]
}

`state` is ACTIVE for what is worn right now and INACTIVE for anything else in the wardrobe. `variant` is CLASSIC for Steve proportions and SLIM for Alex.

GET

Player attributes

/player/attributes

Chat, multiplayer, Realms and telemetry privileges, plus the account's ban status.

+ Request and response
Request
curl -s -X GET \
  -H "Authorization: Bearer $MC_ACCESS_TOKEN" \
  "https://api.minecraftservices.com/player/attributes"

Example response

{
  "privileges": {
    "onlineChat": { "enabled": true },
    "multiplayerServer": { "enabled": true },
    "multiplayerRealms": { "enabled": true },
    "telemetry": { "enabled": true }
  },
  "profanityFilterPreferences": { "profanityFilterOn": false },
  "banStatus": { "bannedScopes": {} }
}

`banStatus.bannedScopes` is empty for an account in good standing; a MULTIPLAYER entry carries banId, reason and an expiry (absent when permanent).

POST

Modify player attributes

/player/attributes

Flips the Realms profanity filter and the friends-list preferences.

+ Request and response
Request
curl -s -X POST \
  -H "Authorization: Bearer $MC_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "profanityFilterPreferences": { "profanityFilterOn": true }, "friendsPreferences": { "friends": "ENABLED", "acceptInvites": "ENABLED" } }' \
  "https://api.minecraftservices.com/player/attributes"

Payload

{
  "profanityFilterPreferences": { "profanityFilterOn": true },
  "friendsPreferences": { "friends": "ENABLED", "acceptInvites": "ENABLED" }
}

Only those two blocks are writable — privileges and ban status are read-only. The response is the same shape as the query.

GET

Name change information

/minecraft/profile/namechange

When the account was created, when it last renamed, and whether it may rename now.

+ Request and response
Request
curl -s -X GET \
  -H "Authorization: Bearer $MC_ACCESS_TOKEN" \
  "https://api.minecraftservices.com/minecraft/profile/namechange"

Example response

{
  "changedAt": "2019-08-21T14:32:03Z",
  "createdAt": "2011-05-13T10:00:00Z",
  "nameChangeAllowed": true
}

`createdAt` is the only official source for an account's age — it is not exposed on any public endpoint.

GET

Check name availability

/minecraft/profile/name/{name}/available 20 requests per 5 minutes, per account

Whether a name can be taken, before spending the 30-day rename cooldown on it.

+ Request and response
Request
curl -s -X GET \
  -H "Authorization: Bearer $MC_ACCESS_TOKEN" \
  "https://api.minecraftservices.com/minecraft/profile/name/{name}/available"

Example response

{ "status": "AVAILABLE" }

AVAILABLE means free, DUPLICATE means taken, NOT_ALLOWED means the string fails the name rules — 1-16 characters of letters, digits and underscore.

PUT

Change name

/minecraft/profile/name/{name}

Renames the account the token belongs to.

+ Request and response
Request
curl -s -X PUT \
  -H "Authorization: Bearer $MC_ACCESS_TOKEN" \
  "https://api.minecraftservices.com/minecraft/profile/name/{name}"

Example response

Returns the full profile — same shape as Own profile.
  • 400 CONSTRAINT_VIOLATION — the name breaks the character rules.
  • 403 Could not change name; `details.status: DUPLICATE` if taken.
  • 429 Too many rename attempts.
POST

Change skin by URL

/minecraft/profile/skins

Points the account at a skin file already hosted somewhere public.

+ Request and response
Request
curl -s -X POST \
  -H "Authorization: Bearer $MC_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "variant": "classic", "url": "https://crafty.gg/skin.png" }' \
  "https://api.minecraftservices.com/minecraft/profile/skins"

Payload

{
  "variant": "classic",
  "url": "https://crafty.gg/skin.png"
}

`variant` is lowercase here — classic or slim — unlike the CLASSIC/SLIM in a profile.

POST

Upload skin

/minecraft/profile/skins

Same route, but with the PNG itself as multipart form data.

+ Request and response
Request
curl -s -X POST \
  -H "Authorization: Bearer $MC_ACCESS_TOKEN" \
  -F variant=classic \
  -F file="@skin.png;type=image/png" \
  "https://api.minecraftservices.com/minecraft/profile/skins"

Payload (multipart/form-data)

variant=classic, file=@skin.png;type=image/png

A burst of 429s here is what tends to trip Mojang's automatic account suspension, so back off rather than retrying hard.

DELETE

Reset skin

/minecraft/profile/skins/active

Drops back to the default Steve or Alex skin.

+ Request and response
Request
curl -s -X DELETE \
  -H "Authorization: Bearer $MC_ACCESS_TOKEN" \
  "https://api.minecraftservices.com/minecraft/profile/skins/active"

Example response

Returns the full profile — same shape as Own profile.
DELETE

Hide cape

/minecraft/profile/capes/active

Takes off whichever cape is currently worn.

+ Request and response
Request
curl -s -X DELETE \
  -H "Authorization: Bearer $MC_ACCESS_TOKEN" \
  "https://api.minecraftservices.com/minecraft/profile/capes/active"

Example response

Returns the full profile — same shape as Own profile.
PUT

Show cape

/minecraft/profile/capes/active

Wears one of the capes the account owns, by its cape UUID.

+ Request and response
Request
curl -s -X PUT \
  -H "Authorization: Bearer $MC_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "capeId": "e7dfea16-dc83-4a01-9be4-51d8ffe4a9d9" }' \
  "https://api.minecraftservices.com/minecraft/profile/capes/active"

Payload

{ "capeId": "e7dfea16-dc83-4a01-9be4-51d8ffe4a9d9" }

Cape IDs come from the `capes` list on the profile endpoint.

  • 400 profile does not own cape.
GET

Blocked users

/privacy/blocklist

The UUIDs this account has blocked — their chat and Realms invites are dropped.

+ Request and response
Request
curl -s -X GET \
  -H "Authorization: Bearer $MC_ACCESS_TOKEN" \
  "https://api.minecraftservices.com/privacy/blocklist"

Example response

{
  "blockedProfiles": [
    "069a79f444e94726a5befca90e38aaf5"
  ]
}
POST

Signature keypair

/player/certificates

The RSA keypair the client uses to sign chat messages on 1.19+ servers.

+ Request and response
Request
curl -s -X POST \
  -H "Authorization: Bearer $MC_ACCESS_TOKEN" \
  "https://api.minecraftservices.com/player/certificates"

Example response

{
  "keyPair": {
    "privateKey": "-----BEGIN RSA PRIVATE KEY-----...",
    "publicKey": "-----BEGIN RSA PUBLIC KEY-----..."
  },
  "publicKeySignatureV2": "...",
  "expiresAt": "2026-08-28T12:00:00Z",
  "refreshedAfter": "2026-08-23T12:00:00Z"
}

`publicKeySignature` is the deprecated v1 field; sign against `publicKeySignatureV2`. Refresh once past `refreshedAfter` rather than waiting for the expiry.

Rate limits, errors and CORS

What breaks these calls in production, and how each failure looks

Rate limits

  • 200 requests per 2 minutes, per IP across most endpoints. IPv6 is bucketed by /56 subnet, so a whole prefix shares one bucket.
  • The session server's profile endpoint is the exception at about 400 requests per 10 seconds.
  • Name availability is capped at 20 requests per 5 minutes, per account rather than per IP.
  • UUIDs are permanent and skins change rarely — cache both rather than looking them up per request.

Error shape

Any non-2XX response that carries a body carries this one:

{
  "error": "IllegalArgumentException",
  "errorMessage": "Invalid profile name",
  "cause": "..."
}
  • 204 — valid request, nothing to return
  • 401 — missing or invalid access token
  • 415 — Content-Type was not application/json
  • 429 — rate limited; back off, do not retry hard

These URLs are for your server, not the browser.

None of Mojang's hosts send an Access-Control-Allow-Origin header, so a fetch() from page JavaScript fails on CORS before you see the body. Call them from a backend, a serverless function or your own proxy — that is what the snippets above assume. Repeatedly hitting 429s on the skin upload route can also put an account into a temporary suspension, so treat backoff as mandatory rather than polite.

Tools that use this data

What the same UUID gets you elsewhere on Crafty

Frequently asked questions

UUIDs, CORS, rate limits and the endpoints Mojang retired

How do I get a Minecraft player's UUID from their username? +

Call GET https://api.mojang.com/users/profiles/minecraft/<name>. The response is {"name":"jeb_","id":"853c80ef3c3749fdaa49938b674adae6"} — the id is the UUID without dashes, and name comes back with its real casing because the lookup itself is case-insensitive. A 404 means no account holds that name. That host occasionally returns spurious 403s from a long-standing misconfiguration, so production code should either retry or use https://api.minecraftservices.com/minecraft/profile/lookup/name/<name>, which returns the same body.

Can I call the Mojang API from a browser? +

No. api.mojang.com, api.minecraftservices.com and sessionserver.mojang.com send no Access-Control-Allow-Origin header, so a fetch from page JavaScript is blocked by CORS before you ever see the response. The call has to be made server-side — from your backend, a serverless function or a proxy you control. That is exactly what the Resolve box on this page does: it asks Crafty's own player API, which talks to Mojang from the server. The URLs it builds are meant for your server too, not the browser console.

What are the Mojang API rate limits? +

Most endpoints allow 200 requests per 2 minutes per IP, bucketed by /56 subnet on IPv6. The session server's profile endpoint is far looser at roughly 400 requests per 10 seconds, and the authenticated name-availability check is much tighter at 20 requests per 5 minutes per account. Cache aggressively: UUIDs never change, and skins change rarely. Hammering the skin-upload endpoint into repeated 429s can put the account into a temporary suspension.

How do I get a player's skin URL from their UUID? +

GET https://sessionserver.mojang.com/session/minecraft/profile/<uuid> and base64-decode properties[0].value. The decoded JSON holds textures.SKIN.url and, when the account owns one, textures.CAPE.url, both on textures.minecraft.net. A missing SKIN object means the account still uses the default skin, and textures.SKIN.metadata.model set to slim marks an Alex-model skin. Adding ?unsigned=false also returns Mojang's signature over that value, which is what a server needs to verify the texture is genuine.

Can I look up a player's name history? +

Not any more. https://api.mojang.com/user/profiles/<uuid>/names was removed in September 2022 for privacy reasons, and nothing replaced it. Only the current name is available, either from the UUID lookup or the session server profile. The authenticated /minecraft/profile/namechange endpoint tells the signed-in account when it last renamed and when the profile was created, but it says nothing about anyone else's account.

Which endpoints need an access token, and how do I get one? +

Anything under api.minecraftservices.com that reads or changes an account — the profile, attributes, name change, skin and cape routes — needs Authorization: Bearer <Minecraft access token>. That token comes from the Microsoft OAuth handshake: a Microsoft token is exchanged for an Xbox Live token, that for an XSTS token, and that for a Minecraft access token via authentication/login_with_xbox. The token only ever acts on the account it belongs to, so there is no way to read another player's private data.

Does the Mojang API work for Bedrock players? +

No. Every endpoint here is Java Edition only. Bedrock identities live in Xbox Live as gamertags and XUIDs, and a Bedrock player has no Mojang UUID and no session-server profile. Bedrock skins are held by the client and sent to the server on join rather than fetched from a texture host, so there is no equivalent URL to look up.

17 player endpoints, verified against the Minecraft Wiki