MONITORING API · V1

Your mining data,
connected.

Bring your DMND hashrate, workers, earnings, and payouts into the tools you already use. Here’s how to get started.

View request example → Explore the endpoints
Read-only accessJSON responsesHTTPS
On this page

Example API request

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.

Base URL · the start of every requesthttps://api.dmnd.work/api/v1

This Python example reads the latest account hashrate. It uses the following credentials and permission:

  • Monitoring token. A dedicated token created in the dashboard’s API Keys page for the exact account being monitored.
  • Required permission. The token needs the hashrate_read scope.
  • Secret storage. The token is stored in the monitoring system’s secret store and supplied through the secret manager as DEMAND_MONITORING_TOKEN.
Python · no extra packages needed
import json
import os
import urllib.request

request = urllib.request.Request(
    "https://api.dmnd.work/api/v1/user/hashrate",
    headers={
        "Authorization": "Bearer " + os.environ["DEMAND_MONITORING_TOKEN"],
        "Accept": "application/json",
    },
)
with urllib.request.urlopen(request, timeout=20) as response:
    snapshot = json.load(response)
print(snapshot)

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 error handling and follow the polling guidance.

What you’ll get back: your account ID, the snapshot’s observed_at timestamp, and separate PPLNS, FPPS, and total rates. See the example response →

API keys & permissions

A monitoring token is a secret that tells DMND which account and data your tool may access. Send it in the Authorization header on every request.

Request header · replace the placeholder in your client
Authorization: Bearer <token>

Create a dedicated monitoring key in the dashboard’s API Keys 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.

Permission (scope)What it lets you read
hashrate_readCurrent and historical PPLNS, FPPS, and total hashrate.
workers_readWorker names, online status, hashrates, and accepted/rejected counters.
rejects_readAccepted and rejected share counter changes over a time window.
fees_readPool and broker fee rates.
earnings_readGenerated FPPS BTC, projected PPLNS BTC, and confirmed payout history for recorded addresses.

Keep the token private. Give each key only the permissions your tool needs. For new integrations, keep tokens out of request URLs: /api/v1 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.

Expiry, replacement keys, and older integrations

Tokens have no expiration by default. 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 100 active tokens.

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.

Token creation and revocation use the signed-in dashboard, outside /api/v1.

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.

The dashboard’s unversioned /api routes support existing watcher workflows. Existing unversioned monitoring GET URLs may use a single token 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 /api/v1 is rejected.

Choose what you want to read

Each path below is relative to the base URL. All endpoints use GET to read the data described in this reference.

Account endpoints · all paths start after /api/v1
I want to…GET pathPermission
Check current hashrate/user/hashratehashrate_read
Chart past hashrate/user/hashrate/historicalhashrate_read
List live workers/workersworkers_read
Include offline workers/workers/allworkers_read
Check accepted / rejected shares/user/share_statsrejects_read
Look up my fee rates/user/feesfees_read
Read daily estimated earnings/user/generated_btcearnings_read
Read confirmed payout outputs/user/payoutsearnings_read

These routes read the token’s account. Earnings and payouts also include directly owned subaccounts. To select one exact account, use the subaccount routes.

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.

Hashrate

GET/user/hashrate

Requires hashrate_read

Read the latest account snapshot, with separate PPLNS, FPPS, and total rates. This endpoint returns all three in raw hashes per second (H/s).

Example response
{
  "account_id": "9223372036854775807",
  "observed_at": "2026-09-08T10:00:00Z",
  "pplns_hashrate": 150000000000000,
  "fpps_hashrate": 50000000000000,
  "total_hashrate": 200000000000000
}

Snapshots are normally saved every two minutes 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.

Check the timestamp as well as the rate. Use observed_at to detect stale collection separately from low hashrate. If there is no snapshot yet, observed_at is null and all rates are zero.

Looking back in time

GET/user/hashrate/historical

This returns an array of snapshots in ascending observed_at order—oldest first. Supply from and to as RFC3339 timestamps or YYYY-MM-DD dates. Omitted bounds default to the last 24 hours.

ParameterHow to use it
fromStart timestamp (RFC3339) or UTC date, such as 2026-09-01. A date alone starts at 00:00:00 UTC.
toEnd timestamp or UTC date. A date alone ends at 23:59:59 UTC.

Each request covers at most seven days and returns at most 6,000 points. 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.

Workers

GET/workers
GET/workers/all

Requires workers_read

Use /workers for live workers, or /workers/all to include offline workers. Results come in pages of 200 workers by default; use limit to request up to 1,000.

Responses have the shape {"workers": [...], "next_cursor": "..."}. Pass next_cursor back unchanged as cursor for the next page; it is opaque, so your client should not interpret it. When it is null, you’re done.

FieldWhat it means
nameThe worker’s name.
is_connectedWhether the worker is online.
connected_atA Unix timestamp in milliseconds, or null.
hashrate, fpps_hashrateRates in raw H/s, including on subaccount worker routes.
total_shares, rejected_sharesPPLNS share counters. The equivalent FPPS counter names begin with fpps_.

Missing telemetry for a payout mode is null. Worker counters can reset, disconnected miners can affect changes between readings, and the worker list can change while you fetch pages.

