Custom Metrics API
The custom metrics API is the most flexible part of numbrs. If you can run a curl command, you can get data onto a dashboard. Use it for system stats, GitHub Actions results, cron job outputs, IoT readings — anything numeric.
Estimated setup time: 2 minutes
Quick start
Section titled “Quick start”Get your API key from Settings → API Keys, then push a metric:
curl -X POST https://numbrs.lol/api/ingest \ -H "Content-Type: application/json" \ -H "Authorization: Bearer YOUR_API_KEY" \ -d '{"metric": "my.first.metric", "value": 42}'That’s it. The data point is in your database.
If you’re self-hosting, replace https://numbrs.lol with your own URL.
API reference
Section titled “API reference”Endpoint: POST /api/ingest
Authentication: Authorization: Bearer YOUR_API_KEY
Content-Type: application/json
Single metric
Section titled “Single metric”{ "metric": "server.cpu_pct", "value": 23.4, "timestamp": "2025-03-25T20:00:00Z", "tags": { "host": "mac-mini", "env": "production" }}Field reference
Section titled “Field reference”| Field | Type | Required | Description |
|---|---|---|---|
metric | string | yes | Dot-separated name, e.g. server.cpu_pct |
value | number | yes | The numeric value to record |
timestamp | ISO 8601 | no | When the measurement was taken. Defaults to now. |
tags | object | no | Key-value string pairs for filtering in dashboards |
Batch ingestion
Section titled “Batch ingestion”Send up to 100 metrics in one request — just use an array:
curl -X POST https://numbrs.lol/api/ingest \ -H "Content-Type: application/json" \ -H "Authorization: Bearer YOUR_API_KEY" \ -d '[ {"metric": "server.cpu_pct", "value": 23.4, "tags": {"host": "mac-mini"}}, {"metric": "server.mem_used_gb", "value": 6.1, "tags": {"host": "mac-mini"}}, {"metric": "server.disk_used_gb", "value": 142.7, "tags": {"host": "mac-mini"}} ]'Batch requests are more efficient than individual calls when you’re pushing multiple metrics at once.
How tags work in dashboards
Section titled “How tags work in dashboards”Tags are key-value pairs you attach to a metric when pushing it. They let you push the same metric name from multiple sources and then filter by tag in the dashboard panel.
Example: you’re tracking CPU on three machines. Instead of three metric names, use one name + a host tag:
# Machine 1{"metric": "server.cpu_pct", "value": 45.2, "tags": {"host": "mac-mini"}}
# Machine 2{"metric": "server.cpu_pct", "value": 12.8, "tags": {"host": "vps-1"}}
# Machine 3{"metric": "server.cpu_pct", "value": 78.1, "tags": {"host": "raspberry-pi"}}In the dashboard panel editor:
- Select metric:
server.cpu_pct - Add tag filter:
host = mac-mini
Each panel shows one filtered slice. Add three panels — one per host — to compare them side by side on the same dashboard.
Common tag keys people use:
host— which machineenv— production / staging / localrepo— which GitHub repoproject— which codebasechannel— which Slack/Discord channel
Tags are completely free-form. Use whatever makes sense for your data.
Naming conventions
Section titled “Naming conventions”Use dot-separated names following a source.category.name pattern:
server.cpu_pctserver.disk.used_gbgithub.myrepo.deploy_countbitcoin.price_usdhome.office.temp_cKeep names lowercase with underscores for spaces. Consistent naming makes filtering in dashboards much easier.
Response codes
Section titled “Response codes”| Code | Meaning |
|---|---|
200 | Data accepted |
400 | Bad request — check your JSON structure |
401 | Invalid or missing API key |
422 | Validation error — check field types |
429 | Rate limited — slow down |
Common use cases
Section titled “Common use cases”System stats collector (bash cron)
Section titled “System stats collector (bash cron)”Push CPU, RAM, and disk from any Unix system:
#!/usr/bin/env bash# numbrs-system-stats.sh — run every 5 minutes via cron
API_KEY="your-api-key"HOST="mac-mini"BASE_URL="https://numbrs.lol/api/ingest"
# CPU usage (macOS)CPU=$(top -l 1 -s 0 | grep "CPU usage" | awk '{print $3}' | tr -d '%')
# Memory pressure % (macOS)MEM=$(memory_pressure | grep "System-wide memory free percentage" | awk '{print 100 - $NF}')
# Disk used GBDISK=$(df -g / | awk 'NR==2{print $3}')
curl -sX POST "$BASE_URL" \ -H "Authorization: Bearer $API_KEY" \ -H "Content-Type: application/json" \ -d "[ {\"metric\":\"server.cpu_pct\", \"value\":$CPU, \"tags\":{\"host\":\"$HOST\"}}, {\"metric\":\"server.mem_pct\", \"value\":$MEM, \"tags\":{\"host\":\"$HOST\"}}, {\"metric\":\"server.disk_used_gb\",\"value\":$DISK, \"tags\":{\"host\":\"$HOST\"}} ]"Add to cron: */5 * * * * API_KEY=... bash ~/numbrs-system-stats.sh
Mac battery percentage
Section titled “Mac battery percentage”BATTERY=$(pmset -g batt | grep -Eo '\d+%' | tr -d '%')
curl -sX POST https://numbrs.lol/api/ingest \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d "{\"metric\":\"mac.battery_pct\",\"value\":$BATTERY}"GitHub Actions — track deploys
Section titled “GitHub Actions — track deploys”Add a step to any workflow:
- name: Record deploy in numbrs run: | curl -sX POST https://numbrs.lol/api/ingest \ -H "Authorization: Bearer ${{ secrets.NUMBRS_API_KEY }}" \ -H "Content-Type: application/json" \ -d "{ \"metric\": \"github.deploys\", \"value\": 1, \"tags\": { \"repo\": \"${{ github.repository }}\", \"branch\": \"${{ github.ref_name }}\", \"status\": \"success\" } }"Bitcoin price
Section titled “Bitcoin price”Pull the price from a public API and push it to numbrs every 5 minutes:
#!/usr/bin/env bashPRICE=$(curl -s "https://api.coinbase.com/v2/prices/BTC-USD/spot" | python3 -c "import sys,json; print(json.load(sys.stdin)['data']['amount'])")
curl -sX POST https://numbrs.lol/api/ingest \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d "{\"metric\":\"bitcoin.price_usd\",\"value\":$PRICE}"From Python
Section titled “From Python”import urllib.request, json
def push(api_key, metric, value, tags=None, timestamp=None): payload = {"metric": metric, "value": value} if tags: payload["tags"] = tags if timestamp: payload["timestamp"] = timestamp data = json.dumps(payload).encode() req = urllib.request.Request( "https://numbrs.lol/api/ingest", data=data, headers={ "Authorization": f"Bearer {api_key}", "Content-Type": "application/json", } ) urllib.request.urlopen(req)
push("your-api-key", "app.response_time_ms", 47.3, {"endpoint": "/api/users"})Viewing your data
Section titled “Viewing your data”Once metrics are flowing in, go to Dashboards, add a panel, and select your metric from the dropdown. Choose a panel type based on what you’re measuring:
- Line / Area — time series, great for trends
- Stat — latest single value (current CPU, current price)
- Gauge — current value within a range (battery %, disk %)
Metrics are retained for 90 days on the hosted plan.
Next steps
Section titled “Next steps”- Dashboard Builder — build panels for your metrics
- Alerts — get notified when thresholds are crossed
- API Reference — full endpoint documentation