# Overview

What the Job Opportunities API is, what it deliberately is not, and the ten minutes of reading that will save you the most time.

**What this covers:** What the Job Opportunities API is; What it is not; Every endpoint, in one table; What to read, in what order.

**Assumed knowledge:** Nothing. This is the first page.

**Canonical HTML:** https://jobopportunitiesapi.org/docs/overview  
**Machine-readable index:** https://jobopportunitiesapi.org/docs/ai/index.md  
**Last verified:** 2026-08-22  
**Superseded by:** the live API at https://api.jobopportunitiesapi.org and its spec at https://jobopportunitiesapi.org/openapi.json — where this file and the API disagree, the API is right.

---

This site documents one product completely: the public ledger at [jobopportunitiesapi.org](https://jobopportunitiesapi.org), the REST API behind it, and every control on the website. It is written so that a person can skim it and a program can read it, and there is a plain-Markdown copy of every page at [/docs/ai/index.md](./index.md) for anything that is not a browser.

> **You can start without an account** — Everything under `/public/*` answers with no key, no card and no sign-up, against the same rows the paid endpoints serve. Skip to [the quickstart](./quickstart.md) if you would rather read a response than a paragraph.

---

<a id="what-this-is"></a>

## 1. What the Job Opportunities API is

A ledger of job openings taken from employers' own career pages, their applicant tracking systems, and government feeds — readable free, licensed as an API.

### 1.1 In depth

Most job data products are aggregators: they collect postings from wherever they appear, including from each other, and sell you the union. That produces a large catalogue with an unknowable provenance — you cannot tell whether a row came from the employer, from a staffing agency reposting it, or from another aggregator that had already lost track of where it came from.

This one is built the other way round. A row enters the ledger only from a source we have classified and are permitted to redistribute: the employer's own careers page, the employer's ATS (Greenhouse, Lever, Workday, SmartRecruiters and the rest), or a public employment agency. Every row carries the source it came from, and every **field** on that row carries whether the source stated it or we derived it. That per-field provenance is the distinctive thing here and it is why the catalogue is smaller than an aggregator's.

The second distinctive thing is that the ledger keeps what it removes. A role that closes is not deleted — it is marked closed, with the date and the reason, and stays queryable. So the data answers questions about hiring over time, not only about hiring right now.

### 1.2 Exact contract

- **Transport** — HTTPS only. HTTP/2 and HTTP/3 at the edge. No websockets, no gRPC, no GraphQL.
- **Format** — JSON on every endpoint except `/v1/export`, which is NDJSON (one object per line).
- **Encoding** — UTF-8 throughout. Job titles and company names are not transliterated.
- **Character of the API** — Read-only. There is no endpoint on `/v1` that writes anything.
- **Versioning** — The path carries it: `/v1`. See [Versioning and change policy](./api-overview.md#versioning).
- **CORS** — `Access-Control-Allow-Origin: *` on the API, so browser code can call it directly.
- **Auth** — A bearer key on `/v1/*`. Nothing at all on `/public/*`.

The machine contract is [/openapi.json](https://jobopportunitiesapi.org/openapi.json) (and [/openapi.yaml](https://jobopportunitiesapi.org/openapi.yaml)). Both are keyless, both are served by the running API rather than checked in beside it, and every parameter and field table on this site is generated from them at build time. Where this site and the spec disagree, the spec is right and the difference is a bug in this site.

### 1.3 Worked examples

The whole product, in one keyless request

```console
# No key, no account. Three live rows from France.
$ curl -s 'https://api.jobopportunitiesapi.org/public/jobs?country=FR&limit=1'
{
  "data": [
    {
      "id": "d99f510e-bf39-46c6-8f8c-2b4ed8a79cb4",
      "slug": "directeur-de-creche-h-f-d99f510e",
      "title": "Directeur de Crèche H/F",
      "company": "babilou",
      "company_slug": "babilou",
      "company_logo": "https://supabase-erioun.tzekos.eu/storage/v1/object/public/company-logos/logos/babilou.png",
      "category": "Healthcare",
      "category_confidence": 0.98,
      "country": "FR",
      "city": "Lyon",
      "location": "Lyon",
      "remote_inferred": false,
      "seniority": "Director",
      "posted_at": "2026-09-11T04:03:44Z",
      "first_seen_at": "2026-09-11T04:10:46Z",
      "last_verified_at": "2026-09-11T04:50:03Z",
      "status": "live",
      "closed_at": null,
      "closed_reason": null,
      "apply_url": "https://babilou.wd3.myworkdayjobs.com/babilou/job/Lyon/Directeur-de-Crche-H-F_R0065908",
      "source": "workday",
      "source_type": "ats",
      "provider_type": "employer_ats",
      "has_description": false,
      "field_sources": {
        "remote": "absent",
        "employment_type": "absent",
        "category": "inferred",
        "seniority": "inferred",
        "salary": "absent",
        "location": "inferred",
… 11 more lines
```

_Real response, fetched from `/public/jobs?country=FR&limit=1` when this file was built (11 September 2026, 04:51 UTC)._

**See also**

- [What it is not](./overview.md#what-this-is-not)
- [The three populations](./ledger-data-model.md#ledger-model)
- [field_sources — per-field provenance](./ledger-provenance.md#field-sources)

<a id="what-this-is-not"></a>

## 2. What it is not

Not an aggregator, not a job board, not a salary benchmark, and not the largest catalogue available. Each of those is a deliberate loss.

### 2.1 In depth

Stating the losses plainly is the point. If one of these is what you need, another product will serve you better and finding that out on this page costs you five minutes instead of a procurement cycle.

- **Not an aggregator** — Rows from job aggregators are not merely deprioritised, they are excluded at the source level and never enter the served set. See [the excluded sources](./ledger-sourcing.md#excluded-sources) — they are named.
- **Not a job board** — There is no application flow, no candidate account, no CV store and no employer dashboard for managing a posting. `apply_url` points at the employer's own application page and that is where a candidate goes.
- **Not a compensation benchmark** — Salary is published only where the source published it, or where a real figure was quoted in the advert text and we normalised it. Modelled or estimated salaries never reach this API by any route — see [Salary](./api-fields.md#salary-fields).
- **Not the largest catalogue** — An aggregator that redistributes everything will always have more rows. The trade is that you can tell where each of these came from.
- **Not a real-time feed** — The ledger refreshes on a schedule, not on a webhook. See [Freshness](./ledger-coverage.md#freshness) for what that means in hours.
- **Not a person-level dataset** — There are no candidates, no applicants and no personal data in the ledger at all. A job row names a company, not a human.

### 2.2 Exact contract

The exclusion is implemented as a **join, not a filter**, which is a stronger guarantee than it sounds. `joa.providers.redistributable` defaults to `false` and the projection that builds the served set inner-joins on it. A provider added upstream tomorrow therefore cannot leak into the public API before a human has classified it — the default is exclusion, and there is no query parameter that overrides it.

Similarly, rows whose poster is a staffing agency or a job board are excluded by default from every listing endpoint. They can be re-admitted deliberately with `include_poster_type`, on a paid key, and they arrive labelled. What can never be re-admitted is a row we have judged to breach the employer-direct guarantee: no parameter value opts back into that. See [quality and withheld rows](./ledger-data-model.md#withheld).

**See also**

- [The sources we fetch and never republish](./ledger-sourcing.md#excluded-sources)
- [Withheld rows — held, not served](./ledger-data-model.md#withheld)
- [Staffing agencies and job boards](./ledger-sourcing.md#poster-type)

<a id="map-of-the-api"></a>

## 3. Every endpoint, in one table

Twelve keyed endpoints under /v1 and ten keyless ones under /public. This table is generated from the live specification, so it is never behind the API.

### 3.1 In depth

The split is the licence, not the capability. `/public/*` exists so that you can evaluate the data — and build small things with it — without an account: it serves the same rows, from the same projection, with a page-size cap and a modest rate limit. `/v1/*` is the licensed surface: bigger pages, the delta feed, bulk export, and a record allowance instead of a request throttle.

There is no endpoint on `/v1` whose data is unavailable on `/public` in some form; what `/public` does not give you is volume, the change feed, or an export. If you are deciding whether to buy, everything you need to decide is keyless.

### 3.2 Exact contract

| endpoint | key required | returns |
| --- | --- | --- |
| GET /v1/changes | yes | Delta feed — created, updated, withdrawn and delisted, in change order. |
| GET /v1/companies | yes | Employers whose live roles this API returns. |
| GET /v1/companies/{slug} | yes | One employer. |
| GET /v1/export | yes | Bulk export of the full corpus, as a stream of NDJSON. |
| GET /v1/jobs | yes | List listings, newest first. |
| GET /v1/jobs/{id} | yes | One listing, with its description. |
| GET /v1/jobs/closed | yes | Roles that have left their source, most recently closed first. |
| GET /v1/jobs/expired | yes | Ids of roles that have come off the ledger |
| GET /v1/me | yes | Your key, plan and quota. |
| GET /v1/meta/facets | yes | Every filter value with its live count. |
| GET /v1/meta/freshness | yes | How recently the ledger was verified, and how much of it is inferred. |
| GET /v1/meta/providers | yes | Every source the ledger is built from, with its live row count. |

The keyless mirrors are `/public/jobs`, `/public/jobs/{slug}`, `/public/companies`, `/public/companies/{slug}`, `/public/facets`, `/public/providers`, `/public/freshness`, `/public/coverage`, `/public/coverage/countries`, `/public/coverage/employers`, `/public/stats`, `/public/plans` and `/public/openapi.json`. They are documented in full on [the keyless endpoints page](./endpoints-public.md).

**See also**

- [What you get without an account](./api-keyless.md#keyless-what-you-get)
- [The header](./api-authentication.md#auth-bearer)
- [GET /v1/jobs](./endpoints-jobs.md#endpoint-jobs)

<a id="reading-order"></a>

## 4. What to read, in what order

Developers should read the API group first. Buyers should read the ledger group first. Programs should fetch /docs/ai/BUILD-A-SITE.md and nothing else.

### 4.1 In depth

1. **If you are integrating today** — [Quickstart](./quickstart.md), then [Authentication](./api-authentication.md), [Pagination](./api-pagination.md), [Errors](./api-errors.md). That is about fifteen minutes and it is the part that, skipped, costs the most later.
2. **If you are deciding whether to buy** — [What the ledger is](./ledger-what-joa-is.md), [Coverage and honesty](./ledger-coverage.md), then [Plans](./account-plans.md) and [the record meter](./account-record-meter.md). The meter is the most commonly misunderstood commercial mechanic here; read it before you compare prices.
3. **If you are a program** — [/docs/ai/BUILD-A-SITE.md](./BUILD-A-SITE.md). It is self-contained and needs no other file.
4. **If you are looking for one specific thing** — the search box at the top of the left-hand navigation searches every section title and summary on the site, and every section has a copyable deep link.

### 4.2 Exact contract

Three habits will save you the most time, and each of them is a section on this site rather than a line here: never parse `next_cursor`; never hard-code a filter vocabulary; and always read `field_sources` before you trust a field.

**See also**

- [next_cursor is opaque — this is the rule that bites](./api-pagination.md#cursor-opacity)
- [Controlled vocabularies — never hard-code these](./api-parameters.md#filter-vocabularies)
- [field_sources — per-field provenance](./ledger-provenance.md#field-sources)

**See also**

- [The three tiers](./conventions.md#how-to-read)
- [The machine-readable mirror](./for-agents.md#agents-start-here)

---

## Where to go next

This file is part of **Start here**. Others in the same group:

- [Quickstart](./quickstart.md) — From nothing to a real response in one command, and to an authenticated one in about a minute. No card at any point.
- [If you are a program](./for-agents.md) — This documentation has a plain-Markdown mirror with no JavaScript, no redirects and no browser required. Here is where it is and what is in it.
- [How to read this site](./conventions.md) — Three tiers on every concept, a copyable link on every section, and a rule about numbers that explains why almost nothing here is typed by hand.

Always useful:

- [index.md](./index.md) — the map of every file here
- [BUILD-A-SITE.md](./BUILD-A-SITE.md) — the paste-whole brief for building against this API
- [quickstart.md](./quickstart.md) — zero to a first authenticated response
- [api-errors.md](./api-errors.md) — every status code and whether to retry it
