For developers / Application APIs
Application APIs
Four first-party services sit beside the chain. None of them is the chain, none of them is versioned, and each one fails in a way of its own - which is what this section is for.
What these are
The chain's interface is JSON-RPC. Everything on these pages is something an application built for itself and then left reachable: a Postgres index over the sequencer's log spool, a process holding a key, a game loop. They are convenient, they are first-party, and they are not authoritative.
A read from one of these is not a read from the chain
Every figure here has been through an indexer, a cache, or both. A stopped indexer keeps answering with its last state and no field in the response says so; the only routes that tell you are the health routes, and you have to ask them. If the answer decides money, read the chain.
A read-only view of what the Pepper indexer has derived from the chain's logs: constant-product pools, concentrated pools, swaps, liquidity events and positions. It reads Postgres rather than the chain, deliberately.
- Stack
- Python, standard-library ThreadingHTTPServer, psycopg pool of at most 8 connections, HTTP/1.1
- Mounted
- /api/ on the Pepper origin
The archive the node deliberately is not. Blocks, mini-blocks, transactions, receipts, logs, call traces, decoded transfers, accounts, tokens, NFTs and the name-service record, all from a Postgres index fed by the sequencer's own log spool.
- Stack
- Python, standard-library ThreadingHTTPServer, psycopg pool of at most 12 connections, plus live reads against the node
- Mounted
- /api/ on the explorer origin
The only first-party service that WRITES to the chain. It holds the faucet operator key and submits the drip on your behalf, which is why the page needs no wallet.
- Stack
- Python, standard-library SimpleHTTPRequestHandler serving a static site as well as the API, signing with eth-account
- Mounted
- its own origin; the home origin re-exposes two of its routes
A WebSocket server for the games, with a small HTTP surface beside it for health and for the figures the site's chrome draws. The socket is the interface; the HTTP routes are a snapshot of it.
- Stack
- Node, `ws` in noServer mode behind a plain http server, one event loop for eight game rooms
- Mounted
- /api/ and /ws/ on the arcade origin
Where they answer
| Service | Base URL |
|---|---|
| Explorer API | https://explorer.picklechain.xyz/api/also mounted at /api/ by the name-service and toolkit sites |
| Pepper DEX API | https://pepper.picklechain.xyz/api/same-origin only; a browser on another origin cannot call it |
| Faucet | https://faucet.picklechain.xyzthe home origin re-exposes POST /drip and GET /faucet-status |
| Arcade | https://minigame.picklechain.xyz/api/and the socket at wss://minigame.picklechain.xyz/ws/<room> |
Not one of these services has a hostname of its own. Each is an /api/ mount on a front end's origin, which makes the public base an nginx fact rather than a service fact: a deployment that moves a site moves its API with it, and the path inside stays the same. On a stack you run yourself, the same services answer on loopback instead, on the port each service's own page names.
There is no /v1, no version header and no deprecation policy on any of the four. There is no OpenAPI document and no JSON Schema in the repository either, so a field can change shape between two deployments with nothing to compare against. Read defensively: ignore keys you do not know, and do not fail on a key that has gone.
Authentication
Not one of the four takes a credential, and the difference that matters is what each of them can do without one.
- The explorer, the DEX API and the arcade take none. They are read-only and public. The explorer answers
405to every POST; the DEX API implements no verb but GET. - The faucet takes none either, and it writes to the chain. That is the whole reason its quota is strict and is reserved before anything is signed.
The arcade socket has no authentication and the process holds a key
There is no authentication layer in front of it and none is planned: the proxy forwards the upgrade and that is all. What protects it is that every dimension of a connection is bounded - count, origin, frame size, message rate, buffered bytes - and every overflow is dropped rather than queued. Nothing a client says over that socket authorises anything; the contracts decide who may play.
Calling one from a browser
CORS is set per service and they do not agree, so the first thing to establish is whether a page on your origin can call the one you want at all.
- The DEX API sends no CORS header and has no OPTIONS handler. A cross-origin
fetchfrom a browser fails outright while the same request from a server succeeds, which is the shape of bug that gets blamed on the network. Proxy it, or call it from a backend. - The explorer and the faucet keep allowlists. A preflight from an origin that is not on the list is refused
403, which is at least a clear answer. - The arcade checks the Origin on the upgrade and refuses
403before the handshake, so the socket never opens rather than opening and going quiet.
Limits and paging
Only the explorer limits request rate. The other three rely on a clamp, a quota or a connection ceiling instead.
| Service | Limit |
|---|---|
| Pepper DEX API | `limit` is clamped to 1..500 on every list route and defaults to 50. There is no request-rate limit in the service. |
| Explorer API | 300 requests per minute per client address, then 429. Request bodies are capped at 600,000 bytes. Live streams are capped at 32 concurrent. |
| Faucet | Quota is reserved before the transaction is signed: 5 a day per address seen, 2 a day per funded address, 1000 a day in total. Request bodies are capped at 4096 bytes. |
| Arcade server | 256 sockets in total and 8 per source address, then the upgrade is refused 429. Inbound frames are 4 KiB at most and rate-limited per socket. A socket more than 512 KiB behind is cut. |
A clamped list is silently a short list
Ask the DEX API for a thousand swaps and you get five hundred, with a 200 and nothing to say the answer was cut. Ask the explorer for two hundred blocks and you get a hundred. Neither service returns a total on most list routes, so there is no field to compare your page length against - the only signal that more exists is that you got exactly the clamp.
Where paging exists it is limit and offset, not a cursor. That means a list that is growing at the head shifts under you between two pages: rows you have already seen reappear, and rows you have not can be skipped. For anything that has to be complete, page by a key you control - a block number, a hash - rather than by offset.
Conventions
| Convention | Value |
|---|---|
| Versioning | None of them is versionedno /v1, no version header, no deprecation policy. A field can change shape between two deployments. Read defensively and pin nothing. |
| Schema | There is no OpenAPI documentand no JSON Schema anywhere in the repository. Every shape documented here was read out of a route handler, which is why each record names the line. |
| Errors | {"error": "..."}a human-readable string, occasionally with a second key. There are no error codes and no stable error strings - match on the HTTP status, never on the message. |
| Caching | cache-control: no-storeon every route of all four services, without exception. Nothing here is safe to cache, and nothing offers you an ETag to revalidate against. |
| Content type | JSONwith two exceptions: the explorer's logo route returns raw image bytes, and its live route is an event stream. |
| Units | not uniformtoken amounts and wei are DECIMAL STRINGS, not numbers, because they do not survive a double. Mini-block timestamps are in MICROSECONDS. Concentrated-pool swap amounts are SIGNED. |
Amounts are strings, and some of them are signed
Token amounts, wei and balances come back as decimal STRINGS throughout, because they do not survive a double. Parse them with a big-integer type. On the concentrated-pool routes the amounts are also signed - one leg of every swap is negative, and that is which way the trade went - so a sum that does not take absolute values nets a whole market to nothing.
// The shape of a careful call against any of these: a timeout, a status check
// before the body, and a big-integer parse of anything that looks like money.
const res = await fetch(`${BASE}/pools`, { signal: AbortSignal.timeout(5000) });
if (!res.ok) throw new Error(`pools: ${res.status}`); // never the message
const { pools } = await res.json(); // an object, not an array
const reserve0 = BigInt(pools[0].reserve0); // a decimal stringWhen one is down
Each of the four fails differently, and only one of those failures looks like a failure from the outside.
- The DEX API answers
503 index not readywhen the database is unreachable or the indexer has never run, so the tables do not exist. That is the honest case. The dishonest one is an indexer that has STOPPED: every route keeps answering with the state it reached, and onlylagBlockson/healthsays so. - The explorer keeps serving the index when the node is unreachable, and the parts of an answer that need a live read - a balance, a name-service record, the fee router's pending balance - come back null or absent rather than erroring. The
indexLagfield on/statsis the figure to watch. - The faucet answers
502when the node does not respond, and nothing has been signed. A503means it is out of funds, or the drip reverted. - The arcade's
/readyanswers 503 when its socket to the node is down, or when the last mini-block it APPLIED is more than five seconds old. Measuring arrival instead let a wedged process answer 200 with nothing moving.
Only the faucet writes, and a 502 from it means nothing was submitted - that one is safe to retry. A 503 after the transaction went out is not: the drip may have landed and the quota reservation has already been rolled back, so poll the chain for the receipt rather than sending again.
What is documented elsewhere
- The node. Chain state, transactions, logs and subscriptions are JSON-RPC and are documented in this reference's own RPC pages, not here.
- Contracts. What the pools, the faucet and the games actually do on chain - their functions, their events and their revert reasons - is the contract reference.
- Prices. Several answers here carry a dollar figure or a price snapshot. Those fields come from a layer this page does not own; read its own reference for what the number means, how old it is and when it is absent.