API docs
17 endpoints, all but one read-only. Every one below runs against whatever backend this build points at, so what you see is your data, not a sample.
Conventions
- Nothing here signs anything. The service holds no private key, and no route can produce a transaction. One route is a POST,
/wallet/:address/scan, and it appends a job to a queue; the worker that drains it reads the chain and writes to this service’s own database. Everything else is a GET. - Two routes reach the chain.
?refresh=trueon the balances route costs oneeth_callper token on the page being served, capped at 50 per request; ask for more and it is refused rather than partly served. A queued scan costs aneth_getLogssweep, which is why it is queued rather than served inline, and why it is refused when the wallet was scanned recently or the queue is full. Every refusal carriesRetry-After. - The API is rate-limited. Reads and chain-touching requests carry separate budgets, because a database round-trip and an
eth_callfan-out do not cost the same thing. Exceeding either returns429with aRetry-Afterheader. - Amounts come back twice. A raw base-unit string, because JSON has no bigint, and a decimal string for display. Compute on the raw one.
- A null
formattedvalue is information. It means the token never disclosed its decimals. Guessing 18 would be worse than saying so. - Addresses are returned checksummed and accepted in any case.
- 404 means “not indexed yet”, not “no such wallet”. The message names the route that fixes it. Two routes deliberately never 404: the scan status and the sweep list both answer for a wallet nothing is known about, because “nothing has happened here” is a complete answer and an error is a poor way to say it. 503 means the index is unreachable; the token routes keep working without it.
- Balances carry the block they were read at. Every response says how old its numbers are instead of letting you assume they are current.
Endpoints
Two of these read history this service caused rather than history it observed. /wallet/:address/scan queues the work; /wallet/:address/sweeps reads back what an executed cleanup actually did, out of the chain’s own logs. The second only counts events from a DustSweeper this instance is configured to trust: logs are not authenticated, so an unconstrained filter would let any contract write payouts into a stranger’s history.
One reads nothing about anybody. /metrics answers about this deployment (how much has been recovered, in which tokens, how many wallets got as far as sweeping, and why scans have failed) and returns no address, no transaction hash and no per-wallet row. Every figure in it that could be assembled dishonestly says so instead: a combined recovered total only exists when the tokens can actually be added, and every rate arrives with its own numerator, denominator, and the window it covers.
Liveness. Answers without touching the chain or the database.
Which capabilities are actually wired up, read off the running service rather than reconstructed from defaults: the providers behind prices, quotes, simulation, stock-token routing, execution and gas, the market resolver that decides whether a position can be valued at all, and the fee model: 1% capped at $5.00, a 30% ceiling on the BAG discount, scans free, revokes costing only gas. A provider named 'none' is unconfigured, and nothing fabricates a value in its place. priceProvenance separates a market feed ('live') from a price an operator typed into a config file ('stated'), because the two are indistinguishable once rendered as dollars. gasProvenance answers the same question separately about the gas figure, since the two prices come from different places and a deployment can have a feed for one and a config constant for the other; 'none' there is the answer to why a plan says 'before gas'.
Readiness. Confirms the RPC answers and reports the head block. 503 when the node is unreachable.
Metadata and supply for the configured DustToken. 503 until TOKEN_ADDRESS is set.
One balance, read live from the contract. Raw and formatted.
Recent Transfer events for the configured token, read live.
- blocks
- How many blocks back to scan. 1–10000, default 1000.
Sync status and headline counts for an indexed wallet. 404 until it has been scanned.
Balances and the block height they were read at, plus the scan summary: how many positions fell into each of the four classes, and the Recoverable Value across them. That total refuses rather than reporting $0.00 when no market data was configured, or when the request was paged and so describes less than the whole wallet. The only wallet route that can reach the chain, and only for tokens already in the index.
- refresh
- true re-reads this page from the chain first. Capped at 50 tokens per request; a larger limit is refused, not truncated.
- nonZeroOnly
- true hides closed positions.
- limit / offset
- Paging. limit 1–1000, default 100.
Every token the wallet has ever held, zero balances included. The dead positions are deliberately not filtered out.
- nonZeroOnly
- true hides closed positions.
- limit / offset
- Paging.
The cleanup these positions add up to: which would be swept together, which are held back and why, and what the job nets. Answers a different question from the scan rather than a summarised one: a position the scan calls Dust is one it has said is worth sweeping with others, and the scan cannot see whether the others exist. Suspicious positions are never included however good their quote looks, and this is the one route that checks: before planning, each sweepable position's sell is tried against the chain, and a token that will not release the balance is re-classified Suspicious and held back. The response's sellCheck says whether that ran, over how many, and what it proves. Each leg carries the route its quote was priced along and a minimum output derived from a stated slippage, so the plan is executable rather than merely descriptive, and `execution` says whether anything can act on it: the sweeper's address, the venue it trades through, and the fee rate and ceiling, all read off the deployed contract rather than out of this server's configuration. It refuses to offer execution when the deployed sweeper sells into a different token than the plan is priced in, trades at a different venue than produced the quotes, or charges above the published rate. The fee is computed once over the batch, which is what makes the $5.00 cap a per-job ceiling. `gas` prices the whole job rather than the one transaction it looks like (an approval per leg and then the sweep), and once it is priced the proceed-or-hold decision is made against what the user actually keeps rather than against a figure with that cost left out. It is modelled from the leg count and each leg's route, not measured: at the moment a plan is built the approvals do not exist and an estimate would revert on the first transfer, so the model is calibrated against real receipts and set to over-state. `netUserMicros` stays null whenever it could not be priced, and the plan says which figure it is showing. Deliberately not pageable: a page of rows is a smaller true answer, but a plan over a subset is a different recommendation under the same name. Capped server-side at the 200 largest positions, and the response says when that was reached.
- thresholdMicros
- Minimum the whole job must net, in microUSD (1000000 = $1.00). Default $1.00.
Allowances granted by this wallet, with whether they are unlimited and when each was last used.
- activeOnly
- true hides allowances already at zero.
The queued or running scan for this wallet, and how the last one went. Always answers, including for a wallet that has never been scanned: 'nothing has ever run here' is the thing a client needs in order to offer the button, and an error is a poor way to say it. `active` is the job in flight; `latest` is the most recent whatever its status, so a failure with nothing running does not render as silence.
Queue a scan of this wallet. The only non-GET route in this API, and it still signs nothing: the worker draining the queue runs eth_getLogs and eth_call and writes to this service's own database, exactly as the CLI always has. 202 with a new job, 200 with the job already running for this wallet, 429 when the wallet was scanned inside the cooldown, 503 when the shared queue is full. Every refusal carries Retry-After. Full rescans are refused with 403 unless the deployment sets SCAN_ALLOW_FULL: a normal scan resumes from the wallet's checkpoint, and a cooldown sized for that is not a ceiling on re-reading every block from the start.
- full
- Body field, not a query param. Ignores the stored checkpoint. 403 unless SCAN_ALLOW_FULL is on.
§1.8: a plain-language explanation of a result this API already computed. A POST, but it changes nothing: a question does not belong in a URL, where every proxy in the path logs it and the browser keeps it in history, and a user asking about their own holdings has written something that should not outlive the response. `task` is one of priority, before-after, explain-position, summarize-history or answer; the first two describe the whole plan, the last three need a `token`, and answer needs a `question` of at most 300 characters. What it does NOT accept is facts. The browser is already holding a rendered plan and posting it would save this server a full recomputation. It would also let anyone hand the endpoint figures of their choosing and have a model narrate them fluently in this product's voice, on the page where somebody decides whether to approve a token allowance. So the request names a wallet and a task, and the server assembles the brief from its own read path. The model is shown figures the planner had already settled and never the inputs they came from: there is no RPC client and no database handle behind the seam, so a model that wanted to look up a balance has nothing to look it up with. On the way out, every money-, percentage- and quantity-shaped token in the reply is matched against that brief, and one that was not there discards the whole answer rather than being edited out of it, because a sentence with a number cut from it still reads as complete. Links and unfamiliar addresses are refused on the same grounds. Token names and symbols reach the model fenced and stripped of the characters that let a string lie about its own length or direction, because an ERC-20 called 'SYSTEM: ignore prior instructions' costs about a dollar to deploy and this product indexes tokens nobody vetted. The wallet address never leaves the building. Answers 200 either way: no model configured, a rate-limited provider and a failed figure check are all ordinary outcomes that a screen prints as a sentence rather than an error over an intact plan. Rate-limited harder than anything else here: one request costs a plan recomputation and a call against the operator's inference quota.
- task
- Body field. priority | before-after | explain-position | summarize-history | answer.
- token
- Body field. Required by the three position tasks, refused on the two plan tasks: sending one where it will be ignored looks like a question that was answered.
- question
- Body field, answer task only. 1–300 characters.
Executed cleanups, read back out of the chain's own Swept and LegSwept logs, the durable record that a browser's localStorage was never able to be. Every figure is what the transaction reported rather than what the plan predicted, and the two differ because the price moved between quoting and mining; that difference is the only measurement this project has of how accurate its own estimates are. Each sweep carries its legs, so a receipt can show what each position actually sold for. `configured` is the field to branch on: an empty list means 'this wallet has never swept' on a deployment that has a sweeper, and 'nothing was ever looked for' on one that does not, and rendering the same empty state for both tells half the readers something false. `sweepers` names the contracts this instance accepts events from, because logs are not authenticated: any contract can emit Swept naming any address as its owner, so an unconstrained filter would let a stranger write fabricated payouts into somebody's history. `legCount` is the contract's own count and can exceed the number of leg rows returned, which is how an incomplete breakdown says so instead of looking whole. Never 404s: no sweeps is a complete answer.
- limit / offset
- Paging. limit 1–200, default 50.
What this deployment has done, in aggregate. The only read route that answers about the instance rather than about a wallet, and correspondingly the only one that returns no address, no transaction hash and no per-wallet row: it is not a lookup surface and cannot be turned into one. Recovered value is reported per output token, in that token's own units; the single combined figure is present only when every sweep paid out in the same token, or when a price source can convert them, and otherwise carries a sentence explaining why there is no such number, because adding six-decimal USDC to eighteen-decimal DAI produces a figure denominated in nothing. Conversion counts wallets from the durable wallets table rather than from scan_jobs, which is purged on a retention window: a funnel whose denominator is deleted nightly climbs on its own. Scan failures are grouped by a cause the worker recorded at the time, never by pattern-matching the error message, and every count arrives with `oldestFinishedAt`: the purge means these describe whatever survived the window, and that timestamp is the real start of the period. Rates are null rather than zero when there is nothing to divide by, since 'nobody converted' and 'nobody has been measured' are different claims. 404 when METRICS_ENABLED is false.
Indexed transfer history, newest first, with direction and delegation.
- token
- Filter to one ERC-20 contract address.
- limit / offset
- Paging.