Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.
Get an API key
Free tier, no form to fill in. Signing in shares your email address with us — we
store it to create your key and to recognise you if you sign in with another
provider. See our Privacy Policy and
Terms.
openapi: 3.2.0
info:
description: 'Every listing here comes from an employer ATS, a company''s own careers page,
a public employment agency, or a direct submission. Redistribution is
enforced by a database JOIN, not a filter someone can forget.
Three things this API does that a job feed normally does not:
* **It says which fields it guessed.** Every job carries `field_sources`,
naming each field as `published` (carried by the source), `inferred`
(derived by us) or `absent`. 88.0% of live listings have an *inferred*
remote status; 11.3% have one the source actually stated. Filter with
`remote_confirmed=true` to get only the latter.
* **It says when each row was last verified.** `last_verified_at` is when we
last confirmed the vacancy still exists at its source, not when it was
posted. `GET /v1/meta/freshness` publishes the coverage numbers.
* **It keeps what closed.** `GET /v1/jobs/closed` returns roles that have
come off their source, with the reason and the date. A req that opened
and closed in nine days is a hiring signal, not a dead link. The date is
the day JOA''s daily verification confirmed the advert had gone; before
late September 2026 that verification ran in large catch-up batches, so
earlier closure dates show when JOA confirmed a closure, not when the
employer closed the role.
Changelog 2026-10-07: new contract fields `engagement`,
`contract_category`, `agency` and `working_schedule`, each with
`field_sources` provenance and filters (`?engagement=`,
`?contract_category=`, `?agency=`, `?working_schedule=`; absence token
`unknown`, alias `not_stated`). Values appear only when the posting or the
source states them (or, later, when an audited rule establishes them) —
most postings will show `unknown` at launch, and that is the honest answer,
not a defect. `contract_category=permanent` returns only postings that
STATE permanence. Per-field coverage is published on /public/coverage.
`employment_type` is unchanged and frozen.
Counts in this document are not typed in: they come from the daily
statistics snapshot every page of the website uses (GET /public/site-stats):
2,080,732 live employer-direct listings (employer_direct_live) and
129,261 employers as of 2026-10-20.'
summary: A verified ledger of open jobs, with per-field provenance and closure history.
title: Job Opportunities Jobs API
version: '2026-10-07'
servers:
- url: https://api.jobopportunitiesapi.org
security:
- bearerAuth: []
tags:
# --- truncated at 32 KB (80 KB total) ---
# Full source: https://artifacts.apis.io/jobopportunitiesapi-org/openapi/jobopportunitiesapi-org-jobs-api-openapi.yml