AxeOS API Reference: Every ESP-Miner v2.14.2 Endpoint, Verified From Source
Every Bitaxe serves a complete, unauthenticated HTTP API on port 80 — the same API the AxeOS dashboard itself uses. This page is the endpoint-by-endpoint reference for that API, pinned to ESP-Miner v2.14.2 (the current release, published 2026-07-08). Every path, method, request field and response shape below was re-derived from the source at that tag — main/http_server/http_server.c and main/http_server/openapi.yaml — not from another article. The master branch moves; this page names its tag so you can hold it to account.
Scope: this is a reference, not a tutorial. For workflows — overclocking methodology, multi-pool strategy, network hardening, OTA planning — use the AxeOS advanced configuration & API guide; this page will not re-teach them. If you are looking for the DCENT_OS API, that is a different product with its own surface (REST, WebSocket, cgminer 4028, MCP) — see the DCENT_OS API reference. New to the device itself? Start at the Bitaxe hub.
Jump to: conventions · endpoint index · read endpoints · actions · PATCH settings · OTA · WebSockets · monitoring · the LAN rule
Conventions: base URL, authentication, errors
- Base URL:
http://<device-ip>— plain HTTP on port 80. Examples below use192.168.1.45; if mDNS works on your network, the device’s hostname (default shown in AxeOS settings) resolves ashttp://<hostname>.local. - Authentication: none. No password, no token, no API key. The only gate is a network-range check (next section).
- Content types: JSON in and out for the
/api/systemfamily, except/api/system/logs(plain text) and the two OTA upload endpoints (application/octet-stream). - CORS: the server answers
OPTIONS /api/*preflights and setsAccess-Control-Allow-Origin: *, so LAN dashboards served from another host can call the API from the browser. - Error responses:
401— request failed the network-range check (see below);400— invalid settings body or invalid firmware file;500— internal error. Documented per-endpoint in the repo’sopenapi.yaml.
The LAN rule (Verified in source): the /api/system endpoints answer only when both the requesting IP and the browser Origin header (when present) fall inside the RFC-1918 private ranges — 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16 (is_network_allowed and ip_in_private_range in http_server.c). Anything else gets 401 Unauthorized — including well-intentioned calls that arrive with a non-private source address, such as some VPN or overlay-network setups. When the device is in AP mode (initial setup), the check is waived. This origin-checking behavior is the descendant of the CSRF fix ESP-Miner shipped in v2.5.0 (January 2025, patch by @benjamin-wilson, release notes) — a changelog fact worth knowing when you read older API writeups.
Endpoint index — everything registered at v2.14.2
| Method | Path | What it does |
|---|---|---|
| GET | /api/system/info |
Full system status and telemetry — the object every dashboard polls |
| GET | /api/system/asic |
ASIC model, chip count, and the valid frequency/voltage option lists |
| GET | /api/system/statistics |
On-device history series; ?columns= selects which series |
| GET | /api/system/scoreboard |
Top 20 best-difficulty shares |
| GET | /api/system/wifi/scan |
Scan for nearby Wi-Fi networks |
| GET | /api/system/logs |
Download current device logs as a text file |
| POST | /api/system/identify |
Make the physical unit identify itself |
| POST | /api/system/restart |
Reboot the device |
| POST | /api/system/pause |
Pause mining |
| POST | /api/system/resume |
Resume mining |
| POST | /api/system/blockFound/dismiss |
Dismiss the block-found banner (keeps the count) |
| PATCH | /api/system |
Write settings: pools, SV2 fields, tuning targets, fan, network, display |
| POST | /api/system/OTA |
Upload a new esp-miner.bin (firmware OTA) |
| POST | /api/system/OTAWWW |
Upload a new www.bin (AxeOS web UI) |
| GET | /api/theme |
Read the UI color scheme and accent colors |
| POST | /api/theme |
Set the UI color scheme and accent colors |
| GET | /api/ws |
WebSocket: live device log stream (text frames) |
| GET | /api/ws/live |
WebSocket: live telemetry stream (JSON diffs, ≤1 update per 500 ms) |
| GET | /recovery |
Recovery page (also served for every route when the web-UI filesystem is missing) |
| OPTIONS | /api/* |
CORS preflight |
Unmatched /api/* routes hit a catch-all handler, and every other GET serves the AxeOS single-page app.
Added after v2.14.2 (master branch, not at the tag): PUT /api/system/pools/* and DELETE /api/system/pools/* (a pool-list CRUD pair) and POST /api/system/boot exist in current master but are not registered at v2.14.2 — on this release, pool changes go through PATCH /api/system. Conversely, endpoints that appear in older documentation — POST /api/system/factoryreset, GET /api/system/statistics/dashboard — are not registered at v2.14.2 either. If a script depends on any of these, test against your installed version before trusting a writeup, this one included.
Read endpoints
GET /api/system/info — the workhorse. Returns one large JSON object; the schema in openapi.yaml lists 80+ fields. The ones most scripts care about:
| Field | Meaning |
|---|---|
hashRate, hashRate_1m, hashRate_10m, hashRate_1h |
Hashrate in GH/s: instantaneous plus 1 m / 10 m / 1 h averages |
expectedHashrate |
What the current voltage/frequency should produce, in GH/s — compare against hashRate to spot underperformance |
temp, temp2, vrTemp |
Average chip temperature (two sensors) and voltage-regulator temperature |
power, voltage, current |
Power draw (W), input voltage, current (mA) |
frequency, actualFrequency, coreVoltage, coreVoltageActual |
Configured vs measured ASIC frequency (MHz) and core voltage (mV) |
sharesAccepted, sharesRejected, sharesRejectedReasons |
Session share counters; rejections broken down by pool-reported reason |
bestDiff, bestSessionDiff, poolDifficulty |
Best share ever / this session, and current pool difficulty |
stratumURL, stratumPort, stratumUser, stratumProtocol, isUsingFallbackStratum |
Active pool configuration; stratumProtocol is SV1 or SV2 |
responseTime |
Pool response time in ms (v2.14.0+ measures this per share on SV2 too) |
uptimeSeconds, resetReason, runningPartition |
Uptime, why the last reset happened, which OTA partition is active |
version, axeOSVersion, ASICModel, boardVersion, macAddr, hostname |
Identity: firmware and UI versions, chip, board, network identity |
freeHeap, cpuUsage, wifiRSSI, wifiStatus |
Device health: memory, CPU, Wi-Fi signal |
fanspeed, fanrpm, fan2rpm, autofanspeed, temptarget |
Fan state and the PID temperature target |
miningPaused, overheat_mode, power_fault, hardware_fault |
Pause state, overheat protection, and fault strings when something tripped |
blockFound, blockHeight, networkDifficulty, coinbaseOutputs |
Solo-mining context: blocks found, chain height, decoded coinbase outputs (when coinbase decoding is enabled) |
curl -s http://192.168.1.45/api/system/info | jq .
# or just the fields you chart:
curl -s http://192.168.1.45/api/system/info | jq '{hashRate, temp, vrTemp, power, sharesAccepted}'
GET /api/system/asic — the tuning envelope. Returns the detected chip and the exact option lists AxeOS itself offers, so scripts never have to hard-code valid values:
curl -s http://192.168.1.45/api/system/asic | jq .
Response shape (field examples from the spec): ASICModel (one of BM1366, BM1368, BM1370, BM1397), deviceModel (e.g. "Ultra"), asicCount, defaultFrequency / frequencyOptions (MHz), defaultVoltage / voltageOptions (mV), and swarmColor. Anything you plan to PATCH into frequency or coreVoltage should come from these lists.
GET /api/system/statistics — on-device history. Returns currentTimestamp, a labels array, and a statistics array of data-point rows matching those labels. The columns query parameter selects series (comma-separated); the spec’s own example list: hashrate, hashrate_1m, hashrate_10m, hashrate_1h, asicTemp, vrTemp, asicVoltage, voltage, power, current, fanSpeed, fanRpm, fan2Rpm, wifiRssi, freeHeap, responseTime. Sampling cadence is the statsFrequency setting (seconds; 0 disables), and statsLimit in the info object reports the buffer depth.
curl -s 'http://192.168.1.45/api/system/statistics?columns=hashrate,asicTemp,power' | jq .
GET /api/system/scoreboard — the top 20 best-difficulty shares, each with rank, since (seconds ago), difficulty, and the raw share components (job_id, extranonce2, ntime, nonce, version_bits). The badge-of-honor endpoint.
GET /api/system/wifi/scan — nearby networks as {ssid, rssi, authmode} objects, authmode being the ESP-IDF auth-mode code (0 = open through WPA3 variants). GET /api/system/logs — the current log buffer as a plain-text download:
curl -s http://192.168.1.45/api/system/logs -o bitaxe-logs.txt
Action endpoints
All four are parameterless POSTs answering {"message": "..."}:
curl -s -X POST http://192.168.1.45/api/system/restart # reboot; replies "System will restart shortly."
curl -s -X POST http://192.168.1.45/api/system/identify # the unit says hi (find it on a crowded shelf)
curl -s -X POST http://192.168.1.45/api/system/pause # stop hashing without powering off
curl -s -X POST http://192.168.1.45/api/system/resume # start hashing again
The restart endpoint is the one that earns its keep: it is the trigger behind the Bitaxe auto-restart watchdog that recovers flatlined units automatically. POST /api/system/blockFound/dismiss clears the block-found banner while preserving the blockFound count — the one endpoint you hope to need.
Writing settings: PATCH /api/system
One endpoint writes every persistent setting. Send a JSON object containing only the keys you want to change; they are validated against the Settings schema (invalid values return 400) and persisted to NVS. Two things to know before scripting it: the schema sets additionalProperties: true, so an unknown (misspelled) key is not rejected — spell keys exactly; and password-class fields (stratumPassword, fallbackStratumPassword, wifiPass) are write-only — they never appear in GET /api/system/info.
| Group | Keys | Constraints (from the schema) |
|---|---|---|
| Primary pool | stratumURL, stratumPort, stratumUser, stratumPassword |
port 1–65535 |
| Primary protocol | stratumProtocol, stratumV2ChannelType, stratumV2AuthorityPubkey |
protocol SV1 | SV2; channel type standard | extended; pubkey optional, base58, ≤52 chars |
| Fallback pool | fallbackStratumURL, fallbackStratumPort, fallbackStratumUser, fallbackStratumPassword, fallbackStratumProtocol, fallbackStratumV2ChannelType, fallbackStratumV2AuthorityPubkey, useFallbackStratum |
same shapes as primary; useFallbackStratum forces the fallback |
| Tuning | frequency, coreVoltage, overclockEnabled |
MHz / mV, ≥1; overclockEnabled 0|1 unlocks custom values in AxeOS |
| Cooling | autofanspeed, fanspeed, temptarget |
0|1; 0–100 %; 0–100 °C |
| Network / identity | ssid, wifiPass, hostname |
SSID 1–32 chars; password 8–63; hostname [a-zA-Z0-9-]+ |
| Display / misc | rotation, invertscreen, displayTimeout, statsFrequency, overheat_mode |
timeout −1 (always on) to 71582 min; statsFrequency 0 disables; overheat_mode: 0 clears overheat protection |
Switch the primary pool (values shaped like the spec’s own examples):
curl -s -X PATCH http://192.168.1.45/api/system \
-H 'Content-Type: application/json' \
-d '{"stratumURL":"stratum+tcp://pool.example.com","stratumPort":3333,"stratumUser":"worker1","stratumPassword":"x"}'
Enable Stratum V2 on the primary pool, keeping a V1 fallback (v2.14.0 added native SV2 with all four primary/fallback protocol combinations):
curl -s -X PATCH http://192.168.1.45/api/system \
-H 'Content-Type: application/json' \
-d '{"stratumProtocol":"SV2","stratumV2AuthorityPubkey":"<POOL_PUBLIC_KEY>","fallbackStratumProtocol":"SV1"}'
Two honest notes on the SV2 fields. First, the schema enumerates both standard and extended channel types — and at v2.14.2 both are implemented: extended is the default, and on BM1397 chips the firmware forces extended (the chip lacks hardware version rolling), so stratumV2ChannelType: "standard" is honored only on non-BM1397 chips (verified at the tag: main/tasks/stratum_v2_task.c). There is no Job Declarator client at this tag either way — SV2 on a Bitaxe means encrypted, binary-framed mining, not building your own block templates; even on extended channels the pool supplies the template. Second, the authority pubkey is your pool’s — copy it character-for-character from the pool’s own documentation, never from a third-party page. Step-by-step pool setup, endpoints and verification live in the companion guide: enable Stratum V2 on a Bitaxe. For tuning methodology — what to change, in what order, and how to validate it — use the advanced configuration guide; a restart (POST /api/system/restart) after settings changes is the reliable way to make sure every subsystem picks them up, and it is what the companion guide’s workflows do.
OTA endpoints: firmware and web UI
AxeOS updates are two files per release, and the API mirrors that: POST /api/system/OTA takes the firmware image (esp-miner.bin), POST /api/system/OTAWWW takes the web-UI image (www.bin). Both accept the raw binary as application/octet-stream and answer in plain text; the firmware endpoint validates the upload, switches the boot partition, replies "Firmware update complete, rebooting now!" and reboots. A bad file returns 400.
# from the directory holding the release assets for YOUR board:
curl -s -X POST http://192.168.1.45/api/system/OTAWWW \
-H 'Content-Type: application/octet-stream' --data-binary @www.bin
curl -s -X POST http://192.168.1.45/api/system/OTA \
-H 'Content-Type: application/octet-stream' --data-binary @esp-miner.bin
Workflow note: take both files from the same release so firmware and UI stay in step, and update the UI first, firmware second — the firmware upload reboots the device, the UI upload does not, so this order finishes cleanly in one pass. The device runs A/B partitions; runningPartition in the info object tells you which slot booted. Downloads and per-board file names are on the ESP-Miner releases page.
Theme endpoints
GET /api/theme returns {"colorScheme": ..., "accentColors": {...}}; POST /api/theme accepts the same shape and persists it. This is how the AxeOS UI stores its look; scripts rarely need it, but it is part of the registered surface and belongs in a complete reference.
WebSocket streams
Two WebSocket endpoints share one handler but stream different things (Verified in source — the registrations carry different stream types):
/api/ws— the log stream. Text frames containing device log output, drained from the on-device ring buffer only while at least one client is connected. This is the feed behind the AxeOS log viewer./api/ws/live— the telemetry stream. JSON frames shaped{"event": "update", "data": {...}}wheredatais a diff against the previously sent state, rate-limited to one update per 500 ms. First message after connect carries the full state; after that you only receive fields that changed. For dashboards this beats polling: no request overhead, and no wasted bytes on unchanged fields.
# websocat (or any WS client):
websocat ws://192.168.1.45/api/ws/live # telemetry diffs
websocat ws://192.168.1.45/api/ws # live logs
Monitoring recipe: /api/system/info → Prometheus → Grafana
The pattern that works: poll /api/system/info on an interval, translate the fields you care about into Prometheus metrics, graph in Grafana. What follows is a deliberately small sketch — the node_exporter textfile-collector version, no exporter daemon to maintain. Save as axeos-textfile.sh and run it from cron (or a systemd timer) every 30–60 seconds on a machine that already runs node_exporter with --collector.textfile.directory set:
#!/usr/bin/env bash
# axeos-textfile.sh - one Bitaxe -> node_exporter textfile collector. A sketch, not a product.
IP=192.168.1.45
OUT=/var/lib/node_exporter/textfile_collector/bitaxe.prom
J=$(curl -sf --max-time 5 "http://${IP}/api/system/info") || exit 1
{
echo "bitaxe_hashrate_ghs $(jq .hashRate <<<"$J")"
echo "bitaxe_temp_celsius $(jq .temp <<<"$J")"
echo "bitaxe_vr_temp_celsius $(jq .vrTemp <<<"$J")"
echo "bitaxe_power_watts $(jq .power <<<"$J")"
echo "bitaxe_shares_accepted $(jq .sharesAccepted <<<"$J")"
echo "bitaxe_shares_rejected $(jq .sharesRejected <<<"$J")"
echo "bitaxe_uptime_seconds $(jq .uptimeSeconds <<<"$J")"
} > "${OUT}.tmp" && mv "${OUT}.tmp" "$OUT"
Honest footnotes to the sketch: the share counters reset on every reboot, so treat them in Grafana with reset-tolerant functions (increase() / resets()) rather than raw deltas; for multiple Bitaxes, add a label per device (bitaxe_hashrate_ghs{unit="45"} ...) and loop over IPs; and if you would rather stream than poll, /api/ws/live above is the better transport for a custom exporter. On-device history via /api/system/statistics is the zero-infrastructure alternative when all you want is a recent-past chart. Fleet-level scripting on this same API — batch tuning, comparison runs — is covered in the Bitaxe auto-tuning scripts guide, and the highest-value four lines of monitoring you can deploy remain the auto-restart watchdog.
Keep it on the LAN
Standard operating practice for an unauthenticated device API, stated once and calmly: never expose a Bitaxe to the internet. No port-forwards to it, no DMZ. The firmware’s private-range check refuses non-RFC-1918 callers, and that is a floor, not a security architecture — treat network placement as the real control. On a home network, an isolated IoT VLAN (or at minimum your router’s client-isolation for untrusted devices) keeps the miner reachable from your monitoring box and nothing else; if you need remote visibility, reach the LAN through your own VPN and call the API from inside — remembering the LAN rule means the API sees your VPN client’s source address, which must land in a private range to pass. The full hardening walkthrough (DHCP reservations, VLAN layout, remote-access patterns) is in the advanced configuration guide.
FAQ
Does the AxeOS API require a password or API key?
No. There is no authentication of any kind — by design, for a LAN device. The only gate is the firmware’s network-range check: requests (and browser Origins) must come from RFC-1918 private addresses. That is exactly why the API must never be reachable from the internet.
Why does the Bitaxe API return 401 Unauthorized?
Your request failed the private-range check in is_network_allowed: either the source IP or the browser’s Origin header resolves outside 10.0.0.0/8, 172.16.0.0/12, or 192.168.0.0/16. Typical causes: calling across a VPN or overlay network that presents a non-private source address, NAT that rewrites the source, or a proxied browser origin. Call from a machine on the same private LAN.
Which AxeOS endpoint should a dashboard poll?
GET /api/system/info — it carries hashrate (with 1 m/10 m/1 h averages), temperatures, power, shares, pool state and device health in one object. For push instead of poll, connect to the /api/ws/live WebSocket, which sends JSON diffs at most every 500 ms and costs nothing between changes.
Can I configure Stratum V2 through the AxeOS API?
Yes, on ESP-Miner v2.14.0 or later: PATCH /api/system with stratumProtocol: "SV2" and optionally your pool’s stratumV2AuthorityPubkey (base58, up to 52 characters); the fallback pool has mirrored fields and any V1/V2 combination works. Both channel types are implemented at v2.14.2 — stratumV2ChannelType defaults to extended, and BM1397 chips are forced to extended — and there is no Job Declarator client: SV2 on a Bitaxe encrypts and reframes your mining connection; it does not build block templates. Full setup: enable Stratum V2 on a Bitaxe.
Can I add or delete pools through the AxeOS API?
Not at v2.14.2. The PUT /api/system/pools/* and DELETE /api/system/pools/* pair appears on the master branch after the v2.14.2 tag and is not registered in the v2.14.2 build — on this release, change pools with PATCH /api/system. If your device runs a later release, check its own source or release notes before scripting against them.
Credits
This API is the work of the ESP-Miner maintainers and contributors — Skot, WantClue, mutatrum and the wider bitaxeorg community (GPL-3.0). The Stratum V2 fields exist thanks to @warioishere’s PR #1553; the network-origin protection traces back to the v2.5.0 CSRF patch by @benjamin-wilson. Open Source Miners United’s wiki maintained a community endpoint list before this page existed — credit where due, and it remains a good quick lookup. Endpoint-by-endpoint verification against the v2.14.2 tag by D-Central.
Record last verified: 2026-08-13. Every endpoint, field and constraint on this page was re-derived from the ESP-Miner source at the v2.14.2 tag (main/http_server/http_server.c, openapi.yaml, theme_api.c, websocket_api.c, websocket_log.c, main/tasks/stratum_v2_task.c) on that date.
Related products, repair, and setup paths
- how D-Central diagnoses ASIC repairs
- ASIC troubleshooting library
- ASIC manuals and repair guides
- replacement hashboards
- ASIC control boards
- ASIC power supplies
- S19 family replacement hashboard
- C52 replacement control board
- APW12 S19 power supply
- compare specs in the ASIC miner database
- compare ASIC miner specs
- ASIC miner database
- ASIC repair services
- Antminer S19 specs and profitability
- buy a tested Antminer S19
- Antminer S19 maintenance guide
- Antminer S19 repair service
- Antminer S21 specs
- Bitmain Antminer S21
- Antminer S21 maintenance guide
- BM1370BC S21 Pro chip
Last reviewed August 13, 2026.
