Skip to main content

Query last-epoch usage details

This example reads the latest usage-details snapshot embedded in GET /v0/gateways/{address}. The recent.epoch.usageDetails block is available to API clients with Mobile scope when a recent snapshot exists. It separates default usage traffic from unrewarded demand-sampling traffic in the sampled bucket.

Prerequisites​

  • Node.js 18 or newer.
  • An API key exported as HELIUMGEEK_API_KEY.
  • A gateway address provided via the GATEWAY_ADDRESS environment variable or the first CLI argument.

Run the example​

  1. Download mobile-usage-last-epoch.js and save it locally.

  2. Export the environment variables and run the script:

    export HELIUMGEEK_API_KEY="your-api-key"
    export GATEWAY_ADDRESS="your-gateway-address"
    node mobile-usage-last-epoch.js

    Or supply the address inline:

    HELIUMGEEK_API_KEY="your-api-key" node mobile-usage-last-epoch.js <gateway-address>

Script​

mobile-usage-last-epoch.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();

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 response = await fetch(
buildUrl(`/gateways/${encodeURIComponent(GATEWAY_ADDRESS)}`),
{
headers: {
'x-api-key': API_KEY,
},
}
);

if (!response.ok) {
throw new Error(`Request failed: ${response.status} ${response.statusText}`);
}

const gateway = await response.json();
const usageDetails = gateway?.recent?.epoch?.usageDetails;

if (!usageDetails) {
console.log(
`Gateway ${GATEWAY_ADDRESS} has no usage details for the most recent epoch.`
);
return;
}

const epochEnd = new Date(usageDetails.epochEndTimestamp * 1000).toISOString();
console.log(`Usage details for ${GATEWAY_ADDRESS} (epoch ending ${epochEnd})`);

// Keep the buckets separate: `usage` is default traffic and `sampled` is
// unrewarded demand-sampling traffic, with different reward semantics.
printBucket('Usage', usageDetails.usage, true);
printBucket('Sampled', usageDetails.sampled, false);
}

function printBucket(label, bucket, includesRewardedBytes) {
if (!bucket) {
console.log(`${label}: no traffic reported`);
return;
}

const rewarded =
includesRewardedBytes && bucket.rewardedBytes !== undefined
? `, ${formatBytes(bucket.rewardedBytes)} rewarded`
: '';
console.log(
`${label}: ${bucket.userCount ?? 0} users, ${formatBytes(bucket.transferBytes ?? 0)} transferred${rewarded}`
);

if (Array.isArray(bucket.carrierTransferInfo) && bucket.carrierTransferInfo.length) {
console.log(` ${label} carriers:`);
for (const carrier of bucket.carrierTransferInfo) {
const carrierName =
carrier.carrier?.name ?? carrier.carrier?.idString ?? `carrier_${carrier.carrier?.id ?? '?'}`;
const carrierRewarded =
includesRewardedBytes && carrier.rewardedBytes !== undefined
? `, ${formatBytes(carrier.rewardedBytes)} rewarded`
: '';
console.log(
` ${carrierName}: ${carrier.userCount ?? 0} users, ${formatBytes(carrier.transferBytes ?? 0)} transferred${carrierRewarded}`
);
}
}
}

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 formatBytes(bytes) {
const gigabytes = Number(bytes) / 1e9;
return `${byteFormatter.format(gigabytes)} GB`;
}

main().catch((error) => {
console.error(error.message);
process.exit(1);
});