All GET. These answer from the client’s own state and never wait on the server, so they cost about 0.35 ms and are safe to poll.

They carry no response envelope — only their own fields.

Every response below was captured from a live 26.2 server.


GET /state

Everything about the bot itself.

Parameters — none.

Response

field type meaning
username string the offline-mode name it joined with
uuid string derived from the username
entity_id int the server’s id for this player, as other entities see it
state string handshake, login, configuration or play
joined bool true once it is in play
x, y, z float position
yaw, pitch float facing; yaw 0 is south, pitch −90 is straight up
on_ground bool what the bot last told the server
health float 0–20
food int 0–20
dead bool awaiting respawn
deaths int count since connecting
held_slot int 0–8, the hotbar index
game_mode string survival, creative, adventure, spectator
effects array active potion effects: id, name, amplifier, duration in ticks
damageable bool whether damage can land at all
not_damageable_because string present only when damageable is false
curl localhost:8181/state
{
  "damageable": true,
  "dead": false,
  "deaths": 0,
  "effects": [
    {
      "id": 21,
      "name": "minecraft:absorption",
      "amplifier": 0,
      "duration": 652
    }
  ],
  "entity_id": 32428,
  "food": 20,
  "game_mode": "survival",
  "health": 20,
  "held_slot": 5,
  "joined": true,
  "on_ground": true,
  "pitch": 3.5,
  "state": "play",
  "username": "ApiDocs",
  "uuid": "31a931ac-1afc-322a-87cb-685d1076b91d",
  "x": 5000.5,
  "y": 100,
  "yaw": -90,
  "z": 5000.5
}

damageable exists because its absence cost real time. A working totem of undying was reported as broken, and the cause was the player being in creative with nothing anywhere saying so.


GET /inventory

Parameters

name type required meaning
count string no ask about one item instead of listing everything
want int no with count, ask whether that many would fit

Item names may be bare (bread) or namespaced (minecraft:bread), and may carry a block state or a component list — wheat[age=7], potion[potion_contents={potion:"minecraft:water"}]. This client knows an item by its id, so the qualifier is dropped and the answer covers the id: every potion, not only the water ones. When that happens the response says so, with matched_as and ignored_qualifier, because giving that answer without saying it is how a caller comes to trust a number that does not mean what they think.

Everything carried

curl localhost:8181/inventory
field type meaning
items array slot, id, name, count, and potion when the stack is one
held_item, held_slot string, int what is in hand
pickups, picked_up int, array items collected since connecting
truncated bool present when the list was cut for size

Slot numbers are the protocol’s own: 36–44 is the hotbar, 9–35 the main rows, 5–8 armour, 45 the off-hand.

{
  "held_item": "minecraft:golden_apple",
  "held_slot": 5,
  "items": [
    {
      "slot": 36,
      "id": 971,
      "name": "minecraft:netherite_pickaxe",
      "count": 1,
      "potion": -1
    },
    {
      "slot": 37,
      "id": 1,
      "name": "minecraft:stone",
      "count": 64,
      "potion": -1
    },
    {
      "slot": 38,
      "id": 1044,
      "name": "minecraft:snowball",
      "count": 8,
      "potion": -1
    },
    {
      "slot": 39,
      "id": 998,
      "name": "minecraft:diamond_helmet",
      "count": 1,
      "potion": -1
    },
    {
      "slot": 40,
      "id": 161,
      "name": "minecraft:oak_log",
      "count": 16,
      "potion": -1
    },
    {
      "slot": 41,
      "id": 1014,
      "name": "minecraft:golden_apple",
      "count": 4,
      "potion": -1
    }
  ],
  "picked_up": 0,
  "pickups": {},
  "truncated": false
}

One item

curl 'localhost:8181/inventory?count=minecraft:stone'
field type meaning
total int how many are carried anywhere
storage_only int how many are outside the hotbar
free_slots int empty slots left
stack_size int the item’s own stack limit
slots_needed int slots want would occupy
fits bool whether want would physically fit
matched_as string the id actually counted, when a qualifier was dropped
ignored_qualifier string the part that was dropped
{
  "fits": true,
  "free_slots": 30,
  "item": "minecraft:stone",
  "slots_needed": 1,
  "stack_size": 64,
  "storage_only": 64,
  "total": 64,
  "want": 1
}

An id carrying a state or components

curl 'localhost:8181/inventory?count=minecraft:wheat%5Bage%3D7%5D'
{
  "fits": true,
  "free_slots": 35,
  "ignored_qualifier": "age=7",
  "item": "minecraft:wheat[age=7]",
  "matched_as": "minecraft:wheat",
  "slots_needed": 1,
  "stack_size": 64,
  "storage_only": 12,
  "total": 12,
  "want": 1
}

The last two fields are the honest part. Twelve wheat is the right answer here, but ask for a water bottle and the same drop counts every potion — so the answer says which question it actually answered. Match on matched_as if it matters, rather than on the name you sent.

Would this many fit

curl 'localhost:8181/inventory?count=dirt&want=2304'
{
  "fits": true,
  "free_slots": 30,
  "item": "dirt",
  "slots_needed": 36,
  "stack_size": 64,
  "storage_only": 0,
  "total": 0,
  "want": 2304
}

