Skip to main content

Usage & Analytics

The ONE SDK provides a dedicated UsageService for tracking API consumption, monitoring quotas, and reviewing usage analytics. You can view usage data in the dashboard or query it programmatically.

Creating a UsageService

The SDK offers two factory functions for creating the service.

createUsageService

Create a new UsageService instance with explicit configuration.

import { createUsageService } from '@one_deploy/sdk';

const usageService = createUsageService({
engineUrl: process.env.ONE_ENGINE_URL!,
clientId: process.env.ONE_CLIENT_ID!,
secretKey: process.env.ONE_SECRET_KEY!,
projectId: 'proj_abc123',
});

getUsageService

Retrieve a singleton UsageService instance that reuses the configuration from the current OneEngineClient context.

import { getUsageService } from '@one_deploy/sdk';

const usageService = getUsageService();
tip

When using getUsageService() inside a React component, the service automatically inherits configuration from the nearest OneProvider. No additional setup is needed.

Types

UsageCategory

Categorises the type of API call being tracked.

type UsageCategory =
| 'auth'
| 'wallet'
| 'swap'
| 'onramp'
| 'offramp'
| 'ai_trading'
| 'forex'
| 'contracts'
| 'nfts'
| 'billing'
| 'staking'
| 'bridge'
| 'gas'
| 'price'
| 'webhooks'
| 'admin'
| 'project';

DisplayCategory

A higher-level grouping used in the dashboard UI charts and tables.

type DisplayCategory =
| 'Authentication'
| 'Wallet & Assets'
| 'Payments'
| 'Trading'
| 'Infrastructure'
| 'Management';

UsageRecord

A single usage data point representing a time-bucketed count.

interface UsageRecord {
id: string;
projectId: string;
category: UsageCategory;
count: number;
timestamp: string; // ISO-8601
metadata?: Record<string, unknown>;
}

UsageSummary

Aggregated usage totals for a project within a given time range.

interface UsageSummary {
projectId: string;
totalCalls: number;
byCategory: Record<UsageCategory, number>;
byDisplayCategory: Record<DisplayCategory, number>;
periodStart: string; // ISO-8601
periodEnd: string; // ISO-8601
quotaLimit: number;
quotaUsed: number;
quotaRemaining: number;
}

UsageActivity

A timestamped activity entry for the usage feed.

interface UsageActivity {
id: string;
projectId: string;
category: UsageCategory;
action: string; // e.g. "swap.execute", "wallet.getBalance"
timestamp: string; // ISO-8601
statusCode: number;
durationMs: number;
metadata?: Record<string, unknown>;
}

UsageResponse

The standard envelope returned by usage query methods.

interface UsageResponse<T> {
success: boolean;
data?: T;
error?: string;
meta?: {
page: number;
limit: number;
total: number;
};
}

Tracking API Usage

Get Usage Summary

Retrieve an aggregated summary for a project over a specified time range.

const summary = await usageService.getUsageSummary({
projectId: 'proj_abc123',
from: '2025-01-01T00:00:00Z',
to: '2025-01-31T23:59:59Z',
});

if (summary.success) {
const data: UsageSummary = summary.data;
console.log('Total API calls:', data.totalCalls);
console.log('Quota used:', data.quotaUsed, '/', data.quotaLimit);
console.log('Remaining:', data.quotaRemaining);

// Breakdown by category
for (const [category, count] of Object.entries(data.byCategory)) {
console.log(` ${category}: ${count} calls`);
}
}

Get Usage Records

Fetch granular, time-bucketed usage records for charting and detailed analysis.

const records = await usageService.getUsageRecords({
projectId: 'proj_abc123',
category: 'ai_trading',
from: '2025-01-01T00:00:00Z',
to: '2025-01-31T23:59:59Z',
granularity: 'day', // 'hour' | 'day' | 'week' | 'month'
page: 1,
limit: 31,
});

if (records.success) {
for (const record of records.data) {
console.log(`${record.timestamp}: ${record.count} calls`);
}
}

Get Usage Activity Feed

Retrieve a chronological feed of individual API calls for debugging and auditing.

const activity = await usageService.getUsageActivity({
projectId: 'proj_abc123',
category: 'wallet',
page: 1,
limit: 50,
});

if (activity.success) {
for (const entry of activity.data) {
console.log(
`[${entry.timestamp}] ${entry.action} -- ${entry.statusCode} (${entry.durationMs}ms)`
);
}
}

