Bulk export of the full corpus, as a stream of NDJSON.
One JSON object per line, not a JSON array: the stream can be processed as it arrives, and a dropped connection costs one line rather than the whole document. Requires a plan with bulk_export. Rows are ordered by id, so an interrupted transfer resumes exactly where it stopped — pass the last id you received as after. On a multi-gigabyte export that is not a rare need. Records are charged in batches as they are written, not at the end, so an export that is abandoned partway is still billed for what it delivered. If the monthly allowance runs out mid-stream, the last line of the body is an object carrying error: record_quota_exhausted and the after cursor to resume from. A consumer must therefore check the final line rather than assume a clean end of stream. Every filter from GET /v1/jobs applies. Personal contact details removed: when include_description=true, recruiter email addresses and phone numbers found inside the advert body are replaced with [contact removed — see apply_url] before the row is written. apply_url and every other field are unchanged. GET /v1/jobs and GET /v1/jobs/{id} apply a narrower rule instead: they remove only email addresses that identify a person (see the description field), keep company mailboxes such as jobs@, and leave phone numbers unchanged.
Request
- Base URL: https://api.jobopportunitiesapi.org
- URL: https://api.jobopportunitiesapi.org/v1/export
- Auth: HTTP bearer
Query parameters
Resume cursor — the id of the last row you received.
Return the full advert text in a description field. Off by default: descriptions averaged 2,581 bytes (measured 5 Aug 2026), so a 200-row page would be a ~516 KB response nobody asked for. With this on, limit may not exceed 50 — a larger request is refused with 422 rather than quietly clamped. Personal email addresses in the text are replaced with [personal contact removed]; company mailboxes and phone numbers are kept (see the description field).
Comma-separated. uncategorised selects rows with no confident classification.
Alias for category (the facet key in /v1/meta/facets is named family; the filter parameter was category). Both names are accepted everywhere category is.
Comma-separated ISO-3166 alpha-2.
remote, hybrid, on_site, or not_stated.
Comma-separated; not_stated selects rows with none.
Comma-separated list of the exact source systems to include, e.g. greenhouse,lever,workday. This is the ATS or board a vacancy came from, finer than source_type which buckets them. A name we do not publish is a 422, never a silently empty page. Up to 12 values.
Comma-separated provenance filter. The legacy spellings employer_ats, government and direct are still accepted and map onto ats, public_agency and career_site.
Filter values are matched case-insensitively: category=engineering and category=Engineering are the same query. Enumerate the legal values with /v1/meta/facets.
Comma-separated company slugs.
Date or RFC3339 timestamp.
Only rows re-confirmed at their source since this instant.
true (and structured) returns only rows whose salary the SOURCE published — the meaning this parameter has always had, kept so that adding derived salaries does not change the results of a query you already ship. any also includes figures we read out of the advert text (salary_source: parsed_description, reported as inferred). AI estimates are never published under any value.
true returns only the 1,861,942 live rows (94.3%) that carry a description.
Response
A stream of listings, one JSON object per line.
Changes
No recorded changes to this operation across all 1 revision of this API.