Shares & fees

Check your rejection rate

GET/user/share_stats?hours=24

Requires rejects_read

This endpoint reports changes in accepted and rejected share counters over your chosen window. The hours parameter defaults to 24 and can be at most 168 (seven days).

The response includes window_hours, pplns_accepted, pplns_rejected, fpps_accepted, fpps_rejected, accepted, and rejected.

Rejection ratio: rejected / (accepted + rejected). Only calculate it when accepted + rejected is not zero.

These numbers come from telemetry counter changes, rather than an immutable record of every raw share.

Read your pool and broker fees

GET/user/fees

Requires fees_read

The response contains pool_fee and broker_fee. Both are fractions: 0.02 means 2%.

Estimated earnings, day by day

GET/user/generated_btc

Requires earnings_read · Includes directly owned subaccounts

Read daily generated FPPS BTC alongside projected PPLNS BTC. Results are an array ordered by entry_day, newest first. Every day is measured in UTC.

Estimated earnings are not confirmed payments. 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 confirmed payout history to see transaction outputs.

ParameterHow to use it
dateOne UTC day: YYYY-MM-DD. Don’t combine with before or limit.
beforeThe last UTC day to include, inclusive. Defaults to today.
limitNumber of calendar days, not rows. Defaults to 30; maximum 100.
FieldWhat it means
entry_dayThe work day, formatted YYYY-MM-DD in UTC.
fpps_btc_generatedGenerated FPPS earnings in BTC.
pplns_btc_generatedCached future PPLNS projected BTC attributed to this work day.
btc_generatedThe sum of the FPPS and projected PPLNS values.
hashrateThe FPPS-derived daily hashrate in H/s; zero for a day with only PPLNS projections.
pplns_hashratePPLNS hashrate from recorded accepted-share work, in H/s. null means unavailable.

Collecting older earnings

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.

For both account and subaccount routes, a request for one specific date 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.

Why PPLNS hashrate can be null, and how it is calculated

Keep null distinct from zero. A covered day that was read successfully and had no recorded PPLNS work returns pplns_hashrate: 0. null means the rate is unavailable, whether due to age, incomplete coverage, or a temporary refresh problem. Unavailable rates are never reported as zero.

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 pplns_hashrate: null. Recorded work alone does not create an earnings row.

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 ntime does not set the day. For completed days the calculation is sum(difficulty) * 2^32 / 86400. Today uses elapsed whole seconds since UTC midnight. Rates are rounded to two decimal places.

This is separate from worker telemetry, the difficulty in the moving PPLNS reward window, and the FPPS-derived hashrate field.

For current monitoring, use ?date=YYYY-MM-DD 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.

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 null without failing or changing the earnings response. Failed refreshes are also cached for 60 seconds.

Compatibility note: unversioned dashboard earnings routes under /api return complete available history when these parameters are omitted, so dashboard history and exports do not need pagination. New integrations using /api/v1 use the bounded defaults above.

Confirmed payouts

GET/user/payouts

Requires earnings_read · Includes directly owned subaccounts

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.

This is history associated with addresses. 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 409 with payouts-ambiguous-address. The API does not guess ownership or silently leave that address out.

ParameterHow to use it
from, toInclusive UTC dates, YYYY-MM-DD. No future dates. to defaults to today; from defaults to 29 days before it. Maximum 30 calendar days.
limitOutputs per page: 1–100. Defaults to 100.
cursorThe previous next_cursor, unchanged. Keep the same explicit dates and target account across pages.
Example URL · use the same Bearer header
https://api.dmnd.work/api/v1/user/payouts?from=2026-09-01&to=2026-09-09&limit=100
Example response
{
  "payouts": [
    {
      "txid": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
      "output_index": 2,
      "kind": "fpps",
      "address": "<recorded Bitcoin address>",
      "amount_sats": 125000,
      "confirmed_at": 1788912000
    }
  ],
  "next_cursor": null,
  "as_of": 1788912300,
  "from": "2026-09-01",
  "to": "2026-09-09"
}
  • amount_sats is an integer number of satoshis, never a floating-point BTC amount.
  • txid and the zero-based output_index together identify a unique transaction output. Each pair appears once.
  • kind is fpps or pplns.
  • confirmed_at is the block timestamp in Unix seconds, not the payment request time. as_of is when the server retrieved the snapshot, also in seconds.
  • next_cursor: null means the last page. Rows are ordered descending by confirmation timestamp, then transaction ID and output index.

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.

When a payout snapshot changes

Bitcoin confirmations can change after a chain reorganization. If you receive 409 payouts-cursor-changed, restart without a cursor using the same fixed date window. Compare the replacement results with what you collected, using txid and output_index, so removed or changed outputs are reconciled.

A change to the funding snapshot, requested dates, target account, or recorded recipient addresses invalidates the cursor with 409 payouts-cursor-changed. A funding change can affect another recipient, even if your own payouts look unchanged.

Payout refresh limits and history coverage

Poll every few minutes, reuse collected pages, and backfill one fixed window at a time. Set a client timeout that allows for a 20-second provider deadline plus authentication and database work.

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 429, follow Retry-After.

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.

