# API reference

> Ten read-only endpoints over the job index, and the conventions that apply to all of them.

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

The API is read-only. Every endpoint is a `GET` against
`https://api.hyperjobs.io`, returns JSON, and takes its input entirely from the
query string. There are no webhooks, no writes, and no batch endpoint.

## Endpoints

| Endpoint | | Returns |
| --- | --- | --- |
| [`/v1/jobs`](/docs/api-reference/jobs-list) | <Method>GET</Method> | Search the index with filters, paging, and collapsing. |
| [`/v1/jobs/count`](/docs/api-reference/jobs-count) | <Method>GET</Method> | Count matches for any `/v1/jobs` filter set, without fetching. |
| [`/v1/jobs/feed`](/docs/api-reference/jobs-feed) | <Method>GET</Method> | Newly indexed postings, ascending — for syncing. |
| [`/v1/jobs/expired`](/docs/api-reference/jobs-expired) | <Method>GET</Method> | Ids that expired since a cutoff — the purge feed. |
| [`/v1/jobs/modified`](/docs/api-reference/jobs-modified) | <Method>GET</Method> | Jobs whose tracked fields changed — the update feed. |
| [`/v1/jobs/{id}`](/docs/api-reference/jobs-retrieve) | <Method>GET</Method> | One job by id. |
| [`/v1/companies`](/docs/api-reference/companies-list) | <Method>GET</Method> | Search companies. |
| [`/v1/companies/{slug}`](/docs/api-reference/companies-retrieve) | <Method>GET</Method> | One company by slug. |
| [`/v1/meta`](/docs/api-reference/meta) | <Method>GET</Method> | Live dataset counts, source vocabulary, per-field coverage. |
| [`/v1/health`](/docs/api-reference/health) | <Method>GET</Method> | Liveness. No key needed. |
| [`/openapi.json`](https://api.hyperjobs.io/openapi.json) | <Method>GET</Method> | The generated OpenAPI 3.1 spec, served live. No key needed. |

## Authentication

Send your key as a bearer token:

```bash
curl -H "Authorization: Bearer $HYPERJOBS_KEY" https://api.hyperjobs.io/v1/meta
```

The `Bearer ` prefix is optional — a bare key in the header works. The key can
also go in the query string as `?api_key=`. If both are present, **the header
wins**.

[`/v1/health`](/docs/api-reference/health) and
[`/openapi.json`](https://api.hyperjobs.io/openapi.json) are the exceptions:
no key, and no rate limiting. Everything else returns
[`401`](/docs/errors#401-unauthorized) without a valid key. See
[authentication](/docs/authentication).

<Warning title="Validation is strict">
  Every parameter is validated on every endpoint. An unknown parameter, a bad
  or empty value, or a malformed cursor is a `400` that names the parameter
  and says what's allowed — `?limit=abc`, `?seniority=senior`, and
  `?time_frame=1week` are all rejected, not silently coerced or dropped.

  The upside: bad input can no longer masquerade as an empty result set. If
  you got a `200`, every filter you sent was applied.
  [Errors](/docs/errors#there-is-a-400-now) has the shapes.
</Warning>

## Status codes

| Status | Meaning | Retry? |
| --- | --- | --- |
| `200` | Success. | — |
| `400` | Unknown parameter, bad value, or malformed search syntax. | No — fix the request |
| `401` | Missing, unknown, or revoked key. | No — fix the key |
| `404` | Unknown job id, company slug, or endpoint path. | No |
| `405` | Non-`GET` method — the API is read-only. | No |
| `429` | Per-minute [rate limit](/docs/rate-limits) exceeded (retry after `Retry-After`) **or** monthly quota exhausted (no retry until reset). | Depends — check for `Retry-After` |
| `500` | Unhandled server error. | Yes, with backoff |

That's the complete list. Full shapes in [errors](/docs/errors).

## Response headers

Every response carries these headers worth knowing about:

| Header | Value |
| --- | --- |
| `Content-Type` | `application/json; charset=utf-8` |
| `X-Content-Type-Options` | `nosniff` |
| `Access-Control-Allow-Origin` | `*` |
| `X-RateLimit-Limit` | Your per-minute limit (authenticated responses). |
| `X-RateLimit-Remaining` | Requests left in the sliding 60s window. |
| `X-RateLimit-Reset` | Seconds until the window frees up. |

A per-minute `429` adds a standard `Retry-After` header (seconds); the
monthly-quota `429` doesn't. See
[rate limits](/docs/rate-limits#rate-limit-headers).

<Note>
  There is no `Cache-Control` and no `ETag` — conditional requests aren't
  supported. CORS preflight (`OPTIONS`) is handled, so the `Authorization`
  header works from browsers; whether a browser should hold your key at all is
  another matter — see
  [authentication](/docs/authentication#calling-from-a-browser).
</Note>

## Conventions

- **Versioning.** `/v1` is the only version. There's no version header and no
  dated pinning; additive changes ship in place and are noted in the
  [changelog](/docs/changelog).
- **Trailing slashes** are stripped — `/v1/jobs/` and `/v1/jobs` are the same
  route.
- **Sorting** isn't configurable. See [pagination](/docs/pagination#sorting).
- **Unknown paths** return `404` with `{"error": "unknown endpoint", "see": "…"}`.

## Reference pages

<CardGroup cols={2}>
  <Card title="List jobs" icon="Search" href="/docs/api-reference/jobs-list">
    The flagship endpoint. Every filter, with exact semantics.
  </Card>
  <Card title="Count jobs" icon="Calculator" href="/docs/api-reference/jobs-count">
    The same filters as the search, returning just the number.
  </Card>
  <Card title="Get the feed" icon="RefreshCw" href="/docs/api-reference/jobs-feed">
    Newly indexed postings, ascending. Excludes expired and duplicates.
  </Card>
  <Card title="Expired jobs" icon="Trash2" href="/docs/api-reference/jobs-expired">
    The purge feed — ids to delete from your mirror.
  </Card>
  <Card title="Modified jobs" icon="FilePen" href="/docs/api-reference/jobs-modified">
    Jobs whose tracked fields changed, with what changed.
  </Card>
  <Card title="Retrieve a job" icon="FileText" href="/docs/api-reference/jobs-retrieve">
    One job by id, returned bare rather than wrapped.
  </Card>
  <Card title="List companies" icon="Building2" href="/docs/api-reference/companies-list">
    Search the company dataset.
  </Card>
  <Card title="Retrieve a company" icon="Building" href="/docs/api-reference/companies-retrieve">
    One company by slug.
  </Card>
  <Card title="Dataset metadata" icon="ChartBar" href="/docs/api-reference/meta">
    Live counts and the exact `?source=` vocabulary.
  </Card>
</CardGroup>
