Skip to main content
You’ve made a call and gotten jobs back. Before you design the integration around it, five ideas determine almost every decision you’ll make: what identifies a job, which read path to use, how fresh the data is, how removals work, and what gets billed.

The job object and its id

Every endpoint that returns jobs — search, feed, company jobs — returns the same shape. id is a stable UUID assigned once and never reused, even as the rest of the posting changes underneath it. It’s the correct primary key for an upsert: write on id, and an edited posting updates your row instead of creating a duplicate. See The job object for the full field reference rather than restating it here.

Two ways to read jobs

Search and feed cover different jobs-to-be-done. Picking the wrong one for your use case is the most common integration mistake. If a person is reading the results on a screen, use search. If you’re mirroring the corpus into your own database, use the feed — it’s cheaper per job and, paired with a stable-scan cursor, immune to records shifting between pages as the underlying data changes mid-export. Deep page-based pagination over a moving dataset can skip or repeat records; cursor pagination can’t.
See Pagination for the mechanics of both models and Sync a database for the end-to-end pattern.

Freshness

Data refreshes continuously. For an incremental sync against the feed, 15–60 minutes is a healthy schedule — more frequent runs spend wallet credits without turning up much new data. Two timestamps matter, and they mean different things:
  • created_at — when Jobo first ingested the job.
  • updated_at — when the posting last changed.
Store updated_at per job and skip the write when the incoming value hasn’t moved. That turns a sync loop into a no-op for the vast majority of jobs on any given run.
created_at is exact ingestion time on the managed feed described below, under Global inventory vs. managed sources. On the main feed, it reports indexing time for jobs under 24 hours old, which can lag true ingestion slightly.

Expiry

POST /api/jobs/feed returns only active jobs. An expired job doesn’t come back with a flag set — it simply stops appearing. If you only ever call /feed, you will never see a removal.
Detect removals with a separate call: GET /api/jobs/expired — see Pagination for its cursor mechanics. It enforces a maximum 7-day lookbackexpired_since cannot be older than 7 days, and when omitted it defaults to just the last 24 hours. The consequence is mechanical: a sync that runs less often than weekly, or that stalls for more than 7 days, will silently accumulate dead rows that this endpoint can no longer help you find. There’s no way to ask “what expired 3 weeks ago.” Recovery is a full re-sync against /feed: page through the whole feed again, and treat any id still in your store that no longer appears as expired. See Sync a database for the sweep pattern and how to run it on the same schedule as your incremental sync so you never hit the 7-day wall.

Sources

Jobs are normalized from 106 ATS and job-board platforms into the one schema described above. source on every job is the platform’s provider_idgreenhouse, workday, lever, and so on. See Sources for the catalogue; it isn’t reproduced here because it changes as coverage expands.

Global inventory vs. managed sources

POST /api/jobs/feed draws from Jobo’s whole corpus — every job across all 106 sources. POST /api/jobs/feed/managed is a private, narrower feed scoped to a set of employers you nominate in the Jobo portal, rather than everything Jobo indexes. Same batch shape and cursor semantics as the main feed, different scope. See Sync a database for how the two interact in a sync loop.

Metering, briefly

Search and feed meter per delivered job, not per request — a page of 5 results costs less than a page of 100. Geocode is a flat per-lookup charge, refunded automatically on error. GET /api/jobs/{id}, GET /api/jobs/expired, and GET /api/companies/{id} are free. This page won’t restate prices or the request lifecycle — see Billing for both.

Developing without spending

There’s no separate sandbox host for the main API — everything above runs against https://connect.jobo.world. To build and test without running up a bill:
  • Call the free endpoints while you’re wiring up the integration: GET /api/jobs/{id}, GET /api/jobs/expired, GET /api/companies/{id}.
  • GET /api/companies/{id} needs no API key at all, so it’s the cheapest possible smoke test for connectivity.
  • When you do need search or feed, keep page_size and batch_size small — metering is per delivered job, so a page_size=5 request during development costs a fraction of a production-sized page.