- Stream active jobs —
POST /api/jobs/feed. Paginated stream of full job objects (active postings only). - Detect expired jobs —
GET /api/jobs/expired. Paginated stream of IDs that have expired in the last 7 days.
Both endpoints are included with a Feed subscription. Without a subscription,
/feed is metered by delivered jobs; /expired is unmetered so callers can
keep downstream inventories clean.Keeping your system in sync
A typical integration runs an initial backfill, then an incremental sync on a schedule. Useid as your primary key — it’s stable across updates, so syncs are simple upserts.
1. Initial backfill
Page through the entire feed once with cursor pagination. New scans use a stable scan by default (stable_scan: true): pages follow immutable creation time, so jobs updated or expired mid-backfill cannot shift records across page boundaries — no skips, no duplicates. Three rules make the backfill robust:
- Persist
next_cursorafter every successful batch so a crash resumes mid-stream rather than restarting from page 1. - Paginate serially — one in-flight request per cursor. Cursors are not designed for parallel fan-out.
- Send filters and
batch_sizeonly on the first request. The cursor embeds them; values sent alongside a cursor are ignored.
has_more is false.
2. Incremental sync
Run on a schedule (15–60 minutes is a healthy range — more frequent runs can add wallet usage without much fresh data). Each run:- Read your stored
last_run_started_at. Recordnowasthis_run_started_atbefore the first request. - Call
/feedwithupdated_after = last_run_started_at - 15m(a small overlap protects against clock skew and late-arriving postings).updated_afteralso catches edits to jobs you already hold, whichposted_afterwould miss. - Page through with
cursoruntilhas_moreisfalse. Upsert each job byid. - After the loop succeeds, persist
this_run_started_atas the newlast_run_started_at.
updated_at lets you detect re-published edits — store it and skip writes when it hasn’t changed.
3. Handling deletions
/feed only returns active jobs, so jobs that expire silently disappear. Sweep them with /expired on the same schedule as the incremental sync:
cursor, and mark every returned id as expired in your store.
4. Backoff & rate limits
- Configure a client response timeout of at least 120 seconds. The feed may
spend up to 90 seconds querying the search backend before returning a
retryable
503. - Retry only
503and429, with bounded backoff. On503, wait the seconds named inRetry-After(currently5) before retrying — the search backend’s circuit breaker reopens within ~30 s. - On
400 Invalid cursor, drop the cursor and restart pagination — don’t loop on the same value. - Watch
X-Credits-Balanceto alert before you run dry.
End-to-end examples
These samples implement the full workflow against a small key-valuestore. Replace store with your real database (Postgres upsert, etc.).
Managed jobs for your target companies
Your target companies. Fresh jobs. One feed. Use Managed Job Scraping when you need a private feed for a specific set of employers. Add the companies in the Jobo portal, then callPOST /api/jobs/feed/managed. Jobo discovers and verifies their career sites,
maintains collection, and delivers structured, deduplicated jobs through the
same batch shape, cursor semantics, and metering as the main feed. The result is
managed job collection that is white-label ready for your own product.
Things to know:
- Each job source resolves to one verified career-site identity. Add a separate source row when the same company recruits through another ATS; jobs from all linked source rows appear in the same private feed.
- Companies not in the Jobo catalog yet remain in setup and link automatically once Jobo verifies them; no re-import is needed.
- The
locationsfilter is not supported on the managed endpoint (usesources,work_models,posted_after,updated_after). created_atreflects the exact ingestion time; the main feed reports indexing time for jobs younger than 24 hours.- Requires a customer API key — sandbox and marketplace keys receive
403.
Endpoints
Full request/response reference and a live “Try it” playground live on the dedicated pages below.Stream active jobs
POST /api/jobs/feed — cursor-paginated stream of full job objects (active postings only).Stream managed jobs
POST /api/jobs/feed/managed — the same stream, scoped to your managed job sources.Detect expired jobs
GET /api/jobs/expired — paginated stream of IDs that have expired in the last 7 days.JobDto schema
Both feed endpoints (and the search endpoints) return job objects in this shape:Company object
object
Location object
object
An entry in the
locations[] array. A single posting may cover multiple cities/countries.Compensation object
object
Qualifications object
object
Structured qualifications split into must-have and preferred buckets.Each bucket contains:

