API reference
The 0xCPU API
Everything the site does, you can do with curl. JSON in, JSON out — except /api/work, which streams binary frames.
Overview
Base URL https://0xcpu.fun. Reading is open. Spending — custom renders, grinds, reservations — needs a session: a signed, HttpOnly cookie you get by signing a Sign-In with Ethereum message. Requests that change state must come from the same origin or carry no Origin header (e.g. curl).
Money is ETH. Credit is kept in wei, quotes convert at the Chainlink ETH/USD feed at quote time, and every job bills the core time its workers measured — never more than the quote, with the remainder refunded when the job completes.
Sign-In with Ethereum
{ nonce } and sets a signed nonce cookie (10 min).{ message, signature } — an EIP-4361 message for this domain with that nonce, signed with personal_sign. Smart-contract wallets (ERC-1271 / ERC-6492) are verified on-chain. Sets a 7-day session cookie.{ address } or { address: null }.0xcpu.fun wants you to sign in with your Ethereum account:
0xYourAddress
Sign in to 0xCPU. This signature costs no gas and approves nothing.
URI: https://0xcpu.fun
Version: 1
Chain ID: 1
Nonce: <nonce>
Issued At: 2026-09-12T12:00:00.000Z
Expiration Time: 2026-09-12T12:10:00.000ZPulse & health
curl https://0xcpu.fun/api/pulse | jq '{eth, block, stats: {renders: .stats.renders, coreMs: .stats.coreMs}}'Jobs
A job is a signed promise of work: the server validates the request, reserves the quote, and returns a token that authorizes exactly that job’s units. Workers record what they did; completion bills from those records only.
{ "kind": "render" } — the public hero render (16 tiles, 768², no sign-in).{ "kind": "render", "params": { "view": View, "location"? } } — a custom render (session).{ "kind": "grind", "params": { "prefix", "deployer", "initCodeHash", "protect"? } } — CREATE2 salt search (session).{
"cx": -0.7453, "cy": 0.1127, // centre in the complex plane
"scale": 3.4e-6, // complex units per pixel
"cos": 1, "sin": 0, // rotation (sent as numbers so both sides agree bit-for-bit)
"maxIter": 800, "width": 1920, "height": 1080,
"ss": 1 // 1, or 2 for 2×2 supersampling
}curl -X POST https://0xcpu.fun/api/jobs -H 'content-type: application/json' -d '{"kind":"render"}'
# → { "job": { "id": "…", "units": 16, "params": { "view": {…}, "tile": 192 }, "tiles": [...] }, "token": "…" }Streaming work
{ token, i } — run unit i of the job. Renders stream four progressively sharper passes of tile i; grinds stream progress while searching salts for slot i % 16, round ⌊i / 16⌋. Every call ends with a trailer carrying the worker’s CPU, region, timing and a keccak256 receipt.curl -X POST https://0xcpu.fun/api/work -H 'content-type: application/json' \
-d '{"token":"<token>","i":0}' --output tile-0.binFrame format
Each frame is u32 LE length (of type + body), u8 type, then the body.
| Type | Body |
|---|---|
| 1 PASS | u8 pass · u8 stride · u16 gw · u16 gh then deflate-raw of Δmu u16[gw·gh] ‖ inside u8[gw·gh]. mu is the mean smooth iteration ×16 of the escaped samples; Δ is a row-wise delta (mod 2¹⁶); inside counts samples that never escaped. |
| 2 TRAILER | { n, ms, hash, iterations | attempts, receipt, worker: { cpu, cores, memMb, region, node, container, cold } } |
| 3 PROGRESS | { n, attempts, ms, rate, best: { nibbles, address } } (grinds) |
| 4 ERROR | { n, error } — e.g. tile_timeout; the unit is not billed. |
hash is keccak256 of the final pass’s mu bytes (LE) followed by the inside bytes.
Verifying a tile
Integer iteration counts use only +, −, × and comparisons on IEEE-754 doubles, so every JavaScript engine reproduces them exactly. Re-render the tile with the same kernel and compare: inside must match exactly and mu within 1 (the smooth term uses log2, whose last bit may differ; any integer mismatch moves mu by at least 4).
import { renderTile, compareTiles } from "./lib/compute/mandelbrot";
const local = renderTile(view, tile); // same View + Tile the server used
const { match, total } = compareTiles(received, local);
console.log(match === total ? "identical" : `${total - match} pixels differ`);Credit & top-ups
{ chainId, txHash }. Credits the sender of an ETH transfer to the treasury once it has enough confirmations (Ethereum 2, others 1); returns 202 pending until then. Idempotent — resubmitting never double-credits.Reservations
{ tier, region, term: "hourly"|"monthly"|"yearly", qty, note? } — a request, nothing is charged.Limits
| Limit | Value |
|---|---|
| Hero renders | 8 per network per 10 min · 150 per day for everyone |
| Free core-time | 10 core-minutes per wallet per UTC day |
| Custom renders | ≤ 3840×2160, ≤ 3000 iterations, ≤ 96 tiles, ss 1 or 2 |
| Grinds | prefix ≤ 7 hex (≤ 6 on the free allowance) |
| Per unit | 8 s of compute; job tokens live 5 min (renders) / 10 min (grinds) |
| Top-ups | ≥ 0.001 ETH, ETH only |
Errors
Errors are JSON { error, detail? } with a meaningful status: 400 bad input, 401 sign_in_required / bad_token, 402 insufficient_credits / free_allowance_exhausted, 409 job_closed / fresh, 429 rate_limited / budget_exhausted, 503 market_unavailable / busy.