External API

September 25, 2026 ยท View on GitHub

StatLite's external API is a small, read-only interface for scripts and other automation clients. The current contract is API v1. It reports one configured target's collection and source-reported health, recent collector events, and a fixed rolling hour of derived metrics. Requests do not start a poll or change stored data.

The dashboard's other /api/* routes are internal and are not part of this contract. Callers own their thresholds, schedules, deduplication, and notification delivery. This API does not implement alert rules or delivery.

For runnable one-shot checks with caller-owned thresholds, see the automation cookbook.

Access and target selection

All endpoints are GET requests. Bind StatLite to loopback or protect access with deployment-level network controls or a reverse proxy. StatLite does not provide API authentication; do not expose the listener to an untrusted network.

Each endpoint accepts an optional target query parameter. If exactly one target is configured, it is selected when target is omitted. With multiple targets, callers must name one. The value must exactly match a configured target name; an unknown name is an HTTP 400 error. Requests never fall back to the dashboard's selected or problem target.

Unknown parameters, repeated scalar parameters, and explicitly empty values are rejected with HTTP 400. Non-GET methods return HTTP 405 with Allow: GET. Query and target errors are client errors; unexpected storage/query failures return HTTP 500. Error bodies are plain text and should not be parsed as a versioned response shape.

Set values used in the examples below:

BASE_URL=http://127.0.0.1:9090
TARGET=my-app

GET /api/v1/status

Returns the current collection state and the latest available authoritative application and dependency health for the selected target.

FieldMeaning
targetConfigured target name.
typeCanonical integration type, such as spring, quarkus, or statlite-metrics.
collection_statusnot_polled before any poll, ok after a successful latest poll, or error when the latest poll failed.
last_poll_atCompletion time of the latest collection attempt, or null.
last_successful_poll_atCompletion time of the latest successful collection, or null.
last_failed_poll_atCompletion time of the latest failed collection, or null.
consecutive_collection_failuresNumber of consecutive failed collection attempts.
last_collection_errorLatest collection error summary, or null. Transport diagnostics containing an absolute URL are generalized to collection request failed.
application_healthApplication health string reported by the integration, or null when unavailable.
dependency_healthDependency or database health string reported by the integration, or null when unavailable.
health_observed_atCompletion time of the poll that supplied the health values, or null.

Application and dependency health are independent source values. StatLite does not normalize them to a universal status vocabulary: a negative value is different from null, which means no observation is available. If the latest poll reports either health value, both fields come from that poll and an unreported value is null. If the latest poll reports no health, the API may retain both values from the last successful poll; health_observed_at then shows their older observation time. Collection success is not application health.

Example:

curl -fsS --get "$BASE_URL/api/v1/status" \
  --data-urlencode "target=$TARGET" | jq .

The same request using only Python's standard library:

python3 - "$BASE_URL" "$TARGET" <<'PY'
import json
import sys
from urllib.parse import urlencode
from urllib.request import urlopen

base_url, target = sys.argv[1:]
url = base_url.rstrip("/") + "/api/v1/status?" + urlencode({"target": target})
with urlopen(url, timeout=5) as response:
    status = json.load(response)

print(
    f"{status['target']}: collection={status['collection_status']}; "
    f"application={status['application_health'] or 'unreported'}; "
    f"dependency={status['dependency_health'] or 'unreported'}"
)
PY

GET /api/v1/events

Returns { "target": "...", "events": [...] }, including an empty array when no events match. Results are newest first. Event objects contain:

FieldMeaning
timestampStart time of the poll that recorded the event, not a separate event occurrence time.
severityStored diagnostic severity, currently warning or error.
typeOpaque diagnostic type string from the collector or monitor. It is not a normalized, closed, cross-integration category list; avoid depending on specific values as a stable policy interface.
messageHuman-readable diagnostic text. A message containing an absolute HTTP(S) URL is projected to a generic request failure.
metric_keyOptional existing metric key affected by the diagnostic. Omitted when absent.

Query parameters:

ParameterBehavior
targetOptional when one target is configured; otherwise required.
range5m, 1h, or 24h; defaults to 1h.
limitPositive integer from 1 to 500; defaults to 100. The limit is applied in the storage query.

An invalid range or limit returns HTTP 400. Events are subject to the configured retention window.

Example, showing recent error-severity diagnostics:

curl -fsS --get "$BASE_URL/api/v1/events" \
  --data-urlencode "target=$TARGET" \
  --data-urlencode range=1h \
  --data-urlencode limit=100 |
  jq -r '.events[] | select(.severity == "error") |
    "\(.timestamp) \(.type): \(.message)"'

No output means this response contains no error-severity events. Event type and message text are diagnostics for people; callers should use status and metrics fields for machine decisions.

GET /api/v1/metrics

Returns a fixed rolling one-hour view evaluated at request time. It has no caller-selected range or bucket size. It is an operational summary, not a general metrics query or a historical analytics endpoint.

