the detector, in your own code.
Everything the site and the Discord rooms show — the verdict, the screener, wallet dossiers, the live signals — as JSON for your bots, your mirrors, your own screener. The door is the Engineer: 50 machines on shift in the wallet that owns the key. Nothing is bought; standing is read from the shift contract every five minutes. API v1 is not open yet — this page is the spec.
getting a key
| 1 | put 50 machines on a shift — hopiummachines.xyz/staking. Machines on shift count; union cards and machines at home do not. |
| 2 | sign in with the same wallet on /account and create a key. Up to five keys, all sharing the wallet's budget. |
| 3 | send it in the x-api-key header. Clock out below 50 and the key answers 403 within five minutes; clock back in and it works again within five. |
curl -H "x-api-key: rh_…" https://detector.hopiummachines.xyz/api/v1/token/0x…
endpoints
| GET /api/v1/token/{address} | The verdict on a token. The detector's score (risk 0–100, band, every check with its detail), the ecosystem flag, and the holder census (holders, top-10 %, deployer %, supply float). A fresh cached verdict is free; a stale or missing one runs a fresh analysis inside the wallet's scan budget — past it you get the last verdict with `stale: true`, or 429 if there is none. address — 0x-prefixed 40-hex address (any case) (required); curl -H "x-api-key: rh_…" https://detector.hopiummachines.xyz/api/v1/token/0x… |
| GET /api/v1/screen | The screener. The same rows and filters as the site's screener: age, market cap, liquidity, holders, top-10 and deployer shares, bands, sources, sort, paging. Payload: `rows`, `runners`, `busted`, `total`, `scanned`, `now`. ageMaxMin — max age in minutes; mcMin — min market cap, USD; mcAthMin — min all-time-high market cap, USD; liqMinEth — min liquidity, ETH; holdersMin — min holders; top10Max — max top-10 holders share, %; devMax — max deployer share, %; bands — comma list: fresh,low,medium,high,critical; sources — comma list: v2,v3,v4,pons; hideBusted — 1 to drop rugged/dumped rows; sort — age | mc | holders | run; limit — 1–100; offset — 0–500; curl -H "x-api-key: rh_…" "https://detector.hopiummachines.xyz/api/v1/screen?ageMaxMin=60&liqMinEth=1&sort=holders&limit=50" |
| GET /api/v1/wallet/{address} | A wallet dossier. Positions and swap history from the detector's own ledger, realized PnL, win rate, and the labels the tracker gave the wallet (smart, sniper, bundler, deployer, farm funder). address — 0x-prefixed 40-hex address (any case) (required); curl -H "x-api-key: rh_…" https://detector.hopiummachines.xyz/api/v1/wallet/0x… |
| GET /api/v1/signals | The signals stream. Every card the detector posts, as it posts it: the row carries the token, the kind, the time and the card itself (`payload.embed`, Discord embed shape) plus a dossier url. Poll with the `next` id you got back; without `after` you get the newest 50. after — the last id you have seen (from `next`); rows with a greater id follow, ascending; kinds — comma list of tracker, first_swap, surge, new_token, liquidity_pulled, score_drop, deployer_dump; limit — 1–100, default 100 with `after`, 50 without; curl -H "x-api-key: rh_…" "https://detector.hopiummachines.xyz/api/v1/signals?after=12345&kinds=tracker,first_swap,surge" |
Machine-readable: /api/v1/openapi.json (OpenAPI 3.1, public).
the signals stream
Every card the detector posts lands in the stream the moment it posts — kinds: tracker · first_swap · surge · new_token · liquidity_pulled · score_drop · deployer_dump. Poll it: the first call sends no after at all and gets the newest 50 with a next; every later call passes that next back and gets only what is new — starting at after=0 would replay the whole week, oldest first. The row is the card itself (payload.embed, Discord embed shape) plus payload.url, the token's dossier. The log keeps seven days.
next=
while true; do
r=$(curl -s -H "x-api-key: rh_…" "https://detector.hopiummachines.xyz/api/v1/signals?kinds=tracker,first_swap,surge${next:+&after=$next}")
echo "$r" | jq -c '.signals[] | {kind, token, symbol, at}'
next=$(echo "$r" | jq .next)
sleep 5
donelimits and errors
| budget | 300 requests a minute per wallet, whatever the key. 30 of them may start a fresh token analysis; past that /token serves the last verdict with stale: true. Headers on every answer: x-ratelimit-limit, x-tier. |
| 401 | no key, or an unknown one — { "error": "api key required" } / { "error": "api key unknown" } |
| 403 | fewer than 50 machines on shift — { "error": "engineer tier required", "required": 50, "onShift": 12 } |
| 429 | budget spent — { "error": "rate limited", "limit": 300 }; on /token with nothing cached: { "error": "scan budget exhausted", "limit": 30 } |
| 503 | the shift contract could not be read — retry; nothing is cached from a failed read — { "error": "standing unavailable, retry" } |
| 404 | API v1 is switched off on this deployment — { "error": "not found" }; this page stays up, the routes do not. |
| CORS | Access-Control-Allow-Origin: * — call it from a browser page if you like; the key is still the key. |