Query usage trends
Fetch a rolling series of usage epochs using GET /v0/gateways/{address}/mobile/usage. The script below prints user counts, transfer bytes, and carrier breakdowns for the most recent 30 days by default. Adjust the window length with the WINDOW_DAYS argument or environment variable.
tip
All usage records are attributed to the epoch end time. When you filter with min_time and max_time, the API compares those bounds against epochEndTimestamp. See the time-series guide for more background.
Prerequisites
- Node.js 18 or newer.
- An API key exported as
HELIUMGEEK_API_KEY. - A gateway address provided as the
GATEWAY_ADDRESSenvironment variable or the first CLI argument.
Run the example
- Download mobile-usage-trend.js and save it locally.
- Export the environment variables and choose the window length (defaults to 30 days).
export HELIUMGEEK_API_KEY="your-api-key"
export GATEWAY_ADDRESS="your-gateway-address"
node mobile-usage-trend.js
Different window (for example, 60 days):
node mobile-usage-trend.js <gateway-address> 60
Set WINDOW_DAYS or pass a numeric CLI argument to tweak the range. LIMIT controls the number of records per request (defaults to 100).
Script
mobile-usage-trend.js
#!/usr/bin/env node
const BASE_URL = (process.env.HELIUMGEEK_API_BASE_URL ?? 'https://api.heliumgeek.com/v0').replace(/\/$/, '');
const API_KEY = process.env.HELIUMGEEK_API_KEY;
const GATEWAY_ADDRESS =
(process.argv[2] ?? process.env.GATEWAY_ADDRESS ?? '').trim();
const WINDOW_DAYS = Number(process.argv[3] ?? process.env.WINDOW_DAYS ?? 30);
const LIMIT = Number(process.env.LIMIT ?? 100);
if (!API_KEY) {
console.error('Set HELIUMGEEK_API_KEY before running this script.');
process.exit(1);
}
if (!GATEWAY_ADDRESS) {
console.error(
'Provide the gateway address as an argument or set GATEWAY_ADDRESS.'
);
process.exit(1);
}
const byteFormatter = new Intl.NumberFormat('en-US', {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
});
async function main() {
const now = new Date();
const minDate = startOfUtcDay(now);
minDate.setUTCDate(minDate.getUTCDate() - WINDOW_DAYS);
const response = await fetchUsage(minDate, now, LIMIT);
const entries = (response.data ?? []).slice().reverse();
if (entries.length === 0) {
console.log(
`Gateway ${GATEWAY_ADDRESS} reported no usage between ${minDate.toISOString()} and ${now.toISOString()}.`
);
return;
}
console.log(`Usage stats for ${GATEWAY_ADDRESS}`);
for (const entry of entries) {
const label = formatLabel(entry.epochEndTimestamp);
const serviceProviderBytes = Number(entry.transferBytes?.serviceProvider ?? 0);
const offloadBytes = Number(entry.transferBytes?.offload ?? 0);
const totalBytes = serviceProviderBytes + offloadBytes;
const spShare = totalBytes > 0 ? ((serviceProviderBytes / totalBytes) * 100).toFixed(2) : '0.00';
const offloadShare = totalBytes > 0 ? ((offloadBytes / totalBytes) * 100).toFixed(2) : '0.00';
const counts = entry.userCounts ?? {};
console.log(
`${label} users SP ${counts.serviceProvider ?? 0} Mapping ${counts.discoMapping ?? 0} Offload ${counts.offload ?? 0}`
);
console.log(
` transfer SP ${formatBytes(serviceProviderBytes)} (${spShare}%) Offload ${formatBytes(offloadBytes)} (${offloadShare}%) Total ${formatBytes(totalBytes)}`
);
if (Array.isArray(entry.carrierTransferInfo) && entry.carrierTransferInfo.length) {
for (const carrier of entry.carrierTransferInfo) {
const carrierName =
carrier.carrier?.name ?? carrier.carrier?.idString ?? `carrier_${carrier.carrier?.id ?? '?'}`;
const carrierBytes = formatBytes(carrier.transferBytes ?? 0);
const carrierUsers = carrier.userCount ?? 0;
console.log(` ${carrierName}: ${carrierBytes} transfer, ${carrierUsers} users`);
}
}
}
if (response.next) {
console.log(`More results available with cursor: ${response.next}`);
}
}
async function fetchUsage(minDate, maxDate, limit) {
const url = buildUrl(`/gateways/${encodeURIComponent(GATEWAY_ADDRESS)}/mobile/usage`, {
min_time: minDate.toISOString(),
max_time: maxDate.toISOString(),
limit: limit,
});
const response = await fetch(url, {
headers: {
'x-api-key': API_KEY,
},
});
if (!response.ok) {
throw new Error(`Request failed: ${response.status} ${response.statusText}`);
}
return await response.json();
}
function buildUrl(path, query = {}) {
const url = new URL(path.replace(/^\//, ''), `${BASE_URL}/`);
Object.entries(query).forEach(([key, value]) => {
if (value !== undefined && value !== null && value !== '') {
url.searchParams.set(key, String(value));
}
});
return url.toString();
}
function startOfUtcDay(referenceDate) {
return new Date(
Date.UTC(
referenceDate.getUTCFullYear(),
referenceDate.getUTCMonth(),
referenceDate.getUTCDate()
)
);
}
function formatLabel(epochEndTimestamp) {
const iso = new Date(epochEndTimestamp * 1000).toISOString();
return iso.slice(0, 10);
}
function formatBytes(bytes) {
const gigabytes = Number(bytes) / 1e9;
return `${byteFormatter.format(gigabytes)} GB`;
}
main().catch((error) => {
console.error(error.message);
process.exit(1);
});