- Published on
Monitor Monero Mining Rewards with Python
Track Monero mining payouts with Python by querying pool APIs, logging history, and automating alerts across SupportXMR and other pools.
- Authors

- Name
- Adam Johnston
- @admjski
Monitor Monero Mining Rewards with Python
By Adam Johnston – 404cache
First-hand context: I have mined and experimented with Monero, and implemented and tested the API example below. The original test date, pool API version, and response fixture were not recorded here, so treat the SupportXMR code as an example to verify against the response your pool currently returns—not as a guarantee of current compatibility.
When a mining rig hums away in a spare room, it's tempting to trust the pool dashboard and move on. The risk is missing an unpaid balance threshold or overlooking stale shares that quietly erode profitability. A lean Python workflow keeps the ledger transparent without adding another bloated dashboard or subscription to maintain.
This guide shows a Python pattern for checking a pool balance, recording observations, and adding alerts. Pool APIs and mining setups vary; verify field names, units, payout rules, and polling guidance with the pool you use.
Image idea: an annotated diagram showing your mining rig, pool API, Python script, and notification channel.
Why Track Your Monero Balance Proactively

Monero's privacy-first design still leaves your mining pool with rich telemetry. Focus on the metrics below so the unpaid balance never lives in isolation.
How pool payouts are calculated
- Share contributions: Pools such as SupportXMR pay according to valid shares submitted, not raw hash rate.
- Payment threshold: Each pool sets its own payout threshold and rules. Check the current pool documentation; thresholds affect when a payout is sent, not whether mining is profitable.
- Luck and variance: Track average vs. current hash rates to separate bad luck from a configuration problem; the Monero pool primer shows how variance normalises over time.
Callout: If your unpaid balance stalls for multiple days, check for stale shares or downtime on the pool status page before blaming your hardware.
Key metrics worth logging
- Unpaid amount: Pool APIs use different names and units (for example,
amtDueorbalance); verify the meaning and conversion before displaying a value as XMR. - Hash rate: Field names and averaging windows vary by pool. Compare like-for-like readings before using them to diagnose a worker.
- Share and payment history: Where supplied, timestamps and payment records can help confirm activity and reconcile transfers.
Keeping a local log gives you leverage when contacting support or measuring the impact of undervolting, new miners, or firmware tweaks.
Tooling Checklist Before You Code