2304 is 36 stacks of 64, so it fits only in a completely empty inventory.


GET /block

Parameters

name type required meaning
x, y, z int yes the block position
field type meaning
state int the raw block-state id
loaded bool whether the chunk has arrived
solid, water, lava, air bool classification of that state
targetable bool whether a dig could break it
curl 'localhost:8181/block?x=5006&y=100&z=5000'
{
  "air": false,
  "lava": false,
  "loaded": true,
  "solid": true,
  "state": 1,
  "targetable": true,
  "water": false,
  "x": 5006,
  "y": 100,
  "z": 5000
}

Check loaded before trusting anything else. An unsent chunk reads as air:

curl 'localhost:8181/block?x=0&y=200&z=0'
{
  "air": true,
  "lava": false,
  "loaded": false,
  "solid": false,
  "state": 0,
  "targetable": false,
  "water": false,
  "x": 0,
  "y": 200,
  "z": 0
}

air: true, solid: false there means “nothing has been sent”, not “there is nothing there”. Treating the two as the same is the single most expensive mistake against this API, and is what once made a bot hover in place until the server kicked it for flying.


GET /ground

Where the floor is beneath the bot, and whether that is known at all.

Parameters — none.

field type meaning
known bool whether the column below has been sent
found bool whether a floor was found in it
ground_y float the height of the surface stood on, not the index of the block below it
y float the bot’s own height
gap float blocks of air between the two
in_water, in_lava, submerged bool fluid state
chunks int how many chunks the client currently holds
curl localhost:8181/ground
{
  "chunks": 159,
  "found": true,
  "gap": 0,
  "ground_y": 100,
  "in_lava": false,
  "in_water": false,
  "known": true,
  "submerged": false,
  "y": 100
}

A floor of blocks at y=99 gives ground_y: 100: the number is the top face they present, which is also where a bot standing on them is, hence gap: 0.

known: false is not found: false. The first means ask again, the second means there is nothing there. After a teleport, wait on known rather than on the coordinate — chunks trail the position, and the coordinate arrives first.


GET /reach

Parameters

name type required meaning
x, y, z int yes the block position
field type meaning
distance float eye to block centre
reach float the limit the server enforces
can bool whether the block is within it
curl 'localhost:8181/reach?x=5006&y=100&z=5000'
{
  "can": false,
  "distance": 5.534835137562817,
  "reach": 4.5
}
curl 'localhost:8181/reach?x=5040&y=100&z=5000'
{
  "can": false,
  "distance": 39.50486552312259,
  "reach": 4.5
}

Asking first turns a silently-ignored packet into a decision you made.


GET /lookingat

Raycasts from the eye along the current facing.

Parameters — none.

field type meaning
hit bool whether anything was struck
x, y, z int the block, when hit
state int its block-state id
face int which face was struck, 0–5
distance float how far away it is
curl -X POST localhost:8181/look -d '{"block":{"x":5006,"y":100,"z":5000}}'
curl localhost:8181/lookingat
{
  "hit": false
}

Face numbering is the protocol’s: 0 down, 1 up, 2 north, 3 south, 4 west, 5 east.


GET /entities

Parameters

name type required meaning
type string no keep only this entity type
radius float no keep only those within this distance
field type meaning
count int how many matched
entities array id, type_name, x, y, z, distance
curl 'localhost:8181/entities?type=zombie&radius=12'
{
  "count": 1,
  "entities": [
    {
      "distance": 3.1622776601683795,
      "id": 32632,
      "type_name": "minecraft:zombie",
      "x": 5003.5,
      "y": 100,
      "z": 5001.5
    }
  ]
}

id is what /attack, /interact and /interactat take as entity_id. Targeting by id rather than by type is the difference between feeding the animal you meant and feeding whichever one happens to be nearest — and “nearest” is three-dimensional, so an entity forty blocks straight down can beat one two blocks away.


GET /recipes

What the player’s unlocked recipe book holds for an item — not the server’s catalogue.

Parameters

name type required meaning
item string yes the item to look up
field type meaning
found bool whether the player’s book has a recipe for it
item string what was asked for
recipe int the recipe id, for /container/craft
known int recipes decoded from the book so far
missing int entries that did not decode
curl 'localhost:8181/recipes?item=stick'
{
  "found": true,
  "item": "stick",
  "known": 108,
  "missing": 0,
  "recipe": 1259
}

A fresh survival player has almost nothing unlocked, so a bot that just joined answers found: false for sticks — a recipe that obviously exists. That reads as a decoding bug and is not one. Unlock the book first if you need all of it:

recipe give <player> *

known climbs as recipes unlock, and the 108 above is a partly-unlocked book rather than the whole catalogue.

missing is separate, and is not zero on every version. It reports how much of what was sent failed to decode, so a half-read book cannot be mistaken for a small one.


Back to top

understudy-client — Apache-2.0. Minecraft is a trademark of Mojang Synergies AB; this project is not affiliated with, endorsed by, or connected to Mojang or Microsoft.

This site uses Just the Docs, a documentation theme for Jekyll.