API Reference

Analytics

Real-time traffic analytics for every domain routed through the Oblien edge — request counts, bandwidth, geo breakdown, raw request logs, and live SSE streaming.

Analytics are collected automatically for all domains registered via Pages, Edge Proxy, Edge Tunnel, and Public Access (exposed ports). No configuration needed.

How it works

  1. Every request hitting the edge (OpenResty) is logged asynchronously — zero impact on request latency
  2. Per-minute metrics are buffered in Redis, then flushed to the database every 60 seconds
  3. The API serves recent data (last 24h) from Redis and older data from DB rollups, auto-stitching both
  4. Live streaming delivers request logs in real-time via SSE from the edge

Namespace isolation

Analytics respect namespace boundaries. When using a namespace-scoped API key, only domains within that namespace are visible — both in the home summary and per-domain endpoints. Domains outside the namespace return 404 with no information leakage.


Home summary

Aggregated stats across all your domains. Optionally filtered by namespace.

// All domains
const home = await client.analytics.home();

// Filtered by namespace
const ns = await client.analytics.home({ namespace: 'production' });
GET /analytics/home/summary
GET /analytics/home/summary?ns=production
curl "https://api.oblien.com/analytics/home/summary?ns=production" \
  -H "X-Client-ID: $OBLIEN_CLIENT_ID" \
  -H "X-Client-Secret: $OBLIEN_CLIENT_SECRET"

Query parameters

ParameterTypeDescription
nsstringFilter by namespace slug. Auto-applied for namespace-scoped API keys.

Response

{
  "success": true,
  "data": {
    "domains": [
      {
        "domain": "myapp.oblien.app",
        "requests": 14820,
        "bandwidth_in": 5242880,
        "bandwidth_out": 104857600
      },
      {
        "domain": "docs.example.com",
        "requests": 3200,
        "bandwidth_in": 1048576,
        "bandwidth_out": 20971520
      }
    ],
    "totals": {
      "requests": 18020,
      "bandwidth_in": 6291456,
      "bandwidth_out": 125829120,
      "domain_count": 2
    }
  }
}

Unified (single domain)

All analytics for a single domain in one call — overview, timeseries, geo, and request logs. This is the recommended endpoint for dashboards.

const { data } = await client.analytics.get('myapp.oblien.app');

console.log(data.overview);   // { requests, bandwidth_in, bandwidth_out }
console.log(data.timeseries); // [{ timestamp, requests, bandwidth_in, ... }]
console.log(data.geo);        // { total, countries: [{ code, count, pct }] }
console.log(data.requests);   // [{ ip, method, status, uri, ... }]
GET /analytics/:domain
GET /analytics/:domain?from=1713398400000&to=1713484800000&interval=hour&limit=100
curl "https://api.oblien.com/analytics/myapp.oblien.app?interval=hour" \
  -H "X-Client-ID: $OBLIEN_CLIENT_ID" \
  -H "X-Client-Secret: $OBLIEN_CLIENT_SECRET"

Query parameters

ParameterTypeDefaultDescription
fromnumbernow - 24hStart of range (epoch ms)
tonumbernowEnd of range (epoch ms)
intervalstringautoBucket size: minute, hour, or day. Auto-detected from range if omitted.
limitnumber50Max request log entries (1–200)

Response

{
  "success": true,
  "data": {
    "overview": {
      "requests": 14820,
      "bandwidth_in": 5242880,
      "bandwidth_out": 104857600
    },
    "timeseries": [
      {
        "timestamp": 1713398400,
        "requests": 620,
        "bandwidth_in": 204800,
        "bandwidth_out": 4194304,
        "response_time_sum": 3100,
        "unique_visitors": 45,
        "countries": { "US": 310, "DE": 180, "JP": 130 }
      }
    ],
    "geo": {
      "total": 14820,
      "countries": [
        { "code": "US", "count": 7200, "pct": 48.6 },
        { "code": "DE", "count": 3800, "pct": 25.6 }
      ]
    },
    "requests": [
      {
        "ip": "203.0.113.42",
        "timestamp": "2026-04-18T12:30:15Z",
        "date": "2026-04-18",
        "method": "GET",
        "status": "200",
        "uri": "/api/data",
        "ua": "Mozilla/5.0 ...",
        "req_size": "256",
        "res_size": "8192",
        "req_time": "0.012"
      }
    ]
  },
  "meta": {
    "domains": ["myapp.oblien.app"],
    "from": 1713398400000,
    "to": 1713484800000,
    "interval": "hour",
    "timeseries_count": 24,
    "requests_count": 50
  }
}

The overview is derived from the timeseries data — it always matches the chart. Bandwidth values are in bytes.


Timeseries

Time-bucketed metrics for charts. Last 24h from Redis (minute-level granularity), older from DB rollups (hour/day).

const ts = await client.analytics.timeseries('myapp.oblien.app', {
  from: Date.now() - 7 * 86400000,
  interval: 'day',
});
GET /analytics/:domain/timeseries?from=1712793600000&interval=day
curl "https://api.oblien.com/analytics/myapp.oblien.app/timeseries?interval=hour" \
  -H "X-Client-ID: $OBLIEN_CLIENT_ID" \
  -H "X-Client-Secret: $OBLIEN_CLIENT_SECRET"