Missing configuration, provider failures, timeouts, incomplete scans, or inconsistent data return 503. Respect Retry-After. After cache expiry, the service retrieves fresh history and does not serve stale data if that refresh fails.

Funding inputs must match configured FPPS/PPLNS sources or the historical FPPS source bc1qwk7rplr3ztrsvlcxujnxdvp77atpqawlh94wgw. Only outputs to authorized recorded recipient addresses are returned, without duplicates when several inputs match or an address is shared within the requested group.

Payout-history endpoints exist only under /api/v1. Dashboard payout-address discovery and watcher links are separate interfaces.

Monitoring subaccounts

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.

To read one exact account, replace {id} 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.

Keep account IDs as strings. They are decimal strings and can be negative. Preserve the exact value; don’t convert it to a JavaScript number.

GET path · exact target onlyPermission
/user/sub_account/{id}/hashratehashrate_read
/user/sub_account/{id}/hashrate/historicalhashrate_read
/user/sub_account/{id}/workersworkers_read
/user/sub_account/{id}/share_statsrejects_read
/user/sub_account/{id}/feesfees_read
/user/sub_account/{id}/generated_btcearnings_read
/user/sub_account/{id}/payoutsearnings_read

One important difference: hashrate units

Subaccount hashrate snapshots return each rate as a value/unit 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.

Example subaccount hashrate response
{
  "account_id": "1234567890123456789",
  "observed_at": "2026-09-08T10:00:00Z",
  "pplns_hashrate": {"value": 150.0, "unit": "TH/s"},
  "fpps_hashrate": {"value": 50.0, "unit": "TH/s"},
  "total_hashrate": {"value": 200.0, "unit": "TH/s"}
}

Outputs to an address shared entirely within an aggregate account group can appear in that group’s history. Each (txid, output_index) appears once. The corresponding exact-account request is ambiguous and returns 409 payouts-ambiguous-address when another recorded owner is outside that exact target.

How often should I check?

Every two to five minutes is an appropriate interval for current statistics, matching the snapshot cadence. Check payouts every few minutes, too.

  • Save collected data in your own monitoring store. Fetch only what you need instead of the entire history each time.
  • Stagger requests across accounts and limit how many run at once.
  • Set connection and request timeouts. Give payout requests enough time for their provider lookup.
Request budgets · each fixed 60-second window
BudgetRequestsShared by
Per token240All requests using that token.
Per token account600All tokens for the same account.
Per client IP1,200Requests from that IP across protected API paths.

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 429, even if your own request count is lower. The separate limits for supplemental PPLNS-rate refreshes return null when a refresh is unavailable, as described under estimated earnings.

If you’re asked to slow down: read the Retry-After header on a 429 response. Wait at least that many seconds, then add a small random delay.

Account responses use Cache-Control: private, no-store. Do not record bearer headers, API secrets, watcher links, or responses that create or list credentials in request logs, traces, or alerts.

When a request doesn’t work

Errors are JSON objects with code and message strings. Use the HTTP status to decide whether to retry. The message is for diagnosis and may change.

HTTP statusWhat it meansWhat to do
200The request worked.Read the result. Empty data can be valid.
400Invalid parameters, URL credentials, or an out-of-range limit.Fix the request before trying again.
401The key is missing, invalid, revoked, or expired.Check the bearer header and the key in API Keys. Don’t retry unchanged.
403The key lacks a scope or access to the target account.Check the selected permissions and account. Don’t retry unchanged.
404Unknown endpoint or unavailable resource.Check the base URL, path, and requested resource.
409Payout address attribution is ambiguous, or the pagination snapshot changed.Check the error code and the guidance below.
429A request or capacity budget was reached.Wait for Retry-After seconds, plus a small random delay.
500 / 503A backend failure or temporary unavailability.For transient failures, retry with bounded exponential backoff. Respect Retry-After when supplied.

Two payout errors to handle differently

payouts-ambiguous-address: an address is recorded against an account outside your requested group. The entire request returns 409 instead of guessing attribution or silently omitting that address.

payouts-cursor-changed: the pagination snapshot or request context changed. Restart the same fixed window without a cursor and reconcile your saved outputs with the replacement results.

For transient server errors, retry with bounded exponential backoff. Don’t keep retrying 400, 401, or 403.

Versioning and compatibility

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.

Reading the values

Account and subaccount hashrate
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.
Estimated earnings and payout history
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.
Dates and timestamps
Earnings days and payout date windows use UTC. Worker connected_at values use Unix milliseconds or null; payout confirmed_at and as_of values use Unix seconds.
Missing PPLNS hashrate
For earnings rows, pplns_hashrate: null means the rate is unavailable. A covered day that was read successfully and had no recorded work returns pplns_hashrate: 0. Unavailable rates are never reported as zero. Do not replace null with zero.
Pages of results
Worker and payout responses use next_cursor to continue to the next page; null marks the last page. Earnings use calendar-day windows instead.

Monitoring keys are managed in the dashboard.
The request example above uses a key with hashrate_read to read the account’s latest hashrate.

Open your dashboard ↗