Viewing Analytics in the Dashboard

The dashboard at dashboard.one23.io provides a visual analytics view with:

  • Volume chart -- daily and hourly API call volume over the selected time range.
  • Category breakdown -- pie or bar chart showing the distribution of calls across DisplayCategory groups.
  • Top endpoints -- ranked list of the most frequently called API methods.
  • Error rate -- percentage of calls returning 4xx or 5xx status codes.
  • Latency percentiles -- p50, p95, and p99 response times across all endpoints.

Quotas and Rate Limits

Each project plan has a monthly API call quota and per-second rate limits.

PlanMonthly QuotaRate Limit (req/s)
Free10,00010
Pro500,000100
EnterpriseCustomCustom

Checking Quota Status

const summary = await usageService.getUsageSummary({
projectId: 'proj_abc123',
from: new Date(new Date().getFullYear(), new Date().getMonth(), 1).toISOString(),
to: new Date().toISOString(),
});

if (summary.success) {
const { quotaLimit, quotaUsed, quotaRemaining } = summary.data;
const usagePercent = ((quotaUsed / quotaLimit) * 100).toFixed(1);

console.log(`Usage: ${quotaUsed} / ${quotaLimit} (${usagePercent}%)`);
console.log(`Remaining: ${quotaRemaining}`);

if (quotaRemaining < quotaLimit * 0.1) {
console.warn('Warning: Less than 10% of your monthly quota remains.');
}
}

Rate Limit Headers

When a request is rate-limited, the API returns a 429 Too Many Requests status with the following headers:

HeaderDescription
X-RateLimit-LimitMaximum requests allowed per second for your plan.
X-RateLimit-RemainingRemaining requests in the current window.
X-RateLimit-ResetUnix timestamp (seconds) when the rate limit window resets.
Retry-AfterNumber of seconds to wait before retrying.

Handling Rate Limits

async function callWithRetry<T>(
fn: () => Promise<UsageResponse<T>>,
maxRetries: number = 3
): Promise<UsageResponse<T>> {
for (let attempt = 0; attempt <= maxRetries; attempt++) {
const res = await fn();

if (res.success || res.error !== 'rate_limited') {
return res;
}

// Exponential backoff: 1s, 2s, 4s
const delay = Math.pow(2, attempt) * 1000;
console.warn(`Rate limited. Retrying in ${delay}ms...`);
await new Promise((resolve) => setTimeout(resolve, delay));
}

return { success: false, error: 'Max retries exceeded' };
}

Full Example

usage-report.ts
import { createUsageService } from '@one_deploy/sdk';

const usageService = createUsageService({
engineUrl: process.env.ONE_ENGINE_URL!,
clientId: process.env.ONE_CLIENT_ID!,
secretKey: process.env.ONE_SECRET_KEY!,
projectId: 'proj_abc123',
});

async function generateMonthlyReport() {
const now = new Date();
const monthStart = new Date(now.getFullYear(), now.getMonth(), 1);

// 1. Summary
const summary = await usageService.getUsageSummary({
projectId: 'proj_abc123',
from: monthStart.toISOString(),
to: now.toISOString(),
});

if (!summary.success) {
console.error('Failed to fetch summary:', summary.error);
return;
}

console.log('=== Monthly Usage Report ===');
console.log(`Period: ${summary.data.periodStart} to ${summary.data.periodEnd}`);
console.log(`Total calls: ${summary.data.totalCalls}`);
console.log(`Quota: ${summary.data.quotaUsed} / ${summary.data.quotaLimit}`);

// 2. Category breakdown
console.log('\n--- By Category ---');
for (const [cat, count] of Object.entries(summary.data.byCategory)) {
if (count > 0) {
console.log(` ${cat}: ${count}`);
}
}

// 3. Daily records for the top category
const topCategory = Object.entries(summary.data.byCategory)
.sort(([, a], [, b]) => b - a)[0];

if (topCategory) {
const records = await usageService.getUsageRecords({
projectId: 'proj_abc123',
category: topCategory[0] as any,
from: monthStart.toISOString(),
to: now.toISOString(),
granularity: 'day',
});

if (records.success) {
console.log(`\n--- Daily Breakdown: ${topCategory[0]} ---`);
for (const record of records.data) {
console.log(` ${record.timestamp}: ${record.count}`);
}
}
}
}

generateMonthlyReport();

Next Steps