jobs

Delta feed — created, updated, withdrawn and delisted, in change order.

Poll this instead of re-reading the ledger. Pass since on the first call and the returned next_since from then on; it is a keyset cursor of <timestamp>|<uuid>, so no row is lost to a shared timestamp.

New: change: "withdrawn". A row can leave the served set without being closed by the employer — because we judged it no longer meets the employer-direct promise this API is sold on. Until now those rows simply stopped being mentioned, which meant a mirror built from this feed kept serving them indefinitely. withdrawn says so explicitly.

Treat it exactly as you treat delisted: stop serving the row. The difference is only in why. delisted means the vacancy is gone; withdrawn means the vacancy may well still exist, but we are no longer willing to stand behind the link — most often because it points at a government or aggregator portal rather than at the employer.

A withdrawn row keeps its id. If we later judge it servable again it returns as updated, so a mirror keyed on id needs no special handling.

Roles an employer pushes to us directly follow the same rule: pausing, unlisting or deleting one is withdrawn (its job carries no closed_at: it is not a closure), closing it or letting its deadline pass is delisted with closed_reason employer_closed or deadline_passed, and publishing it again is updated under the same id.

If your integration switches on change, add the new value before upgrading. An unknown-value branch that ignores it will keep serving rows we have withdrawn.

What updated means. updated is emitted when a job's visible content changed (title, description, company, location, salary, employment type, apply link, ...), when a closed job was re-opened, or when a withdrawn job was restored. Re-verifying that a job is still open does NOT produce an event: last_seen_at is a verification timestamp, not a change timestamp, and created / updated therefore no longer fire for every live job every day. A re-opened job keeps its original id and first_seen_at and arrives as updated, never as created: upsert on id.

Billing follows the same rule. Each event is one record, and since the content clock went live (18 Oct 2026) a verification heartbeat is not an event, so it is not billed. Content changes made before the clock went live were not recorded: if you need a baseline, page /v1/jobs once, then follow this feed.

get/v1/changes

Request

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

Query parameters

countrystring

Restrict the change feed to these upper-cased ISO country codes (comma-separated, up to 12, same semantics as /v1/jobs). A key country lock applies alongside this filter.

sincestring required
limitinteger

Response

A page of changes.

countinteger
next_sincestring

Changes

No recorded changes to this operation across all 1 revision of this API.