Abrova
☰ Docs menu · API latency tracking

API Latency Tracking

Capture timing data for HTTP requests across Next.js, Flutter, Android, iOS, and Node.js with automatic instrumentation and percentile metrics.

Last updated: 27 Oct 2026

API Latency Tracking captures timing data for all HTTP requests made by your application. The SDK automatically instruments HTTP clients to measure request duration and reports this data to AbrovaTrace for analysis with p50, p95, and p99 percentile metrics.

HTTP Method

GET, POST, PUT, DELETE, PATCH, etc.

Request URL & Host

Full URL and host for grouping

Response Status Code

200, 404, 500, etc.

Duration

Request duration in milliseconds

Error Messages

Error details if request fails

Custom Tags

Any additional metadata you attach

Next.js / React#

Automatic Tracking

Enable automatic fetch interception to track all HTTP requests:

typescript
import { AbrovaTrace } from '@abrova-trace/nextjs-sdk';

AbrovaTrace.init({
  apiKey: 'ab_live_your_api_key',
  environment: 'production',
});

// Enable automatic HTTP tracking
// This intercepts window.fetch and reports timing
AbrovaTrace.getInstance()?.enablePerformanceTracking({
  batchSize: 5,        // Send spans in batches of 5 (default)
  flushInterval: 5000, // Flush at most 5 seconds after a span is buffered (default)
});

Manual Operation Tracking

typescript
const sdk = AbrovaTrace.getInstance();

// Track an async operation
const result = await sdk?.trackOperation(
  'fetchUserProfile',
  async () => {
    const response = await fetch('/api/users/123');
    return response.json();
  },
  {
    operationType: 'http',
    tags: { userId: '123' }
  }
);

Performance Fetch Wrapper

typescript
const sdk = AbrovaTrace.getInstance();

// Create a tracked fetch function
const performanceFetch = sdk?.createPerformanceFetch() ?? fetch;

// Use it like regular fetch — timing is automatically captured
const response = await performanceFetch('/api/users');
const data = await response.json();

The wrapper reports spans only after enablePerformanceTracking(), trackOperation() or reportPerformanceSpan() has been called. In the browser, enablePerformanceTracking() already tracks every fetch, so use the wrapper on the server.

Manual Span Reporting

typescript
import { createHttpSpan, PerformanceSpanFactory } from '@abrova-trace/nextjs-sdk';

// Create and report an HTTP span
const span = createHttpSpan({
  method: 'GET',
  url: 'https://api.example.com/users',
  statusCode: 200,
  durationMs: 245,
  startTime: new Date(),
});
AbrovaTrace.getInstance()?.reportPerformanceSpan(span);

// Or use the factory for other span types
const dbSpan = PerformanceSpanFactory.forDbQuery({
  queryType: 'SELECT',
  tableName: 'users',
  durationMs: 15,
  startTime: new Date(),
});
AbrovaTrace.getInstance()?.reportPerformanceSpan(dbSpan);

Flutter#

Performance HTTP Client

Use the PerformanceHttpClient to automatically track all HTTP requests:

dart
import 'package:abrova_trace_flutter_sdk/abrova_trace_flutter_sdk.dart';

// Create a performance-tracking HTTP client
final client = PerformanceHttpClient(
  apiKey: 'ab_live_your_api_key',
  environment: 'production',
  releaseVersion: '1.0.0',
  addBreadcrumbs: true,      // Also add HTTP breadcrumbs
  excludedHosts: {'analytics.example.com'},  // Hosts to skip (Abrova's own API is always skipped)
  minDurationMs: 0,          // Track all requests (set higher to filter fast ones)
);

// All requests are automatically tracked
final response = await client.get(
  Uri.parse('https://api.example.com/users'),
);

// Don't forget to flush and close
await client.flush();
client.close();

Custom Operation Tracking

dart
import 'package:abrova_trace_flutter_sdk/abrova_trace_flutter_sdk.dart';

// Automatic tracking with closure
final result = await PerformanceTracker.track(
  operation: 'processOrder',
  apiKey: 'ab_live_your_api_key',
  block: () async => await orderService.process(orderId),
);

// Manual tracking
final tracker = PerformanceTracker(
  operation: 'processPayment',
  apiKey: 'ab_live_your_api_key',
  operationType: 'custom',
  environment: 'production',
);
// ... perform operation
await tracker.finish(status: SpanStatus.ok);

Android (Kotlin)#

OkHttp Performance Interceptor

kotlin
import ir.abrova.trace.sdk.network.AbrovaTracePerformanceInterceptor
import okhttp3.OkHttpClient
import okhttp3.Request

val client = OkHttpClient.Builder()
    .addInterceptor(AbrovaTracePerformanceInterceptor())  // APM tracking
    .build()

// All API calls are automatically tracked
val request = Request.Builder()
    .url("https://api.example.com/users")
    .build()

client.newCall(request).execute().use { response ->
    // Duration, status code, and errors are automatically reported
    val users = response.body?.string()
}

With Retrofit

kotlin
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory

