# Health

> Liveness check for the API and the dataset behind it. One of two paths served without a key, and the only endpoint that is never rate-limited or metered.

Source: https://hyperjobs.io/docs/api-reference/health

<EndpointHeader method="GET" path="/v1/health" />

Is the API up and is the dataset behind it readable. One of two paths served
without a key — the other is `/openapi.json`, the generated spec — and the
only endpoint that isn't rate-limited.

<CodeGroup>
```bash title="cURL"
# No Authorization header. That's not an omission.
curl https://api.hyperjobs.io/v1/health
```

```js title="Node"
const res = await fetch("https://api.hyperjobs.io/v1/health");
const { ok, jobs, version } = await res.json();
```

```python title="Python"
import requests

res = requests.get("https://api.hyperjobs.io/v1/health", timeout=10)
healthy = res.status_code == 200 and res.json()["ok"]
```
</CodeGroup>

## Query parameters

None. Anything you send is ignored.

## Response

<Fields>
  <ResponseField name="ok" type="boolean">
    `true` when the API is serving. The endpoint doesn't return a `200` with
    `ok: false` — if the service is unhealthy you get a connection failure or a
    [`500`](/docs/errors#500-server-error), not a negative report.
  </ResponseField>
  <ResponseField name="jobs" type="integer">
    Live postings — non-duplicate and non-expired. The **same number**
    [`/v1/meta`](/docs/api-reference/meta) reports — see
    [below](#one-count-everywhere).
  </ResponseField>
  <ResponseField name="version" type="string">
    API version, e.g. `"1.0.0"`. Matches `version` on
    [`/v1/meta`](/docs/api-reference/meta).
  </ResponseField>
</Fields>

```json title="Response"
{ "ok": true, "jobs": 596768, "version": "1.0.0" }
```

That's the whole response. No component breakdown, no latency figures.

## No key required

This endpoint takes no [authentication](/docs/authentication). A key is ignored
if you send one — not rejected, just unused. It's also the only endpoint outside
the [rate limit](/docs/rate-limits): calls here don't count against your quota
and can't return a [`429`](/docs/errors#429-rate-limited), so you can poll it as
often as your monitoring needs.

`/openapi.json` — the generated OpenAPI 3.1 spec — is served without a key too,
so tooling can fetch the contract before it has credentials. Everything else
requires one.

<Note title="It can't validate an API key">
  Because it never reads one. A `200` from `/v1/health` tells you the API is up
  and says nothing about whether your key works — an expired, revoked, or
  misspelled key gets exactly the same response as a good one.

  To check a key, call an endpoint that requires one and look for a
  [`401`](/docs/errors#401-unauthorized).
  [Verifying a key](/docs/authentication#verifying-a-key) has the recipe;
  [`/v1/meta`](/docs/api-reference/meta) is the cheap choice.
</Note>

## Use it for liveness probes

This is what the endpoint is for. It's free, unauthenticated, and does no
filtering or sorting, which makes it the correct target for a health check,
a load-balancer probe, or an uptime monitor.

```bash
# Kubernetes, ECS, an uptime monitor — anything that polls on a schedule.
curl -fsS https://api.hyperjobs.io/v1/health
```

<Tip title="Don't probe with `/v1/jobs?limit=1`">
  It's the reflex, and it's the wrong endpoint on three counts: it needs a key,
  it spends [quota](/docs/rate-limits) on every probe, and it does the [full scan
  and sort](/docs/reference/coverage#query-performance) that every `/v1/jobs`
  query does — so a probe on a short interval burns real work to learn nothing
  that `/v1/health` wouldn't have told you for free.

  Probe here. Query `/v1/jobs` when you want jobs.
</Tip>

## One count, everywhere

`jobs` here is the live count — non-duplicate, non-expired — and it's the same
number [`/v1/meta`](/docs/api-reference/meta) reports. (In a pre-release draft
the two disagreed: health counted every row, duplicates and expired included,
and ran a few thousand higher.) A dashboard can quote this
number as "jobs available" without overstating.

A monitor that alerts on `jobs` dropping is reasonable — it going to zero or
falling sharply means something is wrong. For the source breakdown, per-field
coverage, and freshness, you still want [`/v1/meta`](/docs/api-reference/meta),
which needs a key.

<Note>
  For freshness rather than liveness, use `newest` from
  [`/v1/meta`](/docs/api-reference/meta). `/v1/health` will happily report `ok`
  on a dataset that stopped updating hours ago — it checks that the data is
  readable, not that it's current. The two alerts answer different questions and
  you want both.
</Note>

## Errors

| Status | Cause |
| --- | --- |
| `500` | Server error. |

No `401` — there's no key to be wrong. No `429` — the endpoint isn't rate-limited.
No `400` — there are no parameters to malform.

## Related

<CardGroup cols={2}>
  <Card title="Dataset metadata" icon="Activity" href="/docs/api-reference/meta">
    The same count plus freshness, sources, and coverage. Needs a key.
  </Card>
  <Card title="Authentication" icon="KeyRound" href="/docs/authentication">
    How to check that a key actually works.
  </Card>
  <Card title="Dataset coverage" icon="Database" href="/docs/reference/coverage">
    Per-field fill rates, and what drives each number.
  </Card>
  <Card title="Rate limits & quotas" icon="Gauge" href="/docs/rate-limits">
    What this endpoint is exempt from.
  </Card>
</CardGroup>
