Repository navigation
Expand file tree
/
Copy pathapi.html
More file actions
462 lines (446 loc) · 50.2 KB
/
Copy pathapi.html
File metadata and controls
462 lines (446 loc) · 50.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Monitoring API — Your mining data, connected | DMND</title>
<meta name="description" content="Connect your monitoring tools to DMND. A practical guide to API keys, hashrate, workers, earnings, and confirmed payout history, with examples to get you started.">
<meta property="og:title" content="DMND Monitoring API — Your mining data, connected">
<meta property="og:description" content="Monitor your hashrate, workers, earnings, and payouts with DMND's read-only API. Get started with clear explanations and copyable examples.">
<meta property="og:type" content="website">
<meta property="og:url" content="https://www.dmnd.work/api.html">
<meta property="og:image" content="https://www.dmnd.work/assets/img/og-image.png">
<meta name="twitter:card" content="summary_large_image">
<link rel="canonical" href="https://www.dmnd.work/api.html">
<link rel="icon" href="assets/img/favicon-48x48.png">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
<link rel="stylesheet" href="assets/css/api.css">
<script src="assets/js/api.js" defer></script>
</head>
<body>
<a class="skip-link" href="#quick-start">Skip to documentation</a>
<nav class="top" aria-label="Main navigation">
<div class="top-inner">
<a href="/" class="brand" aria-label="DMND home">DMND<span>.</span></a>
<div class="nav-links">
<a href="/" class="nav-secondary">Home</a>
<a href="slice.html" class="nav-secondary">SLICE</a>
<a href="api.html" aria-current="page">API Docs</a>
<a href="https://blog.dmnd.work/" class="nav-secondary">Blog</a>
<a href="https://dashboard.dmnd.work" class="nav-cta">Dashboard ↗</a>
</div>
</div>
</nav>
<header class="hero">
<div class="eyebrow">MONITORING API · V1</div>
<h1>Your mining data,<br>connected.</h1>
<p class="subtitle">Bring your DMND hashrate, workers, earnings, and payouts into the tools you already use. Here’s how to get started.</p>
<div class="hero-actions">
<a href="#quick-start" class="button primary">View request example →</a>
<a href="#endpoints" class="button">Explore the endpoints</a>
</div>
<div class="hero-meta"><span>Read-only access</span><span>JSON responses</span><span>HTTPS</span></div>
</header>
<main class="layout">
<aside class="toc" aria-label="Documentation contents">
<div class="toc-label">On this page</div>
<nav aria-label="On this page">
<ul>
<li><a href="#quick-start">Quick start</a></li>
<li><a href="#authentication">API keys & permissions</a></li>
<li><a href="#endpoints">Choose an endpoint</a></li>
<li><a href="#hashrate">Hashrate</a></li>
<li><a href="#workers">Workers</a></li>
<li><a href="#shares-fees">Shares & fees</a></li>
<li><a href="#earnings">Estimated earnings</a></li>
<li><a href="#payouts">Confirmed payouts</a></li>
<li><a href="#subaccounts">Subaccounts</a></li>
<li><a href="#polling">Polling & limits</a></li>
<li><a href="#errors">Errors & troubleshooting</a></li>
<li><a href="#reading-data">Reading the values</a></li>
</ul>
</nav>
<div class="toc-note">Need an API key?<a href="https://dashboard.dmnd.work">Open your dashboard ↗</a></div>
</aside>
<details class="mobile-toc">
<summary>On this page</summary>
<nav aria-label="Mobile documentation contents">
<a href="#quick-start">Quick start</a><a href="#authentication">API keys & permissions</a>
<a href="#endpoints">Endpoints</a><a href="#hashrate">Hashrate</a>
<a href="#workers">Workers</a><a href="#shares-fees">Shares & fees</a>
<a href="#earnings">Estimated earnings</a><a href="#payouts">Confirmed payouts</a>
<a href="#subaccounts">Subaccounts</a><a href="#polling">Polling & limits</a>
<a href="#errors">Errors & troubleshooting</a><a href="#reading-data">Reading the values</a>
</nav>
</details>
<article aria-label="Monitoring API documentation">
<!-- Source: demand-user-dashboard-server/monitoring-api.md. Plain-language presentation; API facts, limits, and caveats are sourced only from that document. -->
<section id="quick-start" aria-labelledby="quick-start-title">
<h2 id="quick-start-title">Example API request</h2>
<p>The monitoring API lets your software read your mining activity automatically: account hashrate, worker status, share statistics, fees, estimated earnings, and confirmed payout-output history. Access is read-only.</p>
<div class="base-url">
<div><small>Base URL · the start of every request</small><code id="base-url">https://api.dmnd.work/api/v1</code></div>
<button class="copy-button" type="button" data-copy="base-url" aria-label="Copy base URL" hidden>Copy</button>
</div>
<p>This Python example reads the latest account hashrate. It uses the following credentials and permission:</p>
<ul class="example-requirements">
<li><strong>Monitoring token.</strong> A dedicated token created in the <a href="https://dashboard.dmnd.work">dashboard’s API Keys page</a> for the exact account being monitored.</li>
<li><strong>Required permission.</strong> The token needs the <code>hashrate_read</code> scope.</li>
<li><strong>Secret storage.</strong> The token is stored in the monitoring system’s secret store and supplied through the secret manager as <code>DEMAND_MONITORING_TOKEN</code>.</li>
</ul>
<div class="code-block">
<div class="code-header"><span>Python · no extra packages needed</span><button class="copy-button" type="button" data-copy="quickstart-python" aria-label="Copy Python example" hidden>Copy</button></div>
<pre tabindex="0" aria-label="Python request example"><code class="language-python" id="quickstart-python"><span class="syntax-keyword">import</span> <span class="syntax-variable">json</span>
<span class="syntax-keyword">import</span> <span class="syntax-variable">os</span>
<span class="syntax-keyword">import</span> <span class="syntax-variable">urllib</span><span class="syntax-punctuation">.</span><span class="syntax-variable">request</span>
<span class="syntax-variable">request</span> <span class="syntax-punctuation">=</span> <span class="syntax-variable">urllib</span><span class="syntax-punctuation">.</span><span class="syntax-variable">request</span><span class="syntax-punctuation">.</span><span class="syntax-type">Request</span><span class="syntax-punctuation">(</span>
<span class="syntax-string">"https://api.dmnd.work/api/v1/user/hashrate"</span><span class="syntax-punctuation">,</span>
<span class="syntax-variable">headers</span><span class="syntax-punctuation">=</span><span class="syntax-punctuation">{</span>
<span class="syntax-string">"Authorization"</span><span class="syntax-punctuation">:</span> <span class="syntax-string">"Bearer "</span> <span class="syntax-punctuation">+</span> <span class="syntax-variable">os</span><span class="syntax-punctuation">.</span><span class="syntax-variable">environ</span><span class="syntax-punctuation">[</span><span class="syntax-string">"DEMAND_MONITORING_TOKEN"</span><span class="syntax-punctuation">]</span><span class="syntax-punctuation">,</span>
<span class="syntax-string">"Accept"</span><span class="syntax-punctuation">:</span> <span class="syntax-string">"application/json"</span><span class="syntax-punctuation">,</span>
<span class="syntax-punctuation">}</span><span class="syntax-punctuation">,</span>
<span class="syntax-punctuation">)</span>
<span class="syntax-keyword">with</span> <span class="syntax-variable">urllib</span><span class="syntax-punctuation">.</span><span class="syntax-variable">request</span><span class="syntax-punctuation">.</span><span class="syntax-function">urlopen</span><span class="syntax-punctuation">(</span><span class="syntax-variable">request</span><span class="syntax-punctuation">,</span> <span class="syntax-variable">timeout</span><span class="syntax-punctuation">=</span><span class="syntax-number">20</span><span class="syntax-punctuation">)</span> <span class="syntax-keyword">as</span> <span class="syntax-variable">response</span><span class="syntax-punctuation">:</span>
<span class="syntax-variable">snapshot</span> <span class="syntax-punctuation">=</span> <span class="syntax-variable">json</span><span class="syntax-punctuation">.</span><span class="syntax-function">load</span><span class="syntax-punctuation">(</span><span class="syntax-variable">response</span><span class="syntax-punctuation">)</span>
<span class="syntax-function">print</span><span class="syntax-punctuation">(</span><span class="syntax-variable">snapshot</span><span class="syntax-punctuation">)</span></code></pre>
</div>
<p class="small">This example makes one request, sets a timeout, and prints the decoded response without logging the token. It does not retry. Before scheduling it to run repeatedly, add <a href="#errors">error handling</a> and follow the <a href="#polling">polling guidance</a>.</p>
<div class="callout tip"><p><strong>What you’ll get back:</strong> your account ID, the snapshot’s <code>observed_at</code> timestamp, and separate PPLNS, FPPS, and total rates. <a href="#hashrate">See the example response →</a></p></div>
</section>
<section id="authentication" aria-labelledby="authentication-title">
<h2 id="authentication-title">API keys & permissions</h2>
<p>A monitoring token is a secret that tells DMND which account and data your tool may access. Send it in the <code>Authorization</code> header on every request.</p>
<div class="code-block">
<div class="code-header"><span>Request header · replace the placeholder in your client</span></div>
<pre tabindex="0" aria-label="Authorization header"><code class="language-http"><span class="syntax-property">Authorization</span><span class="syntax-punctuation">:</span> <span class="syntax-keyword">Bearer</span> <span class="syntax-string"><token></span></code></pre>
</div>
<p>Create a dedicated monitoring key in the dashboard’s <strong>API Keys</strong> page, selecting the exact account and only the required scopes. Mining tokens and FPPS credentials won’t work here. Programmatic clients should send no session cookies; if both a session and a bearer token are supplied, the bearer token decides the account and permissions.</p>
<div class="table-wrap" role="region" aria-label="API key permissions" tabindex="0">
<table>
<thead><tr><th scope="col">Permission (scope)</th><th scope="col">What it lets you read</th></tr></thead>
<tbody>
<tr><td><code>hashrate_read</code></td><td>Current and historical PPLNS, FPPS, and total hashrate.</td></tr>
<tr><td><code>workers_read</code></td><td>Worker names, online status, hashrates, and accepted/rejected counters.</td></tr>
<tr><td><code>rejects_read</code></td><td>Accepted and rejected share counter changes over a time window.</td></tr>
<tr><td><code>fees_read</code></td><td>Pool and broker fee rates.</td></tr>
<tr><td><code>earnings_read</code></td><td>Generated FPPS BTC, projected PPLNS BTC, and confirmed payout history for recorded addresses.</td></tr>
</tbody>
</table>
</div>
<div class="callout"><p><strong>Keep the token private.</strong> Give each key only the permissions your tool needs. For new integrations, keep tokens out of request URLs: <code>/api/v1</code> rejects query-string credentials. The legacy exception is explained below. Worker names can identify sites, and earnings can reveal business activity. Anyone with a watcher link can access the data it exposes, so treat the entire link as a secret. Don’t include it in support tickets or third-party link previews. Keep bearer headers, API secrets, watcher links, and responses that create or list credentials out of request logs, traces, and alerts.</p></div>
<details>
<summary>Expiry, replacement keys, and older integrations</summary>
<div class="details-body">
<p><strong>Tokens have no expiration by default.</strong> An owner can choose an explicit future expiry. Check the listed expiry for each key, since existing keys retain their stored dates. Each target account may have at most <strong>100 active tokens</strong>.</p>
<p>Revoking a token prevents authentication on subsequent requests; requests already authenticated may finish. If a key expires or must be rotated, create a replacement, update the monitoring secret, check a successful read, and then revoke the old key.</p>
<p>Token creation and revocation use the signed-in dashboard, outside <code>/api/v1</code>.</p>
<p>The signed-in owner can retrieve available secrets and open or copy watcher links from API Keys. Only active dedicated monitoring-key records authenticate, including preserved historical keys.</p>
<p>The dashboard’s unversioned <code>/api</code> routes support existing watcher workflows. Existing unversioned monitoring GET URLs may use a single <code>token</code> query parameter for compatibility. Don’t use that pattern for new integrations: URLs can leak through logs and link previews. Combining a query credential with an Authorization header, supplying duplicate query credentials, or sending any query credential to <code>/api/v1</code> is rejected.</p>
</div>
</details>
</section>
<section id="endpoints" aria-labelledby="endpoints-title">
<h2 id="endpoints-title">Choose what you want to read</h2>
<p>Each path below is relative to the base URL. All endpoints use <code>GET</code> to read the data described in this reference.</p>
<div class="table-wrap" role="region" aria-label="Account endpoints" tabindex="0">
<table>
<caption>Account endpoints · all paths start after <code>/api/v1</code></caption>
<thead><tr><th scope="col">I want to…</th><th scope="col">GET path</th><th scope="col">Permission</th></tr></thead>
<tbody>
<tr><td><a href="#hashrate">Check current hashrate</a></td><td><code>/user/hashrate</code></td><td><code>hashrate_read</code></td></tr>
<tr><td><a href="#hashrate-history">Chart past hashrate</a></td><td><code>/user/hashrate/historical</code></td><td><code>hashrate_read</code></td></tr>
<tr><td><a href="#workers">List live workers</a></td><td><code>/workers</code></td><td><code>workers_read</code></td></tr>
<tr><td><a href="#workers">Include offline workers</a></td><td><code>/workers/all</code></td><td><code>workers_read</code></td></tr>
<tr><td><a href="#shares-fees">Check accepted / rejected shares</a></td><td><code>/user/share_stats</code></td><td><code>rejects_read</code></td></tr>
<tr><td><a href="#shares-fees">Look up my fee rates</a></td><td><code>/user/fees</code></td><td><code>fees_read</code></td></tr>
<tr><td><a href="#earnings">Read daily estimated earnings</a></td><td><code>/user/generated_btc</code></td><td><code>earnings_read</code></td></tr>
<tr><td><a href="#payouts">Read confirmed payout outputs</a></td><td><code>/user/payouts</code></td><td><code>earnings_read</code></td></tr>
</tbody>
</table>
</div>
<p class="small">These routes read the token’s account. <strong>Earnings and payouts also include directly owned subaccounts.</strong> To select one exact account, use the <a href="#subaccounts">subaccount routes</a>.</p>
<p class="small">Detailed PPLNS projections and the combined dashboard summary are dashboard interfaces outside the versioned monitoring contract. Mining telemetry, payment initiation, registration, and session management are also outside this API.</p>
</section>
<section id="hashrate" aria-labelledby="hashrate-title">
<h2 id="hashrate-title">Hashrate</h2>
<div class="endpoint"><span class="method">GET</span><code>/user/hashrate</code></div>
<p class="scope">Requires <a href="#authentication"><code>hashrate_read</code></a></p>
<p>Read the latest account snapshot, with separate PPLNS, FPPS, and total rates. This endpoint returns all three in raw <strong>hashes per second (H/s)</strong>.</p>
<div class="code-block">
<div class="code-header"><span>Example response</span><button class="copy-button" type="button" data-copy="hashrate-json" aria-label="Copy hashrate response" hidden>Copy</button></div>
<pre tabindex="0" aria-label="Hashrate response"><code class="language-json" id="hashrate-json"><span class="syntax-punctuation">{</span>
<span class="syntax-property">"account_id"</span><span class="syntax-punctuation">:</span> <span class="syntax-string">"9223372036854775807"</span><span class="syntax-punctuation">,</span>
<span class="syntax-property">"observed_at"</span><span class="syntax-punctuation">:</span> <span class="syntax-string">"2026-09-08T10:00:00Z"</span><span class="syntax-punctuation">,</span>
<span class="syntax-property">"pplns_hashrate"</span><span class="syntax-punctuation">:</span> <span class="syntax-number">150000000000000</span><span class="syntax-punctuation">,</span>
<span class="syntax-property">"fpps_hashrate"</span><span class="syntax-punctuation">:</span> <span class="syntax-number">50000000000000</span><span class="syntax-punctuation">,</span>
<span class="syntax-property">"total_hashrate"</span><span class="syntax-punctuation">:</span> <span class="syntax-number">200000000000000</span>
<span class="syntax-punctuation">}</span></code></pre>
</div>
<p>Snapshots are normally saved <strong>every two minutes</strong> using fresh worker telemetry. A worker counts as online while its latest telemetry is less than ten minutes old. These are periodic snapshots, so they may differ from an instantaneous miner reading.</p>
<div class="callout tip"><p><strong>Check the timestamp as well as the rate.</strong> Use <code>observed_at</code> to detect stale collection separately from low hashrate. If there is no snapshot yet, <code>observed_at</code> is <code>null</code> and all rates are zero.</p></div>
<h3 id="hashrate-history">Looking back in time</h3>
<div class="endpoint"><span class="method">GET</span><code>/user/hashrate/historical</code></div>
<p>This returns an array of snapshots in ascending <code>observed_at</code> order—oldest first. Supply <code>from</code> and <code>to</code> as RFC3339 timestamps or <code>YYYY-MM-DD</code> dates. Omitted bounds default to the last 24 hours.</p>
<div class="table-wrap" role="region" aria-label="Historical hashrate parameters" tabindex="0">
<table>
<thead><tr><th scope="col">Parameter</th><th scope="col">How to use it</th></tr></thead>
<tbody>
<tr><td><code>from</code></td><td>Start timestamp (RFC3339) or UTC date, such as <code>2026-09-01</code>. A date alone starts at 00:00:00 UTC.</td></tr>
<tr><td><code>to</code></td><td>End timestamp or UTC date. A date alone ends at 23:59:59 UTC.</td></tr>
</tbody>
</table>
</div>
<p>Each request covers at most <strong>seven days</strong> and returns at most <strong>6,000 points</strong>. Approximately 100 days are retained. To collect a longer history, request successive windows up to seven days each and remove duplicate boundary timestamps. Fix the range before your first request so its end does not move between pages. An empty array means there are no snapshots in that window.</p>
</section>
<section id="workers" aria-labelledby="workers-title">
<h2 id="workers-title">Workers</h2>
<div class="endpoint"><span class="method">GET</span><code>/workers</code></div>
<div class="endpoint"><span class="method">GET</span><code>/workers/all</code></div>
<p class="scope">Requires <a href="#authentication"><code>workers_read</code></a></p>
<p>Use <code>/workers</code> for live workers, or <code>/workers/all</code> to include offline workers. Results come in pages of <strong>200 workers by default</strong>; use <code>limit</code> to request up to <strong>1,000</strong>.</p>
<p>Responses have the shape <code>{"workers": [...], "next_cursor": "..."}</code>. Pass <code>next_cursor</code> back unchanged as <code>cursor</code> for the next page; it is opaque, so your client should not interpret it. When it is <code>null</code>, you’re done.</p>
<div class="table-wrap" role="region" aria-label="Worker response fields" tabindex="0">
<table>
<thead><tr><th scope="col">Field</th><th scope="col">What it means</th></tr></thead>
<tbody>
<tr><td><code>name</code></td><td>The worker’s name.</td></tr>
<tr><td><code>is_connected</code></td><td>Whether the worker is online.</td></tr>
<tr><td><code>connected_at</code></td><td>A Unix timestamp in <strong>milliseconds</strong>, or <code>null</code>.</td></tr>
<tr><td><code>hashrate</code>, <code>fpps_hashrate</code></td><td>Rates in raw H/s, including on subaccount worker routes.</td></tr>
<tr><td><code>total_shares</code>, <code>rejected_shares</code></td><td>PPLNS share counters. The equivalent FPPS counter names begin with <code>fpps_</code>.</td></tr>
</tbody>
</table>
</div>
<p class="small">Missing telemetry for a payout mode is <code>null</code>. Worker counters can reset, disconnected miners can affect changes between readings, and the worker list can change while you fetch pages.</p>
</section>
<section id="shares-fees" aria-labelledby="shares-fees-title">
<h2 id="shares-fees-title">Shares & fees</h2>
<h3>Check your rejection rate</h3>
<div class="endpoint"><span class="method">GET</span><code>/user/share_stats?hours=24</code></div>
<p class="scope">Requires <a href="#authentication"><code>rejects_read</code></a></p>
<p>This endpoint reports changes in accepted and rejected share counters over your chosen window. The <code>hours</code> parameter defaults to <strong>24</strong> and can be at most <strong>168</strong> (seven days).</p>
<p>The response includes <code>window_hours</code>, <code>pplns_accepted</code>, <code>pplns_rejected</code>, <code>fpps_accepted</code>, <code>fpps_rejected</code>, <code>accepted</code>, and <code>rejected</code>.</p>
<div class="callout tip"><p><strong>Rejection ratio:</strong> <code>rejected / (accepted + rejected)</code>. Only calculate it when <code>accepted + rejected</code> is not zero.</p></div>
<p class="small">These numbers come from telemetry counter changes, rather than an immutable record of every raw share.</p>
<h3>Read your pool and broker fees</h3>
<div class="endpoint"><span class="method">GET</span><code>/user/fees</code></div>
<p class="scope">Requires <a href="#authentication"><code>fees_read</code></a></p>
<p>The response contains <code>pool_fee</code> and <code>broker_fee</code>. Both are fractions: <strong><code>0.02</code> means 2%</strong>.</p>
</section>
<section id="earnings" aria-labelledby="earnings-title">
<h2 id="earnings-title">Estimated earnings, day by day</h2>
<div class="endpoint"><span class="method">GET</span><code>/user/generated_btc</code></div>
<p class="scope">Requires <a href="#authentication"><code>earnings_read</code></a> · Includes directly owned subaccounts</p>
<p>Read daily generated FPPS BTC alongside projected PPLNS BTC. Results are an array ordered by <code>entry_day</code>, newest first. Every day is measured in UTC.</p>
<div class="callout"><p><strong>Estimated earnings are not confirmed payments.</strong> PPLNS projections can change and exclude amounts already earned from found blocks. The combined total isn’t a paid balance, payout guarantee, or settled accounting record. Use <a href="#payouts">confirmed payout history</a> to see transaction outputs.</p></div>
<div class="table-wrap" role="region" aria-label="Earnings query parameters" tabindex="0">
<table>
<thead><tr><th scope="col">Parameter</th><th scope="col">How to use it</th></tr></thead>
<tbody>
<tr><td><code>date</code></td><td>One UTC day: <code>YYYY-MM-DD</code>. Don’t combine with <code>before</code> or <code>limit</code>.</td></tr>
<tr><td><code>before</code></td><td>The last UTC day to include, inclusive. Defaults to today.</td></tr>
<tr><td><code>limit</code></td><td>Number of <strong>calendar days</strong>, not rows. Defaults to 30; maximum 100.</td></tr>
</tbody>
</table>
</div>
<div class="table-wrap" role="region" aria-label="Daily earnings response fields" tabindex="0">
<table>
<thead><tr><th scope="col">Field</th><th scope="col">What it means</th></tr></thead>
<tbody>
<tr><td><code>entry_day</code></td><td>The work day, formatted <code>YYYY-MM-DD</code> in UTC.</td></tr>
<tr><td><code>fpps_btc_generated</code></td><td>Generated FPPS earnings in BTC.</td></tr>
<tr><td><code>pplns_btc_generated</code></td><td>Cached future PPLNS projected BTC attributed to this work day.</td></tr>
<tr><td><code>btc_generated</code></td><td>The sum of the FPPS and projected PPLNS values.</td></tr>
<tr><td><code>hashrate</code></td><td>The FPPS-derived daily hashrate in H/s; zero for a day with only PPLNS projections.</td></tr>
<tr><td><code>pplns_hashrate</code></td><td>PPLNS hashrate from recorded accepted-share work, in H/s. <code>null</code> means unavailable.</td></tr>
</tbody>
</table>
</div>
<h3>Collecting older earnings</h3>
<p>Use the preceding calendar window to collect older data. Missing mining days are omitted, so a page with missing days does not mean older data is unavailable.</p>
<p>For both account and subaccount routes, a request for one specific <code>date</code> with no earnings returns one zero-earnings row. Its PPLNS hashrate still follows the independent recorded-work availability rules. The FPPS earnings accumulator normally runs every five minutes.</p>
<details>
<summary>Why PPLNS hashrate can be null, and how it is calculated</summary>
<div class="details-body">
<p><strong>Keep <code>null</code> distinct from zero.</strong> A covered day that was read successfully and had no recorded PPLNS work returns <code>pplns_hashrate: 0</code>. <code>null</code> means the rate is unavailable, whether due to age, incomplete coverage, or a temporary refresh problem. Unavailable rates are never reported as zero.</p>
<p>Coverage is limited to today and the previous 99 UTC days, starting no earlier than the first complete UTC day after the database’s explicit-UTC share-timestamp migration. Earlier timestamps cannot establish that a full day’s work was recorded with the correct date. Older earnings rows remain available with <code>pplns_hashrate: null</code>. Recorded work alone does not create an earnings row.</p>
<p>The rate uses accepted shares grouped by the UTC day the database recorded them. Delayed ingestion can place work in a later day; miner-supplied <code>ntime</code> does not set the day. For completed days the calculation is <code>sum(difficulty) * 2^32 / 86400</code>. Today uses elapsed whole seconds since UTC midnight. Rates are rounded to two decimal places.</p>
<p>This is separate from worker telemetry, the difficulty in the moving PPLNS reward window, and the FPPS-derived <code>hashrate</code> field.</p>
<p>For current monitoring, use <code>?date=YYYY-MM-DD</code> with today’s UTC date every two to five minutes. For older data, use smaller windows and wait at least 60 seconds before retrying missing rates.</p>
<p>The supplemental rate is cached for 60 seconds, with up to 128 account/range combinations, one concurrent refresh per server, and three refresh attempts per target account per minute. Ownership is checked before cache lookup. Refreshes have a five-second SQL deadline; large histories can exceed it. Busy refreshes, exhausted budgets, errors, and invalid work values return <code>null</code> without failing or changing the earnings response. Failed refreshes are also cached for 60 seconds.</p>
</div>
</details>
<p class="small">Compatibility note: unversioned dashboard earnings routes under <code>/api</code> return complete available history when these parameters are omitted, so dashboard history and exports do not need pagination. New integrations using <code>/api/v1</code> use the bounded defaults above.</p>
</section>
<section id="payouts" aria-labelledby="payouts-title">
<h2 id="payouts-title">Confirmed payouts</h2>
<div class="endpoint"><span class="method">GET</span><code>/user/payouts</code></div>
<p class="scope">Requires <a href="#authentication"><code>earnings_read</code></a> · Includes directly owned subaccounts</p>
<p>Read confirmed FPPS and PPLNS transaction outputs sent to your account’s recorded Bitcoin addresses, including inactive addresses. Unrelated recipients, transfers back to funding addresses, unrelated deposits, and unconfirmed transactions are excluded. This API only reads history; it never initiates payments or changes payout addresses.</p>
<div class="callout"><p><strong>This is history associated with addresses.</strong> Address records have no effective ownership dates, so even an unshared recorded address does not prove who owned it at the time of a historical payment. This is not an account ownership ledger. If an address is also recorded against an account outside the requested group, the entire request returns <code>409</code> with <code>payouts-ambiguous-address</code>. The API does not guess ownership or silently leave that address out.</p></div>
<div class="table-wrap" role="region" aria-label="Payout query parameters" tabindex="0">
<table>
<thead><tr><th scope="col">Parameter</th><th scope="col">How to use it</th></tr></thead>
<tbody>
<tr><td><code>from</code>, <code>to</code></td><td>Inclusive UTC dates, <code>YYYY-MM-DD</code>. No future dates. <code>to</code> defaults to today; <code>from</code> defaults to 29 days before it. Maximum <strong>30 calendar days</strong>.</td></tr>
<tr><td><code>limit</code></td><td>Outputs per page: 1–100. Defaults to 100.</td></tr>
<tr><td><code>cursor</code></td><td>The previous <code>next_cursor</code>, unchanged. Keep the same explicit dates and target account across pages.</td></tr>
</tbody>
</table>
</div>
<div class="code-block">
<div class="code-header"><span>Example URL · use the same Bearer header</span><button class="copy-button" type="button" data-copy="payouts-url" aria-label="Copy payouts URL" hidden>Copy</button></div>
<pre tabindex="0" aria-label="Payouts request URL"><code class="language-url" id="payouts-url"><span class="syntax-string">https://api.dmnd.work/api/v1/user/payouts</span><span class="syntax-punctuation">?</span><span class="syntax-property">from</span><span class="syntax-punctuation">=</span><span class="syntax-string">2026-09-01</span><span class="syntax-punctuation">&</span><span class="syntax-property">to</span><span class="syntax-punctuation">=</span><span class="syntax-string">2026-09-09</span><span class="syntax-punctuation">&</span><span class="syntax-property">limit</span><span class="syntax-punctuation">=</span><span class="syntax-number">100</span></code></pre>
</div>
<div class="code-block">
<div class="code-header"><span>Example response</span><button class="copy-button" type="button" data-copy="payouts-json" aria-label="Copy payouts response" hidden>Copy</button></div>
<pre tabindex="0" aria-label="Confirmed payout response"><code class="language-json" id="payouts-json"><span class="syntax-punctuation">{</span>
<span class="syntax-property">"payouts"</span><span class="syntax-punctuation">:</span> <span class="syntax-punctuation">[</span>
<span class="syntax-punctuation">{</span>
<span class="syntax-property">"txid"</span><span class="syntax-punctuation">:</span> <span class="syntax-string">"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"</span><span class="syntax-punctuation">,</span>
<span class="syntax-property">"output_index"</span><span class="syntax-punctuation">:</span> <span class="syntax-number">2</span><span class="syntax-punctuation">,</span>
<span class="syntax-property">"kind"</span><span class="syntax-punctuation">:</span> <span class="syntax-string">"fpps"</span><span class="syntax-punctuation">,</span>
<span class="syntax-property">"address"</span><span class="syntax-punctuation">:</span> <span class="syntax-string">"<recorded Bitcoin address>"</span><span class="syntax-punctuation">,</span>
<span class="syntax-property">"amount_sats"</span><span class="syntax-punctuation">:</span> <span class="syntax-number">125000</span><span class="syntax-punctuation">,</span>
<span class="syntax-property">"confirmed_at"</span><span class="syntax-punctuation">:</span> <span class="syntax-number">1788912000</span>
<span class="syntax-punctuation">}</span>
<span class="syntax-punctuation">]</span><span class="syntax-punctuation">,</span>
<span class="syntax-property">"next_cursor"</span><span class="syntax-punctuation">:</span> <span class="syntax-constant">null</span><span class="syntax-punctuation">,</span>
<span class="syntax-property">"as_of"</span><span class="syntax-punctuation">:</span> <span class="syntax-number">1788912300</span><span class="syntax-punctuation">,</span>
<span class="syntax-property">"from"</span><span class="syntax-punctuation">:</span> <span class="syntax-string">"2026-09-01"</span><span class="syntax-punctuation">,</span>
<span class="syntax-property">"to"</span><span class="syntax-punctuation">:</span> <span class="syntax-string">"2026-09-09"</span>
<span class="syntax-punctuation">}</span></code></pre>
</div>
<ul>
<li><code>amount_sats</code> is an integer number of satoshis, never a floating-point BTC amount.</li>
<li><code>txid</code> and the zero-based <code>output_index</code> together identify a unique transaction output. Each pair appears once.</li>
<li><code>kind</code> is <code>fpps</code> or <code>pplns</code>.</li>
<li><code>confirmed_at</code> is the block timestamp in Unix <strong>seconds</strong>, not the payment request time. <code>as_of</code> is when the server retrieved the snapshot, also in seconds.</li>
<li><code>next_cursor: null</code> means the last page. Rows are ordered descending by confirmation timestamp, then transaction ID and output index.</li>
</ul>
<p>An empty page means there are no matching confirmed outputs in a fully retrieved window, or no recorded recipient addresses. A failed or incomplete provider read returns an error, never a successful empty or partial history.</p>
<h3>When a payout snapshot changes</h3>
<p>Bitcoin confirmations can change after a chain reorganization. If you receive <code>409 payouts-cursor-changed</code>, restart without a cursor using the same fixed date window. Compare the replacement results with what you collected, using <code>txid</code> and <code>output_index</code>, so removed or changed outputs are reconciled.</p>
<p class="small">A change to the funding snapshot, requested dates, target account, or recorded recipient addresses invalidates the cursor with <code>409 payouts-cursor-changed</code>. A funding change can affect another recipient, even if your own payouts look unchanged.</p>
<details>
<summary>Payout refresh limits and history coverage</summary>
<div class="details-body">
<p>Poll every few minutes, reuse collected pages, and backfill one fixed window at a time. Set a client timeout that allows for a <strong>20-second provider deadline plus authentication and database work</strong>.</p>
<p>Chain snapshots are cached for at most 60 seconds, with up to four windows per server process. One provider fill runs at a time, with at most six uncached windows per fixed 60-second interval across all customers on that process. These limits are in addition to normal API budgets. On <code>429</code>, follow <code>Retry-After</code>.</p>
<p>Provider reads are bounded to 20 pages per funding source, 1 MiB per upstream response, and 50,000 outputs per window. Older windows can exceed the scan-depth limit even with few payouts, because scans begin at the newest transaction.</p>
<p>Missing configuration, provider failures, timeouts, incomplete scans, or inconsistent data return <code>503</code>. Respect <code>Retry-After</code>. After cache expiry, the service retrieves fresh history and does not serve stale data if that refresh fails.</p>
<p>Funding inputs must match configured FPPS/PPLNS sources or the historical FPPS source <code>bc1qwk7rplr3ztrsvlcxujnxdvp77atpqawlh94wgw</code>. Only outputs to authorized recorded recipient addresses are returned, without duplicates when several inputs match or an address is shared within the requested group.</p>
<p>Payout-history endpoints exist only under <code>/api/v1</code>. Dashboard payout-address discovery and watcher links are separate interfaces.</p>
</div>
</details>
</section>
<section id="subaccounts" aria-labelledby="subaccounts-title">
<h2 id="subaccounts-title">Monitoring subaccounts</h2>
<p>A token can read its own account and directly owned subaccounts, within its selected permissions. A key created for a subaccount cannot read the master account or sibling subaccounts.</p>
<p>To read one exact account, replace <code>{id}</code> in the paths below with that account’s ID. Unlike the main earnings and payout routes, these routes do not include that target’s subaccounts.</p>
<div class="callout"><p><strong>Keep account IDs as strings.</strong> They are decimal strings and can be negative. Preserve the exact value; don’t convert it to a JavaScript number.</p></div>
<div class="table-wrap" role="region" aria-label="Subaccount endpoints" tabindex="0">
<table>
<thead><tr><th scope="col">GET path · exact target only</th><th scope="col">Permission</th></tr></thead>
<tbody>
<tr><td><code>/user/sub_account/{id}/hashrate</code></td><td><code>hashrate_read</code></td></tr>
<tr><td><code>/user/sub_account/{id}/hashrate/historical</code></td><td><code>hashrate_read</code></td></tr>
<tr><td><code>/user/sub_account/{id}/workers</code></td><td><code>workers_read</code></td></tr>
<tr><td><code>/user/sub_account/{id}/share_stats</code></td><td><code>rejects_read</code></td></tr>
<tr><td><code>/user/sub_account/{id}/fees</code></td><td><code>fees_read</code></td></tr>
<tr><td><code>/user/sub_account/{id}/generated_btc</code></td><td><code>earnings_read</code></td></tr>
<tr><td><code>/user/sub_account/{id}/payouts</code></td><td><code>earnings_read</code></td></tr>
</tbody>
</table>
</div>
<h3>One important difference: hashrate units</h3>
<p>Subaccount hashrate snapshots return each rate as a <code>value</code>/<code>unit</code> object, rounded to two decimal places, using TH/s or PH/s. This applies to current and historical hashrate routes. Worker rates still use raw H/s.</p>
<div class="code-block">
<div class="code-header"><span>Example subaccount hashrate response</span><button class="copy-button" type="button" data-copy="subaccount-json" aria-label="Copy subaccount response" hidden>Copy</button></div>
<pre tabindex="0" aria-label="Subaccount hashrate response"><code class="language-json" id="subaccount-json"><span class="syntax-punctuation">{</span>
<span class="syntax-property">"account_id"</span><span class="syntax-punctuation">:</span> <span class="syntax-string">"1234567890123456789"</span><span class="syntax-punctuation">,</span>
<span class="syntax-property">"observed_at"</span><span class="syntax-punctuation">:</span> <span class="syntax-string">"2026-09-08T10:00:00Z"</span><span class="syntax-punctuation">,</span>
<span class="syntax-property">"pplns_hashrate"</span><span class="syntax-punctuation">:</span> <span class="syntax-punctuation">{</span><span class="syntax-property">"value"</span><span class="syntax-punctuation">:</span> <span class="syntax-number">150.0</span><span class="syntax-punctuation">,</span> <span class="syntax-property">"unit"</span><span class="syntax-punctuation">:</span> <span class="syntax-string">"TH/s"</span><span class="syntax-punctuation">}</span><span class="syntax-punctuation">,</span>
<span class="syntax-property">"fpps_hashrate"</span><span class="syntax-punctuation">:</span> <span class="syntax-punctuation">{</span><span class="syntax-property">"value"</span><span class="syntax-punctuation">:</span> <span class="syntax-number">50.0</span><span class="syntax-punctuation">,</span> <span class="syntax-property">"unit"</span><span class="syntax-punctuation">:</span> <span class="syntax-string">"TH/s"</span><span class="syntax-punctuation">}</span><span class="syntax-punctuation">,</span>
<span class="syntax-property">"total_hashrate"</span><span class="syntax-punctuation">:</span> <span class="syntax-punctuation">{</span><span class="syntax-property">"value"</span><span class="syntax-punctuation">:</span> <span class="syntax-number">200.0</span><span class="syntax-punctuation">,</span> <span class="syntax-property">"unit"</span><span class="syntax-punctuation">:</span> <span class="syntax-string">"TH/s"</span><span class="syntax-punctuation">}</span>
<span class="syntax-punctuation">}</span></code></pre>
</div>
<p class="small">Outputs to an address shared entirely within an aggregate account group can appear in that group’s history. Each <code>(txid, output_index)</code> appears once. The corresponding exact-account request is ambiguous and returns <code>409 payouts-ambiguous-address</code> when another recorded owner is outside that exact target.</p>
</section>
<section id="polling" aria-labelledby="polling-title">
<h2 id="polling-title">How often should I check?</h2>
<p><strong>Every two to five minutes</strong> is an appropriate interval for current statistics, matching the snapshot cadence. Check payouts every few minutes, too.</p>
<ul>
<li>Save collected data in your own monitoring store. Fetch only what you need instead of the entire history each time.</li>
<li>Stagger requests across accounts and limit how many run at once.</li>
<li>Set connection and request timeouts. Give payout requests enough time for their provider lookup.</li>
</ul>
<div class="table-wrap" role="region" aria-label="API request limits" tabindex="0">
<table>
<caption>Request budgets · each fixed 60-second window</caption>
<thead><tr><th scope="col">Budget</th><th scope="col">Requests</th><th scope="col">Shared by</th></tr></thead>
<tbody>
<tr><td>Per token</td><td>240</td><td>All requests using that token.</td></tr>
<tr><td>Per token account</td><td>600</td><td>All tokens for the same account.</td></tr>
<tr><td>Per client IP</td><td>1,200</td><td>Requests from that IP across protected API paths.</td></tr>
</tbody>
</table>
</div>
<p class="small">These limits apply per server process, not as a guaranteed cluster-wide allowance. Payout lookups have additional request budgets. Edge protection or temporary capacity limits may also reject traffic. Always obey a <code>429</code>, even if your own request count is lower. The separate limits for supplemental PPLNS-rate refreshes return <code>null</code> when a refresh is unavailable, as described under <a href="#earnings">estimated earnings</a>.</p>
<div class="callout tip"><p><strong>If you’re asked to slow down:</strong> read the <code>Retry-After</code> header on a <code>429</code> response. Wait at least that many seconds, then add a small random delay.</p></div>
<p class="small">Account responses use <code>Cache-Control: private, no-store</code>. Do not record bearer headers, API secrets, watcher links, or responses that create or list credentials in request logs, traces, or alerts.</p>
</section>
<section id="errors" aria-labelledby="errors-title">
<h2 id="errors-title">When a request doesn’t work</h2>
<p>Errors are JSON objects with <code>code</code> and <code>message</code> strings. Use the HTTP status to decide whether to retry. The message is for diagnosis and may change.</p>
<div class="table-wrap" role="region" aria-label="HTTP status troubleshooting" tabindex="0">
<table>
<thead><tr><th scope="col">HTTP status</th><th scope="col">What it means</th><th scope="col">What to do</th></tr></thead>
<tbody>
<tr><td>200</td><td>The request worked.</td><td>Read the result. Empty data can be valid.</td></tr>
<tr><td>400</td><td>Invalid parameters, URL credentials, or an out-of-range limit.</td><td>Fix the request before trying again.</td></tr>
<tr><td>401</td><td>The key is missing, invalid, revoked, or expired.</td><td>Check the bearer header and the key in API Keys. Don’t retry unchanged.</td></tr>
<tr><td>403</td><td>The key lacks a scope or access to the target account.</td><td>Check the selected permissions and account. Don’t retry unchanged.</td></tr>
<tr><td>404</td><td>Unknown endpoint or unavailable resource.</td><td>Check the base URL, path, and requested resource.</td></tr>
<tr><td>409</td><td>Payout address attribution is ambiguous, or the pagination snapshot changed.</td><td>Check the error code and the guidance below.</td></tr>
<tr><td>429</td><td>A request or capacity budget was reached.</td><td>Wait for <code>Retry-After</code> seconds, plus a small random delay.</td></tr>
<tr><td>500 / 503</td><td>A backend failure or temporary unavailability.</td><td>For transient failures, retry with bounded exponential backoff. Respect <code>Retry-After</code> when supplied.</td></tr>
</tbody>
</table>
</div>
<h3>Two payout errors to handle differently</h3>
<p><code>payouts-ambiguous-address</code>: an address is recorded against an account outside your requested group. The entire request returns <code>409</code> instead of guessing attribution or silently omitting that address.</p>
<p><code>payouts-cursor-changed</code>: the pagination snapshot or request context changed. Restart the same fixed window without a cursor and reconcile your saved outputs with the replacement results.</p>
<p class="small">For transient server errors, retry with bounded exponential backoff. Don’t keep retrying <code>400</code>, <code>401</code>, or <code>403</code>.</p>
<details>
<summary>Versioning and compatibility</summary>
<div class="details-body">
<p>Ignore response fields your client doesn’t recognize. New fields can be added within v1. Incompatible response or authentication changes require a new API version and a documented migration. This interface does not imply an availability SLA.</p>
</div>
</details>
</section>
<section id="reading-data" aria-labelledby="reading-data-title">
<h2 id="reading-data-title">Reading the values</h2>
<dl class="glossary">
<dt>Account and subaccount hashrate</dt><dd>The account hashrate endpoint returns raw H/s. Subaccount hashrate routes return value/unit objects in TH/s or PH/s, rounded to two decimal places. Worker hashrates remain raw H/s on both account and subaccount routes.</dd>
<dt>Estimated earnings and payout history</dt><dd>Generated-BTC results combine generated FPPS BTC with cached future PPLNS projections. That combined amount is not a paid balance, payout guarantee, or settled accounting record. Payout history reports confirmed outputs associated with recorded addresses.</dd>
<dt>Dates and timestamps</dt><dd>Earnings days and payout date windows use UTC. Worker <code>connected_at</code> values use Unix milliseconds or <code>null</code>; payout <code>confirmed_at</code> and <code>as_of</code> values use Unix seconds.</dd>
<dt>Missing PPLNS hashrate</dt><dd>For earnings rows, <code>pplns_hashrate: null</code> means the rate is unavailable. A covered day that was read successfully and had no recorded work returns <code>pplns_hashrate: 0</code>. Unavailable rates are never reported as zero. Do not replace <code>null</code> with zero.</dd>
<dt>Pages of results</dt><dd>Worker and payout responses use <code>next_cursor</code> to continue to the next page; <code>null</code> marks the last page. Earnings use calendar-day windows instead.</dd>
</dl>
<div class="end-note">
<p><strong>Monitoring keys are managed in the dashboard.</strong><br>The request example above uses a key with <code>hashrate_read</code> to read the account’s latest hashrate.</p>
<a href="https://dashboard.dmnd.work" class="button primary">Open your dashboard ↗</a>
</div>
</section>
</article>
</main>
<footer>
<span>© 2026 dmnd pool. all rights reserved.</span>
<div><a href="termsofservice.html">Terms</a> · <a href="privacypolicy.html">Privacy</a> · <a href="responsibledisclosurepolicy.html">Responsible Disclosure</a></div>
</footer>
<div id="copy-status" class="sr-only" role="status" aria-live="polite"></div>
</body>
</html>