Query parameters

ParameterTypeDefaultDescription
fromnumbernow - 24hStart of range (epoch ms)
tonumbernowEnd of range (epoch ms)
intervalstringautominute, hour, or day

Response

{
  "success": true,
  "data": [
    {
      "timestamp": 1713398400,
      "requests": 620,
      "bandwidth_in": 204800,
      "bandwidth_out": 4194304,
      "response_time_sum": 3100,
      "unique_visitors": 45,
      "countries": { "US": 310, "DE": 180 }
    }
  ],
  "meta": {
    "from": 1712793600000,
    "to": 1713484800000,
    "interval": "day",
    "count": 8
  }
}

Geo breakdown

Country breakdown of traffic. Capped to the last 24 hours (Redis-only).

const geo = await client.analytics.geo('myapp.oblien.app');
// geo.data.countries → [{ code: 'US', count: 7200, pct: 48.6 }, ...]
GET /analytics/:domain/geo
GET /analytics/:domain/geo?from=1713398400000&to=1713484800000
curl "https://api.oblien.com/analytics/myapp.oblien.app/geo" \
  -H "X-Client-ID: $OBLIEN_CLIENT_ID" \
  -H "X-Client-Secret: $OBLIEN_CLIENT_SECRET"

Response

{
  "success": true,
  "data": {
    "total": 14820,
    "countries": [
      { "code": "US", "count": 7200, "pct": 48.6 },
      { "code": "DE", "count": 3800, "pct": 25.6 },
      { "code": "JP", "count": 2100, "pct": 14.2 },
      { "code": "GB", "count": 1720, "pct": 11.6 }
    ]
  }
}

Request logs

Recent raw request logs. Served from the Redis request list — most recent first.

const logs = await client.analytics.requests('myapp.oblien.app', { limit: 100 });
GET /analytics/:domain/requests?limit=100
curl "https://api.oblien.com/analytics/myapp.oblien.app/requests?limit=100" \
  -H "X-Client-ID: $OBLIEN_CLIENT_ID" \
  -H "X-Client-Secret: $OBLIEN_CLIENT_SECRET"

Query parameters

ParameterTypeDefaultDescription
limitnumber50Max entries (1–200)

Response

{
  "success": true,
  "data": [
    {
      "ip": "203.0.113.42",
      "timestamp": "2026-04-18T12:30:15Z",
      "date": "2026-04-18",
      "method": "GET",
      "status": "200",
      "uri": "/api/data",
      "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)",
      "req_size": "256",
      "res_size": "8192",
      "req_time": "0.012"
    }
  ],
  "meta": { "count": 1 }
}

Fields

FieldDescription
ipClient IP address
timestampISO 8601 timestamp
methodHTTP method (GET, POST, etc.)
statusHTTP status code
uriRequest path
uaUser agent string
req_sizeRequest body size in bytes
res_sizeResponse body size in bytes
req_timeServer-side processing time in seconds

Live stream

Real-time request log streaming via Server-Sent Events (SSE). The stream is served directly by the edge (OpenResty) — not proxied through the API.

Step 1: Get a stream token

const { data } = await client.analytics.streamToken('myapp.oblien.app');
POST /analytics/:domain/live/token
curl -X POST "https://api.oblien.com/analytics/myapp.oblien.app/live/token" \
  -H "X-Client-ID: $OBLIEN_CLIENT_ID" \
  -H "X-Client-Secret: $OBLIEN_CLIENT_SECRET"

Response

{
  "success": true,
  "data": {
    "token": "eyJhbGciOiJIUzI1NiIs...",
    "expires_in": 300,
    "stream_url": "https://edge.oblien.com/stream",
    "usage": "Authorization: Bearer <token>"
  }
}

Step 2: Connect to the SSE stream

const es = new EventSource(
  `${data.stream_url}?token=${data.token}`
);

es.onmessage = (event) => {
  const entry = JSON.parse(event.data);
  console.log(`${entry.method} ${entry.uri} → ${entry.status}`);
};

es.onerror = () => {
  // Token expired or connection lost — request a new token and reconnect
  es.close();
};

Stream tokens expire after 5 minutes. The SSE connection must be established before expiry. If the connection drops, request a new token and reconnect.

Each SSE message is a JSON object with the same fields as request logs.


Endpoint reference

EndpointMethodDescription
/analytics/home/summaryGETAggregated stats across all domains
/analytics/:domainGETUnified: overview + timeseries + geo + requests
/analytics/:domain/timeseriesGETTime-bucketed metrics
/analytics/:domain/geoGETCountry breakdown
/analytics/:domain/requestsGETRaw request logs
/analytics/:domain/live/tokenPOSTIssue SSE stream token

Data retention

SourceGranularityRetention
Redis (live)Per-minute~24 hours
DB rollupsHourly90 days
DB rollupsDaily1 year
Request logsIndividual~1 hour (last 200 per domain)