Skip to main content
mc.status.health() is a starter / sanity check — confirm Minerva is reachable, see if the API is currently healthy, debug “is my key wrong or is the service down?”. The same method calls one of two endpoints depending on whether the client has an API key configured:
Client stateEndpoint hitAuthWhat you get back
No API keyGET /healthnoneLiveness: ok, latency_ms, status_code, status, message
API key setGET /health/metricsx-api-keySame fields plus refreshed_at (server-side aggregation timestamp). The message rolls up to text like "all systems normal" or "elevated latency on 2 endpoint(s)" — counts, not per-route numerics.
The method never raises on probe failure. Each endpoint’s outcome lives in its HealthStatus (ok=False with error set), so it’s safe to call in a polling loop without try/except.
from minerva import Minerva

mc = Minerva()                            # MINERVA_API_KEY from env
report = mc.status.health()               # auto-picks /health or /health/metrics

for name, h in report.items():
    if not h.ok:
        alert(f"{name} {h.status}: {h.message} (HTTP {h.status_code})")
    else:
        log(f"{name} ok — {h.message} ({h.latency_ms:.0f} ms client-perceived)")

When (and when NOT) to call it

Use as a checkpoint, not a per-call gate

Every mc.status.health() is a real HTTP round-trip. Gating every mc.api.enrich(...) on a health probe doubles your latency for no useful signal — the next mc.api.* call itself raises MinervaAPIError if something’s wrong.
Good use cases
  • A dashboard widget polled every 30-60s
  • A monitor / synthetic that fires every few minutes
  • A one-shot call at process startup
  • Debugging “is the API down or is my key wrong?” (works without a key)
Avoid
  • Wrapping every mc.api.enrich(...) / mc.api.resolve(...) in a health check
  • Tight loops — the authed endpoint’s data is up to ~60 s old by design; polling faster doesn’t get you fresher numbers
  • Treating it as a substitute for client-side error handling

Auto-upgrade behavior

When the client has an API key, health() automatically hits the authenticated /health/metrics endpoint and returns the richer payload. You can override either way:
mc.status.health()                # auto: /health if no key, /health/metrics if key
mc.status.health(detailed=False)  # force /health (basic, skip the metrics overhead)
mc.status.health(detailed=True)   # force /health/metrics (raises MinervaAuthError if no key)

Return shape

Always a dict[str, HealthStatus] keyed by endpoint name. One entry today ("api"); the dict grows as Minerva exposes more endpoints. Iterating today’s code keeps working as endpoints come online.

HealthStatus

ok
bool
True when the endpoint is reachable and the gateway reports healthy (HTTP 2xx and status == "ok" in the body, when present).
latency_ms
float
Client-perceived round-trip time, in milliseconds.
status_code
int | None
HTTP status code returned by the endpoint. None on network-level failure (DNS, TCP, TLS, timeout).
status
str | None
Value of the status field in the gateway’s response body, when present (e.g. "ok", "degraded", "warming"). None if absent.
message
str | None
Human-readable detail. On the unauthenticated endpoint, often None or a short free-text note from the gateway. On the authenticated endpoint, rolls up to phrases like "all systems normal" or "elevated latency on 2 endpoint(s)"counts only, no per-route numerics.
refreshed_at
int | None
Unix timestamp of the last server-side aggregation (authenticated endpoint only). Data can be up to ~60 s old by design.
error
str | None
SDK-side reason for ok=False — the HTTP code (e.g. "HTTP 503") or the underlying network exception class (e.g. "ConnectError: ..."). None when the probe succeeded. Distinct from message: error is the SDK’s verdict; message is whatever the gateway returned.

Parameters

endpoints
str | list[str] | None
Which endpoint(s) to probe. Omit (or pass None) to probe every known endpoint. Pass a single name or a list to filter. Today the only known name is "api"; unknown names raise ValueError.
timeout
float
default:"5.0"
Per-endpoint timeout in seconds. Default 5 — for a liveness check, “no answer in 5 seconds” is itself a useful answer.
detailed
bool | None
None (default) auto-picks the endpoint based on API-key presence. True forces /health/metrics (raises MinervaAuthError if no key). False forces /health (skip the metrics call even when a key is configured).

Examples

Probe everything (default):
mc.status.health()
# without API key  → {"api": HealthStatus(ok=True, status="ok", refreshed_at=None, ...)}
# with API key     → {"api": HealthStatus(ok=True, status="ok", message="all systems normal", refreshed_at=1780617600, ...)}
Force the basic call (skip the metrics overhead):
mc.status.health(detailed=False)
Polling loop in a dashboard:
import time
from minerva import Minerva

mc = Minerva()
while True:
    for name, h in mc.status.health().items():
        if not h.ok:
            page_oncall(f"Minerva {name} {h.status}: {h.message}")
    time.sleep(30)
Prometheus exporter:
from prometheus_client import Gauge

up = Gauge("minerva_endpoint_up", "1 if up", ["endpoint"])
latency = Gauge("minerva_endpoint_latency_ms", "Probe latency", ["endpoint"])

def collect():
    for name, h in mc.status.health().items():
        up.labels(endpoint=name).set(1 if h.ok else 0)
        latency.labels(endpoint=name).set(h.latency_ms)
Distinguish “API down” from “key invalid”:
report = mc.status.health(detailed=False)   # no key needed; hits /health
if not report["api"].ok:
    print("API is down — try again later")
else:
    try:
        mc.api.enrich([{"record_id": "1", "linkedin_url": "..."}])
    except MinervaAuthError:
        print("API is fine — your key is the problem")

What the authed view tells you

The /health/metrics payload is intentionally minimal: an overall status + a generic message + a refreshed_at timestamp. By design it does not expose:
  • Per-route latency or error-rate numbers
  • Internal service / Lambda names
  • Endpoints you’re not entitled to use
Engineers needing the raw view query their observability stack (DataDog / Grafana) directly. The SDK’s view is a quick “is everything behaving?” signal.

Authentication

GET /health is unauthenticated. GET /health/metrics requires the same x-api-key you’d use for any data-API call. Both run on api.minerva.io so there’s no DNS or TLS overhead beyond the data-plane connection your client already uses.

Errors

health() never raises for probe failures — the outcome is encoded in the returned HealthStatus. The only things that raise are misuses of the call itself:
ConditionRaises
Unknown name in endpoints=ValueError
detailed=True but no API key configuredMinervaAuthError
Bad timeout= (e.g. negative)TypeError / ValueError (from httpx)