jobs

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.

get/v1/export

Request

  • Base URL: https://api.jobopportunitiesapi.org
  • URL: https://api.jobopportunitiesapi.org/v1/export
  • Auth: HTTP bearer

Query parameters

afterstring uuid

Resume cursor — the id of the last row you received.

status'live' | 'closed' | 'any'
include_descriptionboolean

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).

categorystring

Comma-separated. uncategorised selects rows with no confident classification.

familystring

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.

countrystring

Comma-separated ISO-3166 alpha-2.

remotestring

remote, hybrid, on_site, or not_stated.

senioritystring

Comma-separated; not_stated selects rows with none.

providerstring

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.

source_typestring
Example:ats

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.

companystring

Comma-separated company slugs.

posted_afterstring

Date or RFC3339 timestamp.

verified_afterstring

Only rows re-confirmed at their source since this instant.

has_salary'true' | 'structured' | 'any'

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.

has_descriptionboolean

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.