Reference · v1
Read every square's live state as JSON.
The whole board is public. No key, no account, no rate card — just GET it. Build a bot that watches for cheap listings, a screensaver that renders the mosaic, a bridge that mirrors the ledger somewhere else. None of it needs our permission.
Conventions
- base
- All endpoints live under
/api/v1. Responses are JSON; errors are{ "error": string }with a real status code. - money
- Every amount is lamports, as a decimal string. Never a JSON number — a price can exceed
Number.MAX_SAFE_INTEGERand parsing it as a float silently loses money. Divide by 1e9 for SOL, carefully. - cors
Access-Control-Allow-Origin: *on every read endpoint. Call it straight from a browser.- rate limit
- 120 requests per minute per IP. The remaining budget comes back in
X-RateLimit-Remaining. Over it you get a 429 and no hard feelings. - caching
- Read endpoints are edge-cached for a few seconds. A revealed tile is
immutable— its pixels never change, only its owner does.
Endpoints
get/api/v1/mosaic
The board in one object: its shape, how much is revealed, what the next piece costs, and the ids of every revealed square.
{
"grid": { "cols": 40, "rows": 25, "total": 1000 },
"revealed": 0,
"unclaimed": 1000,
"holders": 0,
"nextPriceLamports": "20000000",
"volumeLamports": "0",
"market": { "listings": 0, "offers": 0, "swaps": 0 },
"revealedPieceIds": [],
"image": "/api/mosaic"
}get/api/v1/pieces
All 1000 squares in id order.
?claimed=true or ?claimed=false to filter. ?limit= and ?offset= to page.{
"total": 1000,
"count": 1000,
"offset": 0,
"pieces": [
{
"id": 0,
"label": "0000",
"col": 0,
"row": 0,
"claimed": false,
"owner": null,
"pieceNumber": null,
"mintPriceLamports": null,
"acquiredAt": null,
"tile": null
}
]
}get/api/v1/pieces/:id
One square, with its open listing, its offer book, and any swaps proposed for it.
{
"id": 512,
"label": "0512",
"col": 32,
"row": 12,
"claimed": true,
"owner": "7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU",
"pieceNumber": 3,
"mintPriceLamports": "20000000",
"acquiredAt": "2026-08-28T01:14:02.881Z",
"tile": "/api/tile/512",
"listing": null,
"offers": [],
"swapsOffered": [],
"events": "/api/v1/pieces/512/events"
}get/api/v1/pieces/:id/events
Everything that has ever happened to one square, newest first. Cursor through with the returned nextBefore.
?limit= up to 200, ?before= an event id.{
"pieceId": 512,
"count": 1,
"nextBefore": null,
"events": [
{
"id": "1",
"pieceId": 512,
"kind": "mint",
"from": null,
"to": "7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU",
"priceLamports": "20000000",
"signature": "4Nd1mB…",
"at": "2026-08-28T01:14:02.881Z"
}
]
}get/api/v1/market
The whole book: open listings, live offers and proposed swaps.
{
"listings": [],
"offers": [],
"swaps": [],
"note": "Offers are not escrowed. An accepted offer is an invitation to pay, not a claim on funds."
}get/api/v1/events
The board's whole history across every piece, newest first.
?limit={ "count": 0, "events": [] }get/api/v1/wallets/:address
What one wallet holds, what it has bid on, and what swaps it is party to.
{
"wallet": "7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU",
"piece": { "id": 512, "col": 32, "row": 12, "pieceNumber": 3,
"mintPrice": "20000000", "acquiredAt": "2026-08-28T01:14:02.881Z" },
"offers": [],
"swaps": []
}Images
Two routes return pixels rather than JSON, and both are gated on the ledger rather than on a key.
- /api/mosaic
- The board as one WebP, 1600×1000, with every unclaimed square punched out to transparent. On an empty board it is a completely transparent image, which is the correct answer.
- /api/tile/:id
- One 100 × 100 square. Returns 404 if nobody owns it — that is the reveal mechanic, not an error to work around.
Writing
There is no public write API and there will not be one, because every write is authorised by a wallet signature that only a wallet can produce. Buying goes /api/reserve → SystemProgram.transfer → /api/claim; listings, offers and swaps are signed messages posted to /api/market/*. The message text is rebuilt server-side from the fields you send, so a signature cannot be moved onto a different action than the one your wallet showed you.
Ownership is a database row, not a token — read the honest section before you build anything that depends on it.