Documentation menu

Logs & Search

Query log events, aggregate stats, facet counts, feature health, and filter values.

These endpoints accept a Bearer API key or a dashboard session cookie (requireSession) — see Authentication. They’re mounted twice: at the plain paths below (key-scoped to the key’s own project) and at /api/orgs/:orgSlug/projects/:projectSlug/... (session-scoped, used by the dashboard). Both mounts run the same handler.

GET /api/logs

Query log events. Also available at /api/orgs/:orgSlug/projects/:projectSlug/logs.

Auth: API key (Bearer) or session cookie

Query params

paramtypedescription
levelstringFilter by level. Comma-separated for multiple, e.g. error,warn
typestringFilter by type. Comma-separated for multiple
servicestringExact match on service
environmentstringExact match on environment
device_typestringExact match on device_type
user_idstringExact match on user_id
session_idstringExact match on session_id
qstringSubstring match against message
fromnumberEpoch ms, range start. Default: 24h ago
tonumberEpoch ms, range end. Default: now
limitnumberMax results. Default 50, capped at 200
offsetnumberPagination offset. Default 0

Response 200

{
  "results": [
    {
      "id": "evt_01hxyz",
      "project_id": "proj_01abc",
      "timestamp": 1720392000000,
      "level": "error",
      "type": "crash",
      "message": "Unhandled exception in checkout flow",
      "user_id": "user_42",
      "session_id": "sess_abc123",
      "environment": "production",
      "service": "checkout-api",
      "device_type": "ios",
      "metadata": {},
      "trace_id": "trace_01",
      "span_id": "span_01"
    }
  ],
  "total": 128,
  "searched_storage": "hot",
  "approximate": false
}

searched_storage indicates which tier(s) were queried: hot (D1, within the ~24h retention window), archive (R2, older than the window), or merged (the range spans both). approximate: true means the archive scan hit its 50,000-event cap, so the result set may be incomplete.

Note: user_id and session_id filters only apply to the hot (D1) tier — the R2 archive path doesn’t filter on those two fields, so a query whose range dips into archived data may include archived events that don’t match user_id/session_id.

GET /api/logs/:id

Fetch a single log event by ID. Also available at /api/orgs/:orgSlug/projects/:projectSlug/logs/:id.

Auth: API key (Bearer) or session cookie

Response 200 — a single event object, same shape as one item in results above.

Errors: 404{ "error": "Not found" }

GET /api/stats

Event counts grouped by level, type, and hour. Also available at /api/orgs/:orgSlug/projects/:projectSlug/stats.

Auth: API key (Bearer) or session cookie

Query params

paramtypedescription
fromnumberEpoch ms, range start. Default: 24h ago
tonumberEpoch ms, range end. Default: now

Response 200

{
  "stats": [
    { "level": "error", "type": "crash", "count": 12, "hour": 1720400400000 }
  ],
  "from": 1720392000000,
  "to": 1720478400000
}

hour is the start of the hour bucket, in epoch ms. When from predates the ~24h retention window, matching hours are computed from the R2 archive and merged with D1 results.

GET /api/facets

Distinct value counts for level, type, service, environment, and device_type. Also available at /api/orgs/:orgSlug/projects/:projectSlug/facets.

Auth: API key (Bearer) or session cookie

Query params

paramtypedescription
level, type, service, environment, device_typestringSame filter semantics as /api/logs; applied as cross-filters (see below)
qstringSubstring match against message
fromnumberEpoch ms, range start. Default: 24h ago
tonumberEpoch ms, range end. Default: now

Response 200

{
  "facets": {
    "level": [ { "value": "error", "count": 42 } ],
    "type": [ { "value": "crash", "count": 10 } ],
    "service": [ { "value": "checkout-api", "count": 30 } ],
    "environment": [ { "value": "production", "count": 55 } ],
    "device_type": [ { "value": "ios", "count": 20 } ]
  }
}

Each facet’s list is its top 10 values by count, computed with all other active filters applied but excluding that facet’s own filter (so, e.g., the level counts reflect the current service/environment/… filters but not the current level filter). When from predates the retention window, counts are blended with the R2 archive.

GET /api/orgs/:orgSlug/projects/:projectSlug/feature-health

Which features have received data recently, used to drive nudges on the dashboard overview. This endpoint has no plain /api/feature-health path — it’s only mounted org/project-scoped.

Auth: session cookie (or API key, resolved to your account’s org/project membership)

Response 200

{
  "alerts": true,
  "tracing": false,
  "deploys": true,
  "infrastructure": false,
  "sessions": true
}

Each field is a boolean presence flag, not a count:

fieldtrue when
alertsat least one enabled alert rule exists (no time window)
tracingat least one trace span recorded in the last 7 days
deploysat least one deploy event recorded in the last 30 days
infrastructureat least one metric data point recorded in the last 7 days
sessionsat least one session-type event recorded in the last 7 days

Filter values

Three endpoints return the distinct values seen for a field, for populating filter dropdowns. Each is also available org/project-scoped (/api/orgs/:orgSlug/projects/:projectSlug/...).

Auth: API key (Bearer) or session cookie

GET /api/services{ "services": ["checkout-api", "web-app"] }

GET /api/environments{ "environments": ["production", "staging"] }

GET /api/device-types{ "device_types": ["ios", "android", "web"] }

Lists are the distinct non-null values, sorted alphabetically, with no pagination.


Common responses (401; 402 and 429 don’t apply to these query endpoints) — see Authentication.