# Minecraft Player Lookup - Username, UUID, Skin API (`dami_studio/minecraft-player-lookup`) Actor

Look up Minecraft Java players straight from Mojang's public profile API. Usernames to UUIDs ten at a time, UUIDs back to usernames, plus skin and cape URLs, texture ids and the slim or classic model flag. No API key and no account. Unknown names and throttled lookups come back free as diagnostics.

- **URL**: https://apify.com/dami\_studio/minecraft-player-lookup.md
- **Developed by:** [Dami's Studio](https://apify.com/dami_studio) (community)
- **Categories:** Games, Developer tools
- **Stats:** 2 total users, 1 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.40 / 1,000 player returneds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Minecraft Player Lookup API

Give it a list of Minecraft Java usernames and get back UUIDs, skins and capes. Give it a list of
UUIDs and get back the usernames. The data comes from Mojang's own public profile API, so it is the
same answer a Minecraft server gets when it checks who you are.

**Read this part first: name history no longer exists.** Mojang withdrew the public name-history
endpoint in September 2022 and never replaced it. Checked again on 19 September 2026. Both of the
old URLs answer `404`. Any tool still showing you a player's past names is showing you names it
collected itself while the endpoint was alive, or names it has watched change since. That is not
Mojang data and this Actor will not pretend otherwise. There is no `nameHistory` field here.

### What you get per player

One dataset row each:

| Field | What it is |
|---|---|
| `username` | The canonical spelling. Type `philza` and Mojang hands back the account's real capitalisation |
| `uuid` | 32 hex characters, no dashes. The form most APIs and plugin configs want |
| `uuidDashed` | The same id in `8-4-4-4-12` form, for anything that expects a real UUID type |
| `hasCustomSkin` | Whether this account has actually uploaded a skin |
| `skinUrl` | The texture on `textures.minecraft.net` |
| `skinTextureId` | The hash at the end of that URL. Two accounts wearing the same skin share this id, which is how you group them |
| `skinModel` | `slim` or `classic`: the three-pixel arm or the four-pixel arm |
| `hasCape` | Whether a cape is equipped |
| `capeUrl`, `capeTextureId` | Same shape as the skin fields. Capes are rare, so most rows have `null` here |
| `texturesStatus` | `ok`, `none_published`, or `not_requested` if you switched the skin lookup off |
| `texturesTimestamp` | When Mojang signed this texture blob |
| `textureSignature` | Optional. Mojang's RSA signature over the texture data, if you asked for it |
| `queriedBy`, `queriedValue` | Which of your inputs produced this row, so you can join the output back to your list |

A real row, copied out of a live run:

```json
{
  "ok": true,
  "username": "Dream",
  "uuid": "ec70bcaf702f4bb8b48d276fa52a780c",
  "uuidDashed": "ec70bcaf-702f-4bb8-b48d-276fa52a780c",
  "hasCustomSkin": true,
  "skinUrl": "http://textures.minecraft.net/texture/ca93f6fc40488f1877cda94a830b54e9f6f54ab58a5453bad5c947726dd1f473",
  "skinTextureId": "ca93f6fc40488f1877cda94a830b54e9f6f54ab58a5453bad5c947726dd1f473",
  "skinModel": "slim",
  "hasCape": false,
  "capeUrl": null,
  "capeTextureId": null,
  "texturesStatus": "ok",
  "texturesTimestamp": "2026-09-20T01:14:27.202Z",
  "textureSignature": null,
  "queriedBy": "username",
  "queriedValue": "Dream",
  "scrapedAt": "2026-09-20T01:14:27.306Z"
}
```

That row is copied straight out of a real run, not typed by hand. Mojang serves the texture URL over
plain `http`. That is their URL, passed through unchanged.

### Input

```json
{
  "usernames": ["jeb_", "Dinnerbone", "Dream", "Technoblade"],
  "uuids": ["069a79f4-44e9-4726-a5be-fca90e38aaf5"],
  "includeTextures": true,
  "maxItems": 100
}
```

You can use either list, or both in the same run. Usernames are matched without regard to case.
Duplicates are collapsed before anything is requested, so pasting a messy list costs you nothing
extra. You can paste several names on one line separated by commas or spaces. A Minecraft username
can never contain either, so there is no ambiguity to get wrong.

That collapsing works across both lists, not just within one. If you put a username in one box and
that same player's UUID in the other, you get one row, charged once, not two rows for the same
person.

`includeTextures` is worth a thought. With it on, every player needs a second request to Mojang's
session server. With it off, the run does username-to-UUID only and finishes in a fraction of the
time. If you are building a UUID map for a whitelist or a plugin config, turn it off.

`includeSignature` adds the signature Mojang attaches to the texture blob. It is there so you can
verify offline that the skin data really came from Mojang. If you are not going to check it, leave
it off. It makes every row noticeably larger for nothing.

### Usernames are resolved ten at a time

Mojang takes up to ten names in a single request and this Actor uses that. A list of 500 usernames
is 50 requests, not 500. Measured on 19 September 2026: 200 consecutive batches, 2,000 names,
completed with no refusals at a 120 ms gap between calls.

UUIDs do not get that. Mojang has no bulk endpoint for them; the batch URL that would do it answers
`404`. So each UUID in your list is its own request, and a thousand UUIDs takes roughly ten times
as long as a thousand usernames. If you have a choice about which direction to run, run names.

### What this does not do

- **No name history.** Covered above. The endpoint is gone.
- **Java accounts only.** Bedrock gamertags are a different identity system that Mojang's profile
  API does not answer for, and an XUID is not a UUID. Feed it a gamertag and you get an uncharged
  "not found", not a wrong answer.
- **Offline-mode UUIDs never resolve.** A server running in offline mode generates its own UUIDs
  from the username. They look right and Mojang has never heard of them. You get an uncharged
  `UUID_NOT_FOUND`.
- **No server data.** This Actor looks up players, not servers. Worth saying why rather than just
  saying no: the server ping protocol does carry a `sample` list of online players, which sounds
  like it belongs here. In practice it does not. Probing well-known public servers on 19 September
  2026, every reachable one returned a sample that was either empty or filled with advertising
  lines carrying the all-zero UUID. A server with 24,089 players online returned zero names.
  Folding that in would have added a field that is almost always blank.
- **No skin images.** You get the URL and the texture id. Downloading and rendering the PNG is a
  different job and it would mean charging you for bandwidth you can fetch yourself for free.
- **No ban or account status.** Mojang does not publish it.
- **Nothing that needs an account.** No login, no API key, no cookies, nothing to configure.

### When a lookup fails

Rows that are not real data are written as diagnostics with `_diagnostic: true` and `charged: false`,
and they say what went wrong:

| `errorCode` | What happened |
|---|---|
| `INVALID_USERNAME` | Not 3-16 characters of letters, numbers and underscore. Rejected before it costs a request |
| `INVALID_UUID` | Not 32 hex characters |
| `USERNAME_NOT_FOUND` | No account holds that name right now. Released names get re-registered, so a name that existed last year may not today |
| `UUID_NOT_FOUND` | Mojang has no account with that id |
| `RATE_LIMITED` | Mojang throttled the request. Re-run for the names that missed |
| `TEXTURES_RATE_LIMITED` | The name and UUID are correct and are in the row, but the skin lookup was throttled |

That last one matters for what you pay. If you asked for skins and Mojang would not serve them, you
still get the identity, as an uncharged diagnostic row, not a charged one. You do not pay for half
a row.

If a run produces a lot of problem rows, the first 50 are written out one by one and the rest are
rolled into a single summary row per error code, carrying the count and a sample of the values. A
list of 300 names where 272 did not exist writes 79 rows rather than 300, so your real results are
not buried under a wall of "not found".

### Rate limits, honestly

Mojang limits how fast it will answer. This Actor paces itself under that limit
and, if Mojang starts refusing anyway, switches connection for the rest of the
run and says so in the log. That is usually enough. It is not magic: a very large list run against
a busy Mojang can still return some `RATE_LIMITED` rows, and re-running just those is the fix.

The pacing is deliberately not the fastest possible. Running flat out is how you get throttled two
minutes in and lose the rest of the batch.

### Billing

- One charge per player row that carries the data you asked for.
- The sample row an empty run returns is **free**.
- Every diagnostic row is **free**: invalid input, unknown names, throttled lookups, all of it.
- A run that resolves nothing charges nothing beyond starting.

The current per-row price is on the Pricing tab of this Actor's page.

### FAQ

**How do I get a Minecraft UUID from a username?**
Put the username in the `usernames` list and run it. The `uuid` field is the undashed form and
`uuidDashed` is the standard form. Both are in every row.

**Can I get a username from a UUID?**
Yes. Put it in the `uuids` list, with or without dashes. Bear in mind Mojang has no bulk endpoint
for UUIDs, so a large UUID list is slower than the same number of usernames.

**Can I get a player's old usernames?**
No, and neither can anyone else from Mojang. The endpoint was removed in September 2022. Sites that
show name history are showing their own records of names they watched change, not Mojang data.

**How do I tell if a skin is Alex or Steve shaped?**
`skinModel` is `slim` for the three-pixel arm and `classic` for the four-pixel arm. When
`hasCustomSkin` is `false` the account has never uploaded one and the game picks a default.

**Why do two players have the same `skinTextureId`?**
Because they are wearing the same skin. The id is the content hash, so grouping on it finds everyone
using a given skin.

**Does this work for Bedrock, Pocket Edition or Xbox?**
No. Those use gamertags and XUIDs, which Mojang's profile API does not answer for. Java only.

**Do I need a Mojang API key or a Minecraft account?**
No. Nothing to sign up for, nothing to configure.

**Do I need a proxy?**
No. Mojang serves this Actor fine without one. There is a proxy field for people who need requests
to leave from a specific network, and adding a proxy measured about six times slower, so leave it
alone unless you have a reason.

**What is the biggest list I can send?**
`maxItems` goes to 10,000 charged rows per run. Very long lists are better split across runs. It
gives you results sooner and keeps a single throttle from affecting the whole batch.

**Is the data live?**
Yes. Every row is fetched from Mojang at run time. Nothing is served from a cache.

# Actor input Schema

## `usernames` (type: `array`):

Minecraft Java usernames, one per line. Case does not matter — Mojang returns the real capitalisation, so "philza" comes back as the account's actual spelling. Names are resolved ten at a time in a single request, so a list of 500 costs 50 requests, not 500.

## `uuids` (type: `array`):

Player UUIDs, with or without dashes, one per line. Use this to go the other way and get the current username. Mojang has no bulk endpoint for UUIDs, so each one costs its own request and a long UUID list runs slower than the same number of usernames.

## `includeTextures` (type: `boolean`):

Fetch the skin URL, texture id, classic/slim model flag and cape from Mojang's session server. This is a second request per player. Turn it off if you only need username-to-UUID and want the run to finish in a fraction of the time.

## `includeSignature` (type: `boolean`):

Also return the RSA signature Mojang attaches to the texture blob, so you can verify offline that the skin data really came from Mojang. Only useful if you are going to check it — leave it off otherwise.

## `maxItems` (type: `integer`):

Hard cap on charged rows for this run (1-10000). Sample rows and diagnostics do not count against it and are never charged.

## `proxyConfiguration` (type: `object`):

Optional. Leave this alone unless you need the run to go out through a particular network. Your own proxy servers are used exactly as given.

## Actor input object example

```json
{
  "usernames": [
    "jeb_",
    "Dinnerbone",
    "Dream"
  ],
  "uuids": [],
  "includeTextures": true,
  "maxItems": 100,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `results` (type: `string`):

One row per player: the canonical username, the UUID in both dashed and undashed form, whether a custom skin is set, the skin URL and texture id, the classic/slim model flag, the cape URL and texture id, and which input value produced the row. Empty input writes a single uncharged sample row; an unknown username, a malformed UUID or a throttled texture lookup writes an uncharged diagnostic row instead of a charged one.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "usernames": [
        "jeb_",
        "Dinnerbone",
        "Dream"
    ],
    "uuids": [],
    "includeTextures": true,
    "includeSignature": false,
    "maxItems": 100,
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("dami_studio/minecraft-player-lookup").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "usernames": [
        "jeb_",
        "Dinnerbone",
        "Dream",
    ],
    "uuids": [],
    "includeTextures": True,
    "includeSignature": False,
    "maxItems": 100,
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("dami_studio/minecraft-player-lookup").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "usernames": [
    "jeb_",
    "Dinnerbone",
    "Dream"
  ],
  "uuids": [],
  "includeTextures": true,
  "includeSignature": false,
  "maxItems": 100,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call dami_studio/minecraft-player-lookup --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,dami_studio/minecraft-player-lookup"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/xf8wcDhE1FMYboQlM/builds/KFzu2TwQ8KxNAE8cZ/openapi.json