val okHttpClient = OkHttpClient.Builder()
    .addInterceptor(AbrovaTracePerformanceInterceptor())
    .build()

val retrofit = Retrofit.Builder()
    .baseUrl("https://api.example.com/")
    .client(okHttpClient)
    .addConverterFactory(GsonConverterFactory.create())
    .build()

// All API calls through this Retrofit instance are automatically tracked

Manual Span Reporting

kotlin
import ir.abrova.trace.sdk.AbrovaTrace
import ir.abrova.trace.sdk.models.PerformanceSpan

// Track a custom operation
val result = AbrovaTrace.trackOperation("load_user_profile") {
    val user = fetchUserFromApi()
    cacheUserLocally(user)
    user
}

// Manual span
AbrovaTrace.reportPerformanceSpan(
    PerformanceSpan.fromHttpRequest(
        method = "GET",
        url = "https://api.example.com/products",
        statusCode = 200,
        durationMs = 245L,
        startTime = System.currentTimeMillis() - 245
    )
)

// Database query span
AbrovaTrace.reportPerformanceSpan(
    PerformanceSpan.forDbQuery(
        queryType = "SELECT",
        tableName = "products",
        durationMs = 15L,
        startTime = System.currentTimeMillis() - 15,
        rowsAffected = 50
    )
)

iOS (Swift)#

Automatic URLSession Tracking

swift
import AbrovaTrace

// Enable automatic tracking for all URLSession requests
AbrovaTrace.shared.enablePerformanceTracking()

// Or create a custom session with tracking
let config = AbrovaTrace.shared.performanceSessionConfiguration()
let session = URLSession(configuration: config)

// All requests through this session are automatically tracked
let (data, response) = try await session.data(from: url)

Configuration

swift
// Exclude specific hosts from tracking
// (Abrova Trace's own API is always excluded)
AbrovaTrace.shared.setPerformanceExcludedHosts(["analytics.example.com"])

// Set minimum duration to track (filter very fast requests)
AbrovaTrace.shared.setPerformanceMinDuration(10) // Skip requests shorter than 10ms

Manual Tracking

swift
import AbrovaTrace

// Track with closure
let profile = try await PerformanceTracker.trackAsync("fetchProfile") {
    await api.fetchProfile()
}

// Manual tracker
let tracker = PerformanceTracker(operation: "processOrder")
tracker.setTag("orderId", value: "12345")
// ... perform operation
tracker.finish(status: .ok)

Node.js#

Operation Tracking

The Node.js SDK has no automatic HTTP span capture. Wrap the work you want timed with trackOperation, or send spans yourself with reportPerformanceSpan:

javascript
const AbrovaTrace = require('@abrova-trace/nodejs-sdk');

app.get('/api/users/:id', async (req, res) => {
  const user = await AbrovaTrace.trackOperation(
    'GET /api/users/:id',
    () => db.users.findById(req.params.id),
    { operationType: 'db', tags: { route: '/api/users/:id' } }
  );
  res.json(user);
});

Express.js Middleware

The Express middleware records each request (method, URL, status, duration) as an HTTP breadcrumb attached to errors — not as a Performance span:

javascript
const middleware = AbrovaTrace.expressMiddleware();
app.use(middleware.requestHandler());

// Your routes

// Error handler (must be last)
app.use(middleware.errorHandler());

Database Query Tracking

javascript
const { Breadcrumb } = require('@abrova-trace/nodejs-sdk');

async function executeQuery(sql, params) {
  const startTime = Date.now();

  try {
    AbrovaTrace.addBreadcrumb(Breadcrumb.database(sql.substring(0, 100), 0));

    const results = await db.query(sql, params);

    AbrovaTrace.addBreadcrumb(Breadcrumb.database(
      'Query completed',
      Date.now() - startTime
    ));

    return results;
  } catch (error) {
    AbrovaTrace.captureException(error, {
      extra: { sql, duration_ms: Date.now() - startTime }
    });
    throw error;
  }
}

Dashboard View#

The Performance dashboard shows latency data with the following views:

Overview Stats

  • • Requests
  • • Error rate
  • • p50 latency
  • • p95 / p99 latency

Latency

  • • p50, p95, p99 over time
  • • Requests over time, errors stacked

Operations

  • • Operation name and type
  • • Request count and latency percentiles
  • • Error count
  • • Status codes, Web Vitals and failed requests cards

Filtering

  • • Time range (1h, 24h, 7d, 30d or custom)
  • • Platform
  • • Environment
  • • Release

Best Practices#

Use Meaningful Operation Names

Use descriptive names like "GET /api/users" instead of generic names for easier identification

Add Custom Tags

Add tags like user ID, tenant ID, or feature flags to filter and segment data in the dashboard

Set Environment Correctly

Always set environment (production, staging, development) to separate data from different deployments

Monitor p95/p99 Not Just Average

Average latency hides outliers — focus on p95 and p99 for worst-case user experience

Exclude Noise

Filter out health check endpoints and internal SDK calls from tracking

Use Batch Reporting

Let the SDK batch spans — use reportPerformanceSpanBatch when you report many spans yourself

Was this page helpful?
Report a problem