divvy-api
v0.1.0
Published
Typed, dependency-free client for the public DIVVY protocol API on Robinhood Chain, plus direct-from-chain curve reads that need no server at all.
Maintainers
Readme
DIVVY API
Public, read-only access to live protocol data.
No key, no account, no rate-limit negotiation. Everything below is served unauthenticated so anyone can monitor DIVVY without asking us first.
https://app.divvy.capital
Website · Verify · Epoch ledger
Why this exists
The protocol's central claim is that its numbers survive being checked. That is only true if checking is easy, so the same data the app reads is open to anybody building a dashboard, an alert bot, a research note, or an independent audit.
Every endpoint is read-only. There is no write path here — every state change in DIVVY is a transaction the user signs themselves.
Status
| | | |---|---| | Live | Endpoints marked ✅ below respond today | | Planned | Endpoints marked 🔨 ship with the vault contract |
$DIVVY is trading on its pons V2 bonding curve. The vault contract is not deployed yet,
so no epoch has settled and no settlement data exists to serve. The endpoints that
describe chain, market and board state are live now, and curve() in the client below
reads the launch's live state straight from the chain without going through this API at
all.
Install
npm install divvy-apiTyped, dependency-free, Node 18+ or any modern browser.
import { DivvyApi, formatEther, formatTokens } from "divvy-api";
const divvy = new DivvyApi();
await divvy.chain(); // { chainId, block, gasGwei, at }
await divvy.screen(); // candidate screen + the thresholds it applied
await divvy.tallies(); // running nominations for the opening boardReads that do not go through us
curve() skips this API entirely and derives everything from the pons V2 factory and
the launch's own bonding curve. Nothing of ours sits in the middle, so the numbers are
checkable without trusting this package's author — which is the point.
const curve = await divvy.curve();
curve.progress; // 0..1 of the way to graduation
formatEther(curve.raised); // quote collected so far, in ETH
formatTokens(curve.sellable);
curve.claimableInEscrow; // swept fees waiting in the launchpad escrow, wei
curve.pendingTax; // creator tax accrued on the curve, not yet swept, weiOne caveat worth knowing before you deploy anything: the public Robinhood RPC refuses datacenter addresses. Browsers are fine on the default endpoint; a server needs its own provider.
const divvy = new DivvyApi({ rpcUrl: process.env.RPC_URL });Contract addresses are exported too — PONS_V2, RH_CHAIN, DIVVY_TOKEN — read from
the live factory rather than copied from the launchpad's published sources, which lag
the deployment.
Live endpoints
✅ GET /api/chain
Chain heartbeat. Read from the explorer rather than the public RPC, which rejects datacenter addresses.
curl https://app.divvy.capital/api/chain{
"chainId": 4663,
"block": 52110488,
"gasGwei": 0.89,
"at": "2026-09-02T01:20:47.206Z"
}| Field | Type | Meaning |
|---|---|---|
| chainId | number | Robinhood Chain, always 4663 |
| block | number | Blocks indexed by the explorer. See the note below — this is not the chain head |
| gasGwei | number | Average gas price in gwei |
| at | string | ISO 8601 timestamp of the reading |
On block. This is the explorer's count of indexed blocks, not eth_blockNumber.
It trails the chain head, and because the upstream is served from a CDN, two requests
seconds apart can land on edge nodes holding different snapshots — so the value can
appear to move backwards. Treat it as a liveness signal, not as a height or an ordering
key. If you need the true head, read eth_blockNumber from an RPC yourself.
✅ GET /api/screen
The candidate screen from the protocol spec, run live. A token must clear every threshold to be eligible for a board seat.
curl https://app.divvy.capital/api/screen{
"screen": { "minLiquidityUsd": 10000, "minAgeHours": 24, "minHolders": 50 },
"rows": [
{
"address": "0x5cb6f181081301b44905f3ae15419112ecabd8a6",
"symbol": "PIPEDOG",
"name": "pipedog",
"liqUsd": 8735764,
"ageHours": 845,
"vol24h": 2528926,
"holders": 8619,
"passes": true,
"fails": []
}
],
"fetchedAt": "2026-09-02T01:20:49.697Z"
}| Field | Type | Meaning |
|---|---|---|
| screen | object | The thresholds in force. Read these rather than hardcoding them |
| rows[].liqUsd | number | Deepest pair liquidity in USD |
| rows[].ageHours | number | Hours since the pair was created |
| rows[].holders | number | null | Distinct holders. null when the explorer has not indexed it |
| rows[].passes | boolean | True only when fails is empty |
| rows[].fails | string[] | Which thresholds failed: liquidity, age, holders |
| stale | boolean? | Present only when serving a cached snapshot |
| staleReason | string? | Why the live fetch failed |
On stale. Upstream sources rate-limit, so a cached snapshot is served rather than an
error. Check fetchedAt before treating a row as current.
Sorted by liquidity descending, capped at 25 rows.
✅ GET /api/tallies
Live nomination counts for the opening board, plus the launch-list size. Aggregate only — no wallet addresses or emails are exposed here or anywhere else.
curl https://app.divvy.capital/api/tallies{ "tallies": [], "waitlist": 2 }| Field | Type | Meaning |
|---|---|---|
| tallies[].seat | number | Seat 1–9 |
| tallies[].token_address | string | Nominated token |
| tallies[].token_symbol | string | Its symbol |
| tallies[].votes | number | Distinct wallets nominating it for that seat |
| waitlist | number | Addresses on the launch list |
Planned endpoints
These ship with the vault contract. They are listed so integrators can design against the shape now — the field names below are the ones that will be used.
| Endpoint | Returns |
|---|---|
| 🔨 GET /api/pot | Open pot, countdown to settlement, weighted wallet count |
| 🔨 GET /api/epochs | Settlement ledger with merkle roots and the tx that wrote each |
| 🔨 GET /api/epochs/:id | One epoch in detail, including its leaves |
| 🔨 GET /api/position/:address | Balance, streak, multiplier, unclaimed, payout mode |
| 🔨 GET /api/proof/:id/:address | Merkle proof for a claim |
| 🔨 GET /api/board | The nine seats and their weights |
| 🔨 GET /api/exclusions | The constructor exclusion list |
Conventions
- Read-only. No endpoint accepts a write.
- No authentication. No key to request, no quota to apply for.
- JSON only.
Content-Type: application/jsonon every response. - Timestamps are ISO 8601 in UTC.
- Amounts in USD are numbers, not strings. On-chain values will be strings in wei when settlement endpoints ship, to avoid float precision loss.
- Errors return a non-2xx status with
{ "error": "..." }. - Send a User-Agent. The edge answers
403to default agents from some HTTP clients — Python'surllibamong them. Any identifiable string works, and naming your tool is appreciated.curland Nodefetchare fine as they are.
Please be reasonable
There is no enforced rate limit. Cache what you poll, and prefer one request every few
minutes over one per second. /api/screen refreshes at most every 5 minutes, so polling
faster returns the same payload.
Examples
Working scripts in examples/:
| File | What it does |
|---|---|
| curl.sh | Every endpoint, plain shell |
| fetch.mjs | Node — prints eligible seat candidates |
| client.py | Python — same, with a tiny typed wrapper |
Verifying rather than trusting
This API is a convenience, not an authority. Every number it returns is derived from public chain data, and where the two disagree, the chain is right.
When the settlement endpoints ship, the snapshot tool will let anyone rebuild any merkle root independently and compare it against the one published on-chain. If a rebuild disagrees with a published root, the rebuild is right.
See divvy.capital/verify.
Disclaimer
$DIVVY is a memecoin. Nothing in this repository is financial advice, and no endpoint here promises, targets, or guarantees any rate of return. Settlement size is a direct function of trading activity and may approach zero.
