Job Opportunities API

Check the data. Then trust it.

start

If you are a program

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.

Last verified 2026-08-22 · Assumes: That you fetch URLs and read text. Nothing about JOA. · Markdown copy

1The machine-readable mirror

Every page here also exists as Markdown under /docs/ai/, served as text/markdown, rendered from the same source as the HTML so the two cannot drift.

In depthWhy it exists, what it is not, what people get wrong

There are two audiences for this documentation and only one set of words. The HTML pages and the Markdown files are rendered from the same content modules in the site's source, so a paragraph added to one is added to both, in the same commit, or the build fails. A hand-maintained second copy would have diverged within a fortnight; this one cannot.

The directory is flat and one level deep. Files cross-link with relative names (./api-errors.md), so a fetcher can follow them mechanically without resolving anything. Every file opens with what it covers and what it assumes, and closes with the three to six files most likely wanted next — because the realistic case is that you fetch exactly one.

Exact contractTypes, defaults, ranges, errors, edge cases
Entry point
https://jobopportunitiesapi.org/docs/ai/index.md — a map of every file with a one-line summary of every section.
Content type
text/markdown; charset=utf-8.
Caching
public, max-age=900 — the figures inside are re-rendered from the live endpoints on that cycle.
Redirects
None. Every URL under /docs/ai/ is terminal.
JavaScript
None. The bytes you receive are the whole document.
Robots
Allowed for search engines, answer engines and unfurlers alike. The docs are a selling page, not the corpus.
Freshness
Each file carries a Last verified: date and names the endpoint that supersedes it.

The same content is in the HTML, too. The expandable tiers on every page are native <details> elements whose contents are present in the served HTML — they are not fetched on expansion. So an agent parsing the HTML receives the deep technical tier without executing anything or clicking anything.

Fetch the map
$ curl -s https://jobopportunitiesapi.org/docs/ai/index.md | head -40
# Job Opportunities API — machine-readable documentation

Every page of https://jobopportunitiesapi.org/docs, as plain Markdown, served as
`text/markdown; charset=utf-8` with no JavaScript and no redirects. …
Truncated. The real file lists every page and every section on the site.

2What is in the mirror

One file per documentation page, plus an index, a paste-whole build brief, and a dated changelog so you can tell how old your copy is.

In depthWhy it exists, what it is not, what people get wrong
FileWhat it is for
index.mdThe map. Every file, every section, each with its one-line summary.
BUILD-A-SITE.mdSelf-contained brief: everything needed to build a working integration, in one file.
CHANGELOG.mdDated changes to this documentation.
quickstart.mdZero to a first authenticated response.
api-authentication.mdKeys, the tolerant header, keyless access, rotation.
api-rate-limits.mdPer-plan limits, keyless limits, headers, backoff.
api-pagination.mdCursors, the opaque-token rule, resumable pulls.
api-errors.mdEvery status code, its JSON shape, and whether to retry.
api-parameters.mdThe full parameter reference, generated from the OpenAPI spec.
api-fields.mdEvery field on a job and a company, with type and nullability.
ledger-data-model.mdlive + withheld + closed, and how they reconcile.
ledger-provenance.mdfield_sources, require_fields, and the deliberate 422.
endpoints-*.mdOne file per endpoint family, with the full parameter table and worked calls.
recipes-*.mdRunnable programs: daily pulls, incremental sync, bulk export, enrichment.
Exact contractTypes, defaults, ranges, errors, edge cases

File names are the page slug with / replaced by -, plus .md. So the HTML page /docs/api/parameters is /docs/ai/api-parameters.md. That mapping is mechanical in both directions and is stated in index.md as well, so you do not have to infer it.

Beyond this documentation there are three other machine surfaces worth knowing: /llms.txt is the short doorway that points here; /openapi.json and /openapi.yaml are the authoritative machine contract, keyless and rendered from the running API; and GET /public/ai-prompt returns an integration brief straight from the API host with no website involved.

3The rules that matter most to a program

Seven behaviours that separate an integration that works from one that looks like it works. Each is stated once, here, in the imperative.

In depthWhy it exists, what it is not, what people get wrong
  1. Use `https://api.jobopportunitiesapi.org`. The website host is edge-protected and may answer a non-browser client with a challenge, which reads as a 403.
  2. *Send `Authorization: Bearer <key>` on `/v1/. Send nothing on /public/`* — a key there is neither required nor useful.
  3. Pass `next_cursor` back verbatim. Never parse it, never rebuild it, never assume its shape.
  4. Read `field_sources` before you trust a field. published is the source's word; inferred is ours; absent means there is none.
  5. Do not retry a 402. It means the month's record allowance is spent. Retry a 429 after Retry-After seconds.
  6. Enumerate vocabularies at run time from /public/facets and /public/providers. An unknown value is a 422, not an empty page.
  7. Key company identity on `company_domain` where you have it. Slugs are not stable, and a retired slug now 301s to its replacement — every slug a company has ever held keeps resolving. Follow redirects (curl -L). Treat a 404 on a slug that used to work as “re-resolve”, not “gone”.
Exact contractTypes, defaults, ranges, errors, edge cases

Two more that only show up at scale. include_description=true caps limit at 50 and is refused with a 422 above that rather than silently clamped — descriptions average about 2.5 KB, so a 200-row page would be a half-megabyte response. And /v1/export charges records as it writes them, so an abandoned export is still billed for what it delivered; if the allowance runs out mid-stream the last line of the body is an error object carrying the resume cursor, which means a consumer must check the final line rather than assume a clean end of stream.

4The paste-whole brief

BUILD-A-SITE.md is the canonical integration brief. If you are being asked to build something against this API, that file is the whole answer.

In depthWhy it exists, what it is not, what people get wrong

It holds, in one document: the base URL and auth header; the endpoint list with what each returns; the pagination contract; the complete error table with retry guidance; the job and company field lists with types and nullability; the provenance model; the rate and record limits; and worked code in curl, Python and JavaScript that has been run against the live API.

Exact contractTypes, defaults, ranges, errors, edge cases
Everything a model needs, in two fetches
curl -s https://jobopportunitiesapi.org/docs/ai/BUILD-A-SITE.md > joa-brief.md
curl -s https://jobopportunitiesapi.org/openapi.json      > joa-openapi.json

The spec is worth fetching alongside the brief because it is the contract, it is machine-validated, and it carries the parameter prose in full — including the long explanations of require_fields, has_salary and include_poster_type that no summary preserves.

This page was rendered 11 September 2026, 03:39 UTC. Every figure on it comes from the endpoint named beside it, and every published request is re-sent against the live API before this site is allowed to build. If something here is wrong, the thumbs-down above reaches a person.