The response contains target, fixed range (1h), fixed bucket_seconds (60), request-time evaluated_at, nullable latest_http_observation_at, and points. Points are grouped into occupied UTC minute slots; empty slots are omitted. Each point's timestamp is the start of the minute containing its source poll time. A point can combine multiple poll observations. points is ordered by timestamp from oldest to newest.

The complete point fields are:

FieldMeaning
timestampStart of the occupied UTC minute slot.
requestsRequest-count delta total.
http_4xx4xx response delta total, including 404.
http_5xx5xx response delta total.
http_5xx_ratehttp_5xx / requests when requests are positive and every contributing request interval has a usable paired 5xx delta. This is a fraction from 0 to 1 when available.
avg_latency_msArithmetic mean request latency in milliseconds, weighted by requests whose request and duration counter baselines match.
latency_requestsNumber of requests contributing to avg_latency_ms; it may be lower than requests.
process_cpu_coresMean of available process CPU gauge samples, in CPU cores. It may exceed 1.
runtime_memory_bytesMean of available runtime-managed memory samples in bytes. Its meaning depends on the integration/runtime; it is not whole-process RSS or a cross-runtime comparable value.
host_cpu_usageMean of available host CPU usage fractions in [0, 1].
host_memory_usageMean of per-poll host memory usage fractions in [0, 1], derived from valid used/total pairs.
host_disk_usageMean of per-poll host disk usage fractions in [0, 1], derived from valid used/total pairs.

Metrics fields without a usable observation are JSON null; zero is a measured zero. Missing samples, counter resets, process restarts, and unmatched baselines do not become fabricated zeroes. Incomplete 4xx or 5xx counter coverage makes the corresponding count null. A 5xx rate requires complete paired 5xx coverage and positive request volume. At zero request volume, requests can be 0 while rate and latency values are null.

Latency can cover fewer requests than the bucket's request total. Consumers should check latency_requests before relying on the mean. A slot timestamp is approximate placement, not a claim that all activity happened during that minute. When a poll interval spans multiple minutes, including a collection gap, its whole derived delta is assigned to the end poll's minute; it is not split or prorated. Poll time is the stored poll start time.

latest_http_observation_at is the actual timestamp of the latest source poll in the hour that contributes a usable HTTP observation. It may advance from usable requests, latency, or a status delta paired with its request baseline. It remains null for resource-only data and unmatched status-only deltas. It does not guarantee freshness of every HTTP field: inspect the relevant point's nullable request, 5xx, rate, and latency-coverage fields, and apply caller-owned freshness and minimum-volume policy. The minute-slot timestamp can hide up to a minute of age. A newer resource-only point does not advance HTTP freshness.

Resource measurements are target-scoped. Application-target host values are included only when that target collected them. For a collocated deployment, select statlite-self separately to inspect the host or container environment visible to StatLite. Those values are not merged into an application's metrics response and do not describe a remote application's host. In containers, resource signals reflect the environment visible to StatLite, which may be the container rather than the physical host.

The one-hour public read materializes at most 4,096 in-window polls. If that bound is exceeded, the endpoint returns HTTP 500 instead of silently truncating the series. Retention is applied as for other stored history.

Example, inspecting the newest point with usable HTTP data and its freshness. A newer resource-only point may be present in the response:

curl -fsS --get "$BASE_URL/api/v1/metrics" \
  --data-urlencode "target=$TARGET" |
  jq ' . as $response
    | ([ $response.points[]
         | select(.requests != null or .avg_latency_ms != null
                  or .http_4xx != null or .http_5xx != null) ] | last // null) as $point
    | {
        target,
        evaluated_at,
        latest_http_observation_at,
        latest_http_point: (if $point == null then null else
          ($point | {timestamp, requests, http_4xx, http_5xx, http_5xx_rate,
                     avg_latency_ms, latency_requests})
        end)
      }'

Select statlite-self explicitly to inspect its host fractions:

curl -fsS --get "$BASE_URL/api/v1/metrics" \
  --data-urlencode target=statlite-self |
  jq '{target, points: [.points[] | {
    timestamp, host_cpu_usage, host_memory_usage, host_disk_usage
  }]} '

Treat absent points and null measurements as unavailable. Any thresholds, minimum traffic, schedules, and notification policy belong to the caller.

/healthz and target status

/healthz reports StatLite process and storage readiness, independently of monitored-target health. It returns a JSON object with status, version, timestamp, and nested storage.status. A SQLite readiness failure produces HTTP 503 and top-level status: "error"; monitored-target poll failures do not make /healthz fail. Use /api/v1/status to inspect a target's collection state and source-reported application/dependency health.

Contract scope

The API is read-only and intentionally narrow. It adds no authentication, alert rules, notification delivery, pagination, arbitrary filters, arbitrary metric queries, or historical series beyond the fixed one-hour metrics view. The dashboard's unversioned /api/* responses are not a compatibility contract. Future external API versions can be described in this stable docs/api.md reference without changing its URL.