Getting the basics right now prevents noisy alerts later. Install Python 3.10 or newer, create an isolated virtual environment, and add requests, python-dotenv, and pydantic to handle HTTP calls, secrets, and validation.
python3 -m venv venv
source venv/bin/activate
pip install requests python-dotenv pydantic
Configuration hygiene
Store wallet addresses and API tokens (if required) in a .env file, document which machines run the script, and version-control your code so experiments are reversible. Do not commit .env files or real wallet identifiers to a public repository.
Hands-On Example: Building the Balance Checker
The following is the SupportXMR-shaped example I implemented and tested. The test date and API version are not available, and the current first-party response contract could not be verified for this review. Inspect a current response and adapt the model before relying on the script; never assume field names or units are stable.
from __future__ import annotations
import os
from datetime import datetime, timezone
import requests
from pydantic import BaseModel, Field
from dotenv import load_dotenv
load_dotenv()
POOL = "https://supportxmr.com/api/miner"
WALLET = os.environ["XMR_WALLET"]
class MinerStats(BaseModel):
balance: float = Field(alias="balance")
hashrate: float = Field(alias="hash")
hashrate_24h: float = Field(alias="hash2")
last_share: int = Field(alias="lastShare")
class ApiResponse(BaseModel):
stats: MinerStats
def fetch_stats(wallet: str) -> MinerStats:
url = f"{POOL}/{wallet}/stats"
response = requests.get(url, timeout=15)
response.raise_for_status()
payload = ApiResponse.model_validate(response.json())
return payload.stats
def human_time(epoch: int) -> str:
return datetime.fromtimestamp(epoch, tz=timezone.utc).strftime("%Y-%m-%d %H:%M UTC")
def main() -> None:
stats = fetch_stats(WALLET)
balance_xmr = stats.balance / 1e12
print(f"Unpaid balance: {balance_xmr:.6f} XMR")
print(f"Current hash rate: {stats.hashrate / 1e3:.2f} kH/s")
print(f"24h average: {stats.hashrate_24h / 1e3:.2f} kH/s")
print(f"Last share submitted: {human_time(stats.last_share)}")
if __name__ == "__main__":
main()
Key upgrades from the original snippet:
- Pydantic checks that the received values fit the model declared here. It cannot confirm that the remote API still returns this shape or that the fields have the assumed meaning.
- Timestamps are converted to human-readable UTC output for faster troubleshooting.
- The script raises on non-200 responses, making it easy to catch outages in logs or monitoring tools.
Tip: When experimenting with different pool payloads, inspect a sanitized JSON sample and write a fixture test for the fields and units your script expects. Do not publish wallet addresses, credentials, or identifying pool data in the fixture.
Persisting Data for Trend Analysis
Knowing today's balance is useful; seeing the trend lines prevents surprises. Two lightweight approaches cover most solo miners.
Append to a CSV ledger
import csv
from pathlib import Path
LEDGER = Path("balance_history.csv")
def log_balance(amount: float, hashrate: float) -> None:
is_new = not LEDGER.exists()
with LEDGER.open("a", newline="") as fh:
writer = csv.writer(fh)
if is_new:
writer.writerow(["timestamp", "balance_xmr", "hashrate_khs"])
writer.writerow([datetime.now(timezone.utc).isoformat(), amount, hashrate])
CSV works everywhere and lets you whip up quick charts in Excel or Google Sheets. Commit the file to a private repo if you track long-term earnings.
Log to SQLite for richer queries
SQLite adds minimal overhead and lets you answer questions like “What was my average hash rate after undervolting the GPUs last month?” Use the sqlite3 module to create a balances table with timestamp, balance, and hash rate columns, then insert rows whenever the cron job runs. Once data accumulates, plug the database into Grafana or a minimal Flask dashboard to visualise progress. Image suggestion: screenshot of a Grafana panel showing balance trend lines.
Automating Alerts Before Payouts
A script you have to remember to run defeats the point. Automation ensures you get nudges without babysitting terminals.
Schedule the script
- Linux / macOS: Add a cron entry such as
0 * * * * /home/miner/venv/bin/python /home/miner/check_balance.py >> /home/miner/logs/balance.log 2>&1to run hourly. - Windows: Use Task Scheduler with the "Start in" directory set to your project folder. Trigger every 60 minutes or whenever your rig starts.
Push notifications you can trust
- Email: Use
smtplibwith environment variables for credentials and trigger when balance exceeds the pool's threshold. - Matrix or Discord: Post to a webhook for mobile-friendly alerts.
- Home Assistant: Forward the data to a local automation hub for audible warnings.
THRESHOLD = 0.05 # XMR
if balance_xmr >= THRESHOLD:
requests.post(
os.environ["DISCORD_WEBHOOK"],
json={"content": f"🎉 Payout ready: {balance_xmr:.4f} XMR"},
timeout=10,
)
Remember to keep webhook URLs secret. Store them in .env and load them like the wallet address.
Beyond SupportXMR: Adapting to Other Pools
Every pool adds quirks. Start with that pool's current API documentation; endpoints, authentication, field names, and units differ. Normalize units only after verifying them, and keep sanitized response fixtures so tests reveal when a provider changes its payload.
Security, Privacy, and Trust Considerations
Pool statistics associated with a wallet address may reveal mining activity. A public wallet address is not a secret credential, but avoid linking it to your identity unnecessarily and protect any API credentials or local records that are private.
- Limit exposure: Avoid publishing wallet addresses or associating them with personal details; redact them from examples and screenshots.
- Rotate credentials: Regenerate API keys regularly and pin dependencies in
requirements.txtso supply-chain changes are deliberate. - Encrypt history: Use
gpgor BitLocker before archiving months of payouts. - Verify binaries: When running third-party tools like
tray-xmr, build from source or verify release signatures from the maintainer's GitHub tags.
The Monero best practices guide offers further reading on securing wallets and mining endpoints.
Notes on the original mining anecdote
An earlier version described a friend's GPU rig, specific pool thresholds and alert settings, a failing riser, and an electricity-cost saving. Supporting logs, tariff calculations, and the method behind that saving are not included, so those figures and the causal claim have been removed rather than presented as a measured result. They can be restored only with evidence and an explanation of how the saving was calculated.
Troubleshooting Checklist
- Script fails with
KeyError: The API payload likely changed. Dumpresponse.json()and adjust your models. requests.exceptions.Timeout: Increase the timeout or run a local pool status check. Pools sometimes throttle frequent polling.- Balance reads zero: Confirm your wallet address matches the pool dashboard. Copy/paste errors are common with Monero's long strings.
- Cron job silent failures: Redirect output to a log file and inspect permissions on the Python interpreter.
FAQ
Can I monitor multiple wallets or workers at once?
Yes. Keep wallet addresses in a list or config file, loop over them, and tag each row with the worker name for quick comparisons.
How often should I poll the API?
Follow the pool's published rate limits. If none are documented, use conservative polling, cache results, and contact the pool operator before increasing request frequency. This article does not verify a SupportXMR-specific limit.
Will this work for pools that pay in Bitcoin or other coins?
The structure is the same. Swap the endpoint, adjust units, and convert payouts with an exchange-rate API such as CoinGecko.
How do I integrate the data into a tax report?
Export the SQLite table or CSV ledger each quarter and reconcile it with wallet transactions. Accounting tools like Koinly accept CSV imports with timestamps, amounts, and transaction IDs.
What if I prefer a GUI over scripts?
Projects like tray-xmr offer Windows-friendly interfaces. Run them alongside your Python logger for redundancy.
Conclusion: Build Confidence in Every Payout
A dependable monitoring workflow is about recording what the pool actually reports and making changes based on comparable measurements. A balance checker can help you notice activity changes, but it cannot predict profitability or replace pool documentation, hardware diagnostics, or tax advice.
Before using the example, verify it against your pool's current API and keep wallet data and credentials private.
Further looks

