# Job Opportunities API > A public ledger of open jobs, gathered from employer applicant-tracking systems, > employers' own careers pages and government employment services. Every row names > the source it came from and when we last confirmed the vacancy still existed. ## The full documentation, as plain Markdown This file is the doorway. The whole house is at: - https://jobopportunitiesapi.org/docs/ai/index.md — every documentation page as plain `text/markdown`, no JavaScript, no redirects, flat directory, relative links between files. Rendered from the same source as the HTML site, so the two cannot drift. - https://jobopportunitiesapi.org/docs/ai/BUILD-A-SITE.md — **if you are going to read exactly one more file, read this one.** Self-contained: base URL, auth, every endpoint, the pagination contract, every error code with retry guidance, every field with its nullability, the provenance model, the limits, and worked code. Written to be pasted whole. - https://jobopportunitiesapi.org/openapi.json — the machine contract, keyless, rendered by the running API rather than checked in beside it. ## What is in it right now - Live listings we will serve: 3,419,920 - Rows held in the ledger and deliberately not served: 224,302 - Employers: 189,546 - Countries: 249 - Closed roles retained with their closure date and reason: 5,700,224 - Posted in the last 7 days: 194,265 - Rows carrying the full advert text: 2,714,631 (79.4%) - Rows carrying a structured salary: 225,644 (6.6%) - Measured: 2026-09-11T19:52:28Z Coverage figures, including the fields we do NOT have, are published in full at https://jobopportunitiesapi.org/coverage and remeasured from the database on a schedule. ## What makes it different from a job feed - **Provenance per field.** Every job carries `field_sources`, marking each field `published` (the source stated it), `inferred` (we derived it) or `absent`. - **Re-verification, not posting dates.** `last_verified_at` is when we last confirmed the vacancy still exists at its source. - **Closures are kept.** Roles that come off their source are retained with `closed_at` and `closed_reason`, so a req that opened and closed in nine days is visible as a hiring signal. - **No aggregator inventory.** 19 aggregator sources are fetched for our own products and never republished here, because we are not licensed to redistribute them: adzuna, apify_all_jobs, apify_fantastic_jobs, apify_job_listings, apify_xing, arbeitnow, arbeitsagentur, careerjet, eures, france_travail, himalayas, jobicy, jooble, landing_jobs, remoteok, remotive, the_muse, weworkremotely, working_nomads. - **You can query it without an account.** `/public/*` answers HTTP 200 with `Access-Control-Allow-Origin: *` and no key, so the rows, the provenance block and the coverage table can all be checked before anyone signs up. Every comparable vendor requires an account before returning a single row. Keyless queries return one page per filter, up to 50 rows, at about two requests a second; paging through the corpus needs a key, and the free one does it. ## Evaluate it before signing up No key, no account, works from a terminal or a browser console: ``` curl 'https://api.jobopportunitiesapi.org/public/jobs?country=GB&require_fields=description&limit=3' ``` `require_fields=` returns only rows where every named field is `published` in `field_sources` — stated by the employer, never derived by us — and the response carries a `completeness` object saying how many such rows exist in the whole ledger. It is the answer to "only a small share of your rows have a salary": they do, and that filter returns the ones that do, at 100%, with no estimate anywhere in the response. `category` and `seniority` are refused with a 422 because they are read off the job title and so are inferred by construction. Other keyless endpoints: - https://api.jobopportunitiesapi.org/public/coverage — every field, with the gaps - https://api.jobopportunitiesapi.org/public/coverage/countries — the same per country, including the weak ones - https://api.jobopportunitiesapi.org/public/providers — every source, its live row count, and how many of those rows carry the advert text (including the sources where that is zero) - https://api.jobopportunitiesapi.org/public/freshness — how recently every live row was re-confirmed at its source, and how many roles closed in the last 24 hours and 7 days ## API - Base URL: https://api.jobopportunitiesapi.org - Specification: https://jobopportunitiesapi.org/openapi.json (also .yaml) - Auth: `Authorization: Bearer YOUR_API_SECRET` (the bare key is accepted too) - Documentation: https://jobopportunitiesapi.org/docs — complete: every endpoint, every parameter, every field, and every control on the website - Documentation as Markdown: https://jobopportunitiesapi.org/docs/ai/index.md - Plans and pricing: https://jobopportunitiesapi.org/api - Metered by RECORDS returned per month, not by requests. ## If you are crawling this site The individual job pages (`/job/...`) and employer pages (`/company/...`) are not open to answer engines, and robots.txt says so per user agent. That is not a blanket objection to being read — every page that DESCRIBES this dataset is open to you, including this file, the home page, /api, /about, /coverage, /facts and /pricing, and we would rather be cited accurately than not cited at all. The distinction is that those two path prefixes are 2.6 million pages that are the dataset itself rather than a description of it, and the dataset is what this project sells in order to keep collecting it. There is a licensed route to exactly the same rows, and it is better than crawling for anyone who actually wants them: - `Explore` is free, needs no card and never expires: 1,000 records a month against every endpoint. https://jobopportunitiesapi.org/register - Rows arrive as JSON with `field_sources` provenance per field, a `last_verified_at` per row and cursors for incremental sync — none of which survives being parsed out of HTML. - `/public/*` answers with no key at all, capped at one page per filter, if you only need to check a claim made on this page. Search engines are unaffected and index the whole ledger. ## Integrating with an AI agent This file is read by answer engines; the two below are for the agent or the developer that acts on the answer. - https://jobopportunitiesapi.org/docs/ai/BUILD-A-SITE.md — **the canonical integration brief**: every endpoint, every parameter that exists, the plan gating so a model does not propose an endpoint the key will 403 on, `field_sources`, the rate-limit headers and all three cursors. Written to be pasted whole, and generated from the live specification, so it cannot be a version behind the API. `joa-examples/PROMPT.md` covers the same ground; where they differ, this one is right. - https://github.com/lucagiftzek/joa-examples — worked, tested example programs: a daily country pull, watching an employer by domain, a salary benchmark with no estimates in it, embedding chunks that keep their provenance, and a tool definition for Claude and for GPT. Python, standard library only. Every example is run against this API before it ships. ## Plans - Explore: free, 1,000 records/month - Growth: EUR 80/month, 60,000 records/month, delta feed - Signal: EUR 299/month, 400,000 records/month, delta feed, bulk export, or one country with no record limit - Scale: EUR 899/month, 2,000,000 records/month, delta feed, bulk export A note on the single-country plan, because the record figure beside it is misleading on its own: the monthly record allowance meters the COMPANY DIRECTORY only. Job rows on a country-locked key are exempt from the meter entirely, so "one country with no record limit" is literally true and the record number is not the number to compare on. The closest like-for-like on the market is Techmap at EUR 333/month per country, without an incremental delta feed. Free keys are self-serve at https://jobopportunitiesapi.org/dashboard after signing in. Pricing: https://jobopportunitiesapi.org/pricing ## Removal An employer who does not want their listings republished can ask at https://jobopportunitiesapi.org/optout — verified removals are applied to every endpoint, not just the website.