Try it
curl https://dagcore.net/api/v1/poolEvery response the API produces is JSON and carries a generatedAt timestamp, errors
included. The machine-readable list of endpoints is at
/api/v1.
The promise
A field in v1 is never renamed and never changes meaning. If something has to
change in a way that would break code reading it, it is published as /api/v2 and v1
keeps running. New fields may appear — code that ignores what it does not recognise keeps
working.
The endpoints under /api/pool/ are not this. They serve this site's own pages, they
change without notice, and most of them are not reachable from outside at all. They are not
documented here on purpose.
Conventions
| Amounts | Decimal strings in wei, 18 decimals. Strings, because JSON numbers cannot
hold them exactly — parse them with a big-integer type, not Number. |
|---|---|
| Times | ISO 8601, always UTC, always with a Z. |
| Hashrate | H/s, and always an estimate — see below. |
| Errors | Always JSON, never an HTML page. error is a stable code for
your code to branch on, message is a sentence for a person. Codes:
not_found, bad_parameter, method_not_allowed,
rate_limited, unavailable.Two of them are produced by the web server in front of the API rather than by the API itself — rate_limited, and not_found for a path under
/api/pool/. Those carry error and message but
no generatedAt: nginx can only stamp local time with an offset,
and a timestamp that broke the UTC rule above would be worse than none. Branch on
error and the difference does not reach your code. |
| Rate limit | 2 requests per second, with a burst of 20. You may
spend 20 at once and then earn 2 a second back. Counted per IPv4 address, or per
/64 on IPv6 — a whole subscriber prefix shares one budget, because a single
IPv6 address is free to rotate. Over the limit you get 429 with
Retry-After: 1 and a JSON body. The Cache-Control on each endpoint says
how often it is worth asking; polling faster returns the same bytes. |
| User-Agent | Please send one that identifies your tool and a way to reach you, for
example bdag-dashboard/1.2 (+https://example.com). If something starts costing the
server real work, being able to write to you is better for both of us than the alternative,
which is blocking an address and leaving you guessing. |
When something goes wrong
Errors are JSON with the same two fields, whether they come from the API or from nginx refusing
the request in front of it. Branch on error; show message to a person.
A bad parameter — HTTP 400:
{
"error": "bad_parameter",
"message": "range must be one of 1h, 24h, 7d."
}Over the rate limit — HTTP 429, with Retry-After:
{
"error": "rate_limited",
"message": "Too many requests. Slow down and try again."
}When a number is an estimate, the field says so
Anything estimated is an object, not a bare number, so you cannot quote it without seeing that it is one:
"current": {
"value": 1704916.22,
"unit": "H/s",
"estimated": true,
"overSeconds": 639
}Hashrate is the estimate you will meet most. It is accepted shares multiplied by the difficulty
assigned to each connection, times 65,536 — derived from shares, not a count of hashes. Over a
short window it is noisy; over 24 hours it settles. The method field says this in the
response itself.
An estimate over a window is only given while the data behind it is newer than the window. If the
newest data is older — the pool or its measurement has stopped — the object carries
"stale": true and "value": null, never the last known figure presented
as current. dataAgeSeconds says how old the data is.
The endpoints
/api/v1the index, machine-readable/api/v1/healthanswering, and how stale/api/v1/poolpolicy, hashrate, blocks, totals/api/v1/minerswho is mining, pseudonymously/api/v1/miners/{address}one wallet you already know/api/v1/miners/{address}/hashratethat wallet over time/api/v1/hashratehashrate over time/api/v1/payoutswhat the pool has paid/api/v1/blocksblocks found
GET /api/v1cached 1 hour
The index: every endpoint, the conventions above, and what is deliberately absent. Start here if you are exploring; it is machine-readable, so a client can discover the rest.
{
"generatedAt": "2026-09-29T20:24:30.321Z",
"version": "v1",
"documentation": "https://dagcore.net/api.html",
"units": {
"amounts": "Decimal strings in wei, 18 decimals. Strings because JSON numbers cannot hold them exactly.",
"hashrate": "H/s",
"time": "ISO 8601, UTC"
},
"contract": "Fields in v1 are never renamed and never change meaning. A change that would break a reader is published as /api/v2, and v1 keeps running.",
"endpoints": [
{
"path": "/api/v1/health",
"description": "Whether this API is answering and how old its data is.",
"cacheSeconds": 0
},
{
"path": "/api/v1/pool",
"description": "Pool-wide state: policy, hashrate, blocks, totals paid.",
"cacheSeconds": 10
},
"\u2026"
],
"absent": {
"addresses": "This API never publishes miner addresses, in full or in part beyond the last four characters. The chain does \u2014 see reason."
}
}GET /api/v1/healthnever cached
Whether this API is answering and how stale its figures are. For a monitor, not for data.
| Field | Type | Meaning |
|---|---|---|
| status | string | ok; degraded — answering, but the
figures are over two minutes old; down — answering, but the data behind it cannot
be read at all. An endpoint that replies is never unreachable; if it were, you would get
no response to read this in. |
| dataAgeSeconds | number | How old the underlying figures are.
null when nothing could be read. |
| responseMs | number | How long this check itself took. |
{
"generatedAt": "2026-09-29T20:24:31.052Z",
"status": "ok",
"dataAgeSeconds": 0,
"responseMs": 728
}GET /api/v1/poolcached 10 s
The pool in one object: what it charges, how it pays, how to point a miner at it, how fast it is hashing, what it has found and what it has paid.
| Field | Type | Meaning |
|---|---|---|
| scheme | string | How rewards are split. PPLNS. |
| fee.bps | number | The pool fee in basis points — 100 is 1%. Read from the pool's own configuration when you ask, so it cannot drift from what is actually charged. |
| fee.percent | string | The same number as a percentage, for display. |
| stratum[] | array | How to point a miner here: url,
host, port, login (what goes in the username field — the
payout address), password ("x") and workerNames. The pool ignores the
password — any value, an empty one, or none at all is accepted; it is given as a value rather
than null because a config generator that sees null omits the field
and some miners will not start without it. note says both of these in the response
itself. workerNames is false: this
pool rejects address.worker at authorization, so a miner configured that way fails
to connect with nothing on screen saying why. Put the address on its own. |
| payout.windowSeconds | number | How often the pool settles. |
| payout.minimumWei | string | Below this, a miner's share is carried to the next settlement instead of being sent. |
| payout.confirmations | number | Confirmations required before a block's reward is treated as final. |
| hashrate.current | estimate | Recent pool hashrate (10 minutes). overSeconds
says how long a window it came from. stale: true and value: null when the
newest data is older than that window. |
| hashrate.last24h | estimate | The same over a day — steadier, and the one to quote. Stale, and null, only once the data is more than a day old. |
| hashrate.dataReadThrough | time | When the share data behind these figures was last read. |
| hashrate.dataAgeSeconds | number | How old that reading is now. |
| miners.active | number / null | Miners that sent a share within
activeWithinMinutes. null when the data is older than that, because the
count would describe the past. |
| miners.seen | number | Miners on record at all, including long-idle ones. |
| miners.connections | number | null | Open stratum connections that have logged in with
a wallet, read from the pool when you ask. One miner can hold several. null when the pool does
not answer. |
| blocks.* | numbers | Three different counts. See the section below — they are not meant to agree. |
| blocks.canonicalTotal | number | foundTotal without the blocks that are not on
the canonical chain: found by the pool, then dropped by a reorg, so their reward never existed. This is the
number the site shows as “Blocks found”. |
| blocks.orphaned | object / null | Why the two differ. verified: blocks
checked against the chain, which has no block by the pool's coinbase at their time.
inferredFromMissingRewards: before checkedSince — the oldest block the pool
still holds detail for — an orphan can only be seen as a window where one reward fewer arrived than
blocks were credited, and it is counted that way. Blocks imported from an earlier database cannot be
judged and stay counted. |
| paid.totalWei | string | Everything the pool has paid out across all miners
since recordsSince — not since the pool began, if its records were rebuilt. |
| paid.payoutCount | number | How many individual payments that was. |
| paid.minersPaid | number | How many distinct miners have been paid. |
| recordsSince | time | When the pool's records begin. Totals cover this period, not all of time. |
| lastBlock | object | The most recent block: height, hash, status, when it was
found. null if none is on record. |
{
"generatedAt": "2026-09-29T20:24:31.045Z",
"scheme": "PPLNS",
"fee": {
"bps": 100,
"percent": "1.00",
"source": "sidecar configuration"
},
"stratum": [
{
"url": "stratum+tcp://stratum.dagcore.net:3334",
"host": "stratum.dagcore.net",
"port": 3334,
"login": "payout address",
"password": "x",
"workerNames": false,
"note": "Worker names are not supported: the username must be the payout address on its own. address.worker is rejected at authorization. The password is ignored \u2014 any value, an empty one, or none at all is accepted; it is given here as a value rather than null because a config generator that sees null omits the field and some miners will not start without it."
}
],
"payout": {
"windowSeconds": 300,
"minimumWei": "1000000000000000000",
"confirmations": 12
},
"hashrate": {
"current": {
"value": 61009175.61587336,
"unit": "H/s",
"estimated": true,
"overSeconds": 657,
"stale": false
},
"last24h": {
"value": 42089694.96708507,
"unit": "H/s",
"estimated": true,
"overSeconds": 86457,
"stale": false
},
"dataReadThrough": "2026-09-29T20:23:56.613Z",
"dataAgeSeconds": 34,
"method": "\u2026"
},
"miners": {
"active": 6,
"seen": 19,
"connections": 8,
"activeWithinMinutes": 10
},
"blocks": {
"foundTotal": 3781,
"canonicalTotal": 3592,
"orphaned": {
"verified": 183,
"inferredFromMissingRewards": 6,
"checkedSince": "2026-09-24T08:04:25.000Z"
},
"last24h": 1148,
"perHour": 47.833333333333336,
"perHourOverHours": 24,
"credited": 3597,
"rewardsReceived": 3472,
"pending": 6,
"rewardUnitWei": "162248406320000000000",
"note": "\u2026"
},
"paid": {
"totalWei": "574360773239824966292413",
"payoutCount": 4571,
"minersPaid": 19
},
"recordsSince": "2026-09-16T10:44:31.619Z",
"lastBlock": {
"height": "17906735",
"hash": "1036cfbdf80bc6c428f77f4c2f6ede941d2628861db2ca6c6db0727100000000",
"status": "PENDING",
"foundAt": "2026-09-29T20:24:23.401Z"
}
}GET /api/v1/minerscached 30 s
Who is mining, pseudonymously, ordered by hashrate. No addresses and no amounts — see what is left out.
window picks the span every hashrate figure here is estimated over: 10m,
1h (the default) or 24h; anything else is a 400. The pool figure
and the rows come from the same span, so the rows add up to poolHashrate, and
10m is the same span as hashrate.current in /pool. Shorter is
noisier: the estimate counts accepted shares, and ten minutes holds a sixth of an hour's, so luck moves
it about 2.5 times as much.
| Field | Type | Meaning |
|---|---|---|
| id | string | A stable handle for one miner, 12 hex characters. It stays the same between calls so you can follow a miner as its rank moves or its name changes, and it reveals nothing about the address. It is not an address and cannot be turned into one. |
| window | string | The window this response was computed over: 10m,
1h or 24h, as asked, 1h when not asked.
hashrateWindowMinutes says the same in minutes. |
| rank | number | Position by hashrate over the chosen window, 1 is largest. |
| name | string / null | The name the miner set by signing a message with the key
that owns the address. null when unset. Chosen by the miner — do not treat it as
identity, that is what id is for. |
| addressSuffix | string | The last four characters of the address, so a miner can recognise itself in the list. Four characters collide; do not key on it. |
| hashrate | estimate | That miner's estimated hashrate over
hashrateWindowMinutes. |
| shareOfPool | number | Its fraction of pool hashrate over the same window, 0 to 1. |
| difficulty | number / null | The share difficulty VarDiff assigned this miner,
recovered exactly from the share records. null when it sent no shares in the window.
This is what it was asked to mine against, not what any hash achieved. |
| sharesPerMinute | number / null | Accepted shares per minute over the same window. With VarDiff working, this stays near constant while difficulty moves instead. |
| hardware | object / null | A guess, marked as one.
{ class, inferred: true, basis }. The pool cannot see hardware; this is banded from
estimated hashrate alone: cpu below 300 KH/s, gpu from there to
30 MH/s, asic above. The edges come from measured machines on this algorithm —
one RTX 3080 is about 1.6 MH/s, an eight-card rig about 13 MH/s, a large GPU farm
reaches about 20 MH/s, and the weakest ASIC seen in practice does about 49 MH/s. They
are edges, not facts: a rig of many small cards looks the same as one large one, and a throttled
machine looks like a smaller class. A hint for grouping, never a fact
about someone's setup. |
| status | string | mining, idle or
offline, from how recently a share arrived; unknown when the pool's data
is more than 5 minutes old, because then nobody can tell. |
| lastShareAt | time | When the pool last accepted a share from it. |
| readThrough | time | How far the share sampler has read. Figures describe the
pool up to this moment, not to generatedAt. |
| dataAgeSeconds | number | How old that reading is now. |
| stale | boolean | true when the reading is older than the window
itself (older than 10 minutes for window=10m). Every hashrate is then
null and every status unknown: the API gives no figure rather than an old
one presented as current. |
{
"generatedAt": "2026-09-29T20:24:31.042Z",
"readThrough": "2026-09-29T20:23:56.613Z",
"dataAgeSeconds": 34,
"window": "1h",
"hashrateWindowMinutes": 60,
"stale": false,
"poolHashrate": {
"value": 80325395.49471433,
"unit": "H/s",
"estimated": true,
"overMinutes": 60,
"stale": false
},
"method": "\u2026",
"count": 19,
"miners": [
{
"id": "63f92a89c45e",
"rank": 1,
"name": null,
"addressSuffix": "48a9",
"hashrate": {
"value": 76180074.68991724,
"unit": "H/s",
"estimated": true,
"overMinutes": 60
},
"shareOfPool": 0.9483933968918974,
"difficulty": 14074.517632901137,
"sharesPerMinute": 4.955405453079121,
"hardware": {
"class": "asic",
"inferred": true,
"basis": "estimated hashrate, not detected hardware"
},
"status": "mining",
"lastShareAt": "2026-09-29T20:23:49.299Z"
},
{
"id": "80c50a80865e",
"rank": 2,
"name": "UFO_from_Galactica 3070",
"addressSuffix": "6c15",
"hashrate": {
"value": 1637752.243584098,
"unit": "H/s",
"estimated": true,
"overMinutes": 60
},
"shareOfPool": 0.020388972049217826,
"difficulty": 194.42379386313513,
"sharesPerMinute": 7.712054844195982,
"hardware": {
"class": "gpu",
"inferred": true,
"basis": "estimated hashrate, not detected hardware"
},
"status": "mining",
"lastShareAt": "2026-09-29T20:23:44.619Z"
}
],
"note": "\u2026"
}GET /api/v1/miners/{address}cached 10 s
One wallet, by its address: what the mining page shows when you type the address in, in a shape that will not change. Added 2026-09-29, for dashboards that run next to a miner.
It answers only for an address you already know, one at a time. It carries no address, no
name and no id: the id in /miners exists so that list can
be followed without addresses, and handing it out here would let anyone who collects payout addresses
from the chain turn the whole list back into addresses. 404 unknown_miner when the pool has
no shares, balance or payouts for the address; 400 bad_parameter when it is not
0x and 40 hex characters, or its mixed-case checksum is wrong (all lower case always works).
| Field | Type | Meaning |
|---|---|---|
| hashrate.10m / 1h / 24h | estimate | The wallet's estimated hashrate over the last 10
minutes, hour and day, with overSeconds. stale: true and value
null when the pool's data is older than that window, as in /miners. |
| hashrate.dataReadThrough, dataAgeSeconds | time, number | How far the share sampler has read, and how old that reading is now. |
| shares.10m / 1h / 24h | object | { accepted, stale, overSeconds, dataStale },
counted as the pool counts them. accepted: shares credited, including those that arrived
within the 500 ms after their job was replaced. stale: shares rejected because their
job had been replaced longer ago, or was no longer known. null when stale counting began
after the window started (unknown is not none). Duplicate, low-difficulty and malformed shares are in
neither. |
| shares.staleCountedSince | time | When the pool began counting stale shares. A
window that starts earlier has stale: null. |
| lastShareAt | time | When the pool last accepted a share from this wallet. |
| lastActiveAt | time / null | The later of the wallet's last authorization and the last minute in which it sent shares, to the minute. Not the time of the last authorization alone: the pool refreshes it every minute while shares arrive. |
| balance.carriedWei | string | Owed and not yet paid: below the payout minimum, carried to the next window. |
| balance.unsentWei | string | Fixed by a closed window and not sent yet (normally 0; it is not 0 while payouts are held or a window is being resumed). |
| balance.openWindow | object / null | The payout window still open:
closesAt; status accumulating (time not up), closing,
waiting (time up, but rewards and credits have not both arrived — waitingFor says
which: credits or reward) or paused (an earlier window or a
deposit hold stops it); inflowWei and feeWei of the whole window so far;
this wallet's weightNum / weightDen in it; share, its estimated part; and
wouldPay, an estimate of whether carried + share would reach the minimum if it closed now.
The window closes on what has arrived by then, so both are estimates. |
| balance.paidTotalWei | string | Everything confirmed paid to this wallet, including payouts from the pool's earlier databases. |
| balance.payoutMinimumWei | string | The threshold below which an amount is carried. |
| payouts | array | The 20 most recent: { at, amountWei, shareNum, shareDen, txHash,
status }. shareNum / shareDen is the payout's part of what its window distributed.
status is pending, confirmed or failed; at
is when it confirmed, else when it was sent, else when it was planned. |
| pplns | object / null | { blockHeight, blockAt, shareNum, shareDen,
minersInWindow, basis }: this wallet's part of the PPLNS window of the most recently
credited block, as the fraction shareNum / shareDen (strings, exact).
Blocks are credited when they mature, a minute or two after they are found. |
{
"generatedAt": "2026-09-29T20:24:31.115Z",
"hashrate": {
"10m": {
"value": 57654079.94708803,
"unit": "H/s",
"estimated": true,
"overSeconds": 657,
"stale": false
},
"1h": {
"value": 76180074.68991725,
"unit": "H/s",
"estimated": true,
"overSeconds": 3657,
"stale": false
},
"24h": {
"value": 34784400.910892405,
"unit": "H/s",
"estimated": true,
"overSeconds": 86457,
"stale": false
},
"dataReadThrough": "2026-09-29T20:23:56.613Z",
"dataAgeSeconds": 35,
"method": "\u2026"
},
"shares": {
"10m": {
"accepted": 46,
"stale": 0,
"overSeconds": 657,
"dataStale": false
},
"1h": {
"accepted": 302,
"stale": 0,
"overSeconds": 3657,
"dataStale": false
},
"24h": {
"accepted": 3495,
"stale": 0,
"overSeconds": 86457,
"dataStale": false
},
"staleCountedSince": "2026-09-27T14:28:00.000Z"
},
"lastShareAt": "2026-09-29T20:23:49.299Z",
"lastActiveAt": "2026-09-29T20:24:07.531Z",
"balance": {
"carriedWei": "\u2026",
"unsentWei": "\u2026",
"openWindow": {
"closesAt": "2026-09-29T20:26:16.470Z",
"status": "accumulating",
"waitingFor": null,
"inflowWei": "1293984490064000147000",
"feeWei": "12939844900640001470",
"weightNum": "1333729446756043740359",
"weightDen": "1372525559103599999979",
"share": {
"value": "\u2026",
"unit": "wei",
"estimated": true
},
"wouldPay": {
"value": true,
"estimated": true
}
},
"paidTotalWei": "\u2026",
"payoutMinimumWei": "1000000000000000000"
},
"payouts": [
{
"at": "\u2026",
"amountWei": "\u2026",
"shareNum": "\u2026",
"shareDen": "\u2026",
"txHash": "\u2026",
"status": "confirmed"
},
"\u2026"
],
"pplns": {
"blockHeight": 17906663,
"blockAt": "2026-09-29T20:23:29.055Z",
"shareNum": "221945883217393035365",
"shareDen": "228754259850599999998",
"minersInWindow": 6,
"basis": "the most recently credited block"
}
}GET /api/v1/miners/{address}/hashratecached 30 s
The same wallet over time: the chart on the mining page. range is 1h,
24h (the default) or 7d, in steps of 120, 1200 and 7200 seconds
(stepSeconds); anything else is a 400. Same errors as
/miners/{address}.
| Field | Type | Meaning |
|---|---|---|
| points[].t | time | Start of the interval. |
| points[].hashrate | number / null | Estimated hashrate over the interval (the
response says estimated: true once, for all of them). |
| points[].accepted | number / null | Shares accepted in the interval, counted as in
/miners/{address}. |
| points[].stale | number / null | Shares rejected as stale in the interval;
null before staleCountedSince. |
| points[].partial | boolean | true for the interval still in progress:
its figures are over the part that has passed. |
| coverageStart, staleCountedSince | time | Where the share records, and the stale
counts, begin. Points before them have null, which means no data, not zero. |
| readThrough, dataAgeSeconds | time, number | How far the data goes. Points after it
are null, and so is an interval in progress until half of it has passed. |
{
"generatedAt": "2026-09-29T20:24:31.130Z",
"range": "1h",
"stepSeconds": 120,
"estimated": true,
"coverageStart": "2026-09-16T12:58:44.265Z",
"staleCountedSince": "2026-09-27T14:28:00.000Z",
"readThrough": "2026-09-29T20:23:56.613Z",
"dataAgeSeconds": 35,
"method": "\u2026",
"points": [
{
"t": "2026-09-29T19:26:00.000Z",
"hashrate": 71802519.63942116,
"accepted": 10,
"stale": 0,
"partial": false
},
{
"t": "2026-09-29T19:28:00.000Z",
"hashrate": 129244535.3509581,
"accepted": 18,
"stale": 0,
"partial": false
},
"\u2026"
]
}GET /api/v1/hashratecached 30 s
Pool hashrate over time. range is 1h, 24h (default) or
7d; anything else is a 400.
| Field | Type | Meaning |
|---|---|---|
| estimated | boolean | Always true, for the reason in method. |
| points[].t | time | Start of the bucket. |
| points[].hashrate | number | Estimated H/s for that bucket. A gap means no samples, which is not the same as zero hashrate. |
| readThrough | time | When the share data was last read. The points run to the
present; every bucket after this moment is null, so a pool that stopped reporting shows
as a gap at the end rather than a line that simply ends early. |
| dataAgeSeconds | number | How old that reading is now. |
{
"generatedAt": "2026-09-29T20:24:31.073Z",
"range": "24h",
"estimated": true,
"readThrough": "2026-09-29T20:23:56.613Z",
"dataAgeSeconds": 34,
"method": "\u2026",
"points": [
{
"t": "2026-09-28T20:40:00.000Z",
"hashrate": 0
},
{
"t": "2026-09-28T21:00:00.000Z",
"hashrate": 0
},
"\u2026"
]
}GET /api/v1/payoutscached 60 s
What the pool has paid, aggregated across every miner. Per-miner amounts are not published.
| Field | Type | Meaning |
|---|---|---|
| totals.paidWei | string | Everything paid since recordsSince. |
| hourly[].t | time | Start of the hour, UTC. |
| hourly[].wei | string | Paid during that hour. |
| hourly[].payouts | number | How many payments that was. |
| hourly[].partial | boolean | True for the hour still in progress. Charting it next to finished hours draws a drop that is not real — either leave it out or mark it. |
{
"generatedAt": "2026-09-29T20:24:31.080Z",
"recordsSince": "2026-09-16T10:44:31.619Z",
"totals": {
"paidWei": "574360773239824966292413",
"payoutCount": 4571,
"minersPaid": 19
},
"hourly": [
{
"t": "2026-09-28T21:00:00.000Z",
"wei": "0",
"payouts": 0,
"partial": false
},
{
"t": "2026-09-28T22:00:00.000Z",
"wei": "0",
"payouts": 0,
"partial": false
},
"\u2026"
],
"note": "\u2026"
}GET /api/v1/blockscached 30 s
Blocks the pool found. limit is 1–200, default 50.
| Field | Type | Meaning |
|---|---|---|
| foundTotal | number | Every block on record. |
| detailAvailableFor | number | How many the pool still holds height and reward
for. It keeps them only while a block is being settled, so this is far smaller than
foundTotal, and the list is a recent tail, not the history. Summing
it does not give what the pool has earned. |
| returned | number | How many this call returned, after limit. |
| blocks[].rewardWei | string | The nominal reward the pool credits for
this block — not what arrives in the wallet. The two differ substantially: this figure
is around 231.8 BDAG while what actually lands is blocks.rewardUnitWei in
/api/v1/pool, about 162.8. Do not multiply this by a block
count to get earnings. |
| blocks[].feesWei | string | The pool's own fee on this block, not
transaction fees. It is exactly rewardWei × the fee in
/api/v1/pool — 1% today — and it is already included in rewardWei, not
additional to it. The name is kept because renaming a field would break v1; what it means is
this. Transaction fees are not recorded separately: the pool stores one reward figure per block,
with whatever fees the block carried folded into it. |
| blocks[].status | string | Where the block is in confirmation, as the pool
records it — for example MATURE. |
| blocks[].canonical | boolean / null | Whether the block is on the canonical chain.
false: dropped by a reorg — the pool still calls it MATURE, but no reward came
from it. null: not checked yet, which is the case for a block under two minutes old or while
our node is behind. Checked again for an hour after it is found, because a later reorg can still drop
it. |
{
"generatedAt": "2026-09-29T20:24:31.089Z",
"foundTotal": 3781,
"detailAvailableFor": 1148,
"returned": 2,
"blocks": [
{
"height": "17906735",
"hash": "1036cfbdf80bc6c428f77f4c2f6ede941d2628861db2ca6c6db0727100000000",
"rewardWei": "231064908940000000000",
"feesWei": "2310649089400000000",
"status": "PENDING",
"foundAt": "2026-09-29T20:24:23.401Z",
"canonical": null
},
"\u2026"
],
"note": "\u2026"
}Three counts of blocks, and why they differ
They look like they should be the same number and they are not. None of them is wrong.
| foundTotal | Every block on record, including ones imported from the pool's earlier database when it was rebuilt. The long-run count. | |
| credited | Blocks the pool was credited for inside its payout accounting — what the miners' shares were divided against. It covers the period the current accounting has been running, so it is smaller. | |
| rewardsReceived | Rewards that actually arrived in the pool wallet. When a
credited block's reward never lands, this is lower than credited. The gap is a real
thing that happened, not a rounding error, and it is published rather than smoothed away. | |
| rewardUnitWei | What one block's reward is worth when it lands, measured
from the wallet. Well below the rewardWei printed on a block in
/api/v1/blocks, which is the nominal figure. To estimate
what the pool earned, multiply this, not that. |
What is deliberately missing
- Miner addresses. This API never publishes them, in full or in
part beyond the last four characters — the chain does, as the note below says. Use
idto follow a miner here. - Per-miner earnings, balances and payout history as a list. They are given for one
address at a time, to whoever already knows it (
/miners/{address}, since 2026-09-29) — never for every miner at once, and never beside a name or anid. - Peak hashrate. Hashrate here is an estimate, and the peak of a noisy estimate is mostly noise: it reports the luckiest minute, not the machine. Averages over a stated window are published instead.
- Per-miner connection counts. The pool's own counter is pool-wide, and the per-miner view reconstructed from its log over-counts connections that were never seen closing. Publishing it would mean publishing a number known to be wrong.
- The miner list with addresses. The list on this site is built from
/api/v1/miners, and the endpoint that carried addresses and totals is no longer reachable from outside.
Not because payments are private — they are not. This pool pays from a wallet whose address it publishes, so every payment it has ever made, to whom and for how much, is already on the chain. Anyone can open that wallet in the explorer and read the lot. Nothing here is hidden that is not a click away, and the pool does not pretend otherwise.
What this API declines to do is assemble it for you: one request returning every
miner's address beside their running total is a different object from the same facts scattered
across a chain, and it is the convenient form that gets saved, sorted and reused. The per-address
lookup on the mining page still works, and so does
/miners/{address} — both need the address, which is the point.
And to be straight about the names: a name is public because a miner chose to publish it, and it sits next to four characters of their address. That is enough to connect a name to an address, given the payments on the chain. The name form says so before anyone signs. This API does not publish the pairing, but it would be dishonest to call it unlinkable.
Fair use
No key, no quota, no sign-up. Honour Cache-Control — asking more often than an
endpoint changes gets you the same bytes and nothing else. If you are building something that needs
more than the rate limit allows, or you want a field that is not here, say so on the
contact page; it is easier to add a field than to have you scrape a
page.