The job object and its id
Search, the feeds, and company jobs all return the same job object. Its id is a stable UUID, assigned once and never reused. Upsert on id, and an edited posting updates your row instead of creating a duplicate.
external_id, the posting’s ID on its source ATS or job board, is not globally unique, so don’t key on it (External ID).
occupation places the job in a category › subcategory › occupation taxonomy, such as Healthcare › Nursing › Registered Nurse. It is null for unclassified jobs. Search can filter and facet on it; see Filter by occupation.
Two ways to read jobs
Page-based paging over a moving dataset can skip or repeat records; the feed’s cursor keeps records from shifting between pages mid-export.
curl for you.
Freshness
Jobo checks every tracked career site daily for new and expired listings. The checks are spread across the day, so new jobs arrive continuously rather than in one batch. Run an incremental feed sync every 15–60 minutes; more often usually returns nothing new.
For an incremental sync, send
updated_after set to when your last successful run started, minus 15 minutes: the feed returns jobs created, updated, or re-indexed since then (Incremental sync). Store updated_at per job and skip the write when it hasn’t moved.
Expiry
Detect removals withGET /api/jobs/expired. It is free and returns job IDs only:
A sync that runs less often than weekly, or stalls for more than 7 days, silently accumulates dead rows that this endpoint can no longer report. There’s no way to ask “what expired 3 weeks ago.”
Run the sweep on the same schedule as your incremental sync, with
expired_since set to the start of your last successful run; see Handling deletions. If you miss the window, recover with a full re-sync: page through the whole feed again and treat any stored id that no longer appears as expired.
Sources
source on every job is the platform’s provider_id, such as greenhouse, workday or lever. Sources lists all 200+ ATS and job-board platforms. New ones are added regularly, so treat source as an open set.
Global inventory vs. managed job sources
Three feeds share the same batch shape and cursor semantics and differ in scope:
A saved feed narrows its starting set by company and job conditions (occupation among them) and can exclude companies. See Managed job sources and Saved feeds for each feed’s filters and limits.
To have Jobo write the jobs into your own database or search index instead, set up an outbound feed under Data Platform → Outbound Feed. It delivers All jobs or one saved feed, with no filters of its own.
Metering, briefly
Search and the feeds bill per delivered job, not per request. A page of 5 results costs less than a page of 100, and an empty page costs nothing. Plans, the shared pool of included jobs, the Unlimited plan, and the free endpoints are on Billing.Developing without spending
Live and sandbox keys use the same host,https://connect.jobo.world; the key picks the environment.
- A sandbox key (
jbe_test_…) returns fictional jobs and companies in the real response shapes, never billed. It serves search, job and company lookups, and Auto Apply; the feeds, geocoding, and everything else need a live key. - With a live key, wire up against the free endpoints first.
GET /api/companies/{id}needs no API key, so it is the cheapest connectivity check. - When you need live search or feed data, keep
page_sizeandbatch_sizesmall: apage_size=5request bills at most 5 jobs.
Next steps
Sync a database
Backfill, then incremental sync with expiry sweeps.
Search recipes
Working filter combinations, facets, and field selection.
Sandbox
Fictional jobs in the real response shapes, never billed.
Client libraries
Official Python, Node.js, and .NET clients.

