id, a stable UUID, as your primary key for upserts.
Fields
Datetimes are ISO 8601.
Null, empty, and omitted values
Every job carries every key in the table exceptexternal_id:
- Nullable fields are
null, never absent. Handlenullon every field marked Yes. - Array fields (
locations,responsibilities,benefits) are[]when empty, nevernull. external_idis omitted, notnull, when unavailable.
include_fields (see Shrinking the response). An excluded field keeps its key with an empty value:
The feed and
GET /api/jobs/{id} always return the full job.
External ID
external_id is the posting’s ID on the platform named by source. It’s returned whenever available, including on searches that request only core fields with include_fields: [].
Store it as a string: it can have leading zeros, letters or punctuation. It may differ from the employer’s requisition number and isn’t guaranteed to be globally unique, so keep id as your key for upserts and single-job lookups.
Description format
description is Markdown by default. Jobo converts the employer’s HTML: tags are stripped, line breaks kept, and lists, bold text and links become Markdown (- item, **bold**, [text](url)).
Render it as Markdown or show it as plain text. If you render the Markdown to HTML in a web UI, run the output through a sanitizer such as DOMPurify.
For HTML, set description_format to html (the default is markdown):
In the HTML, raw HTML from the source is escaped, and only
http(s), mailto and relative URLs are kept.
Enums
- Facet buckets are keyed by the filter value, so a facet key works as a filter.
- The workplace filter is
work_model(work_modelsin thePOST /api/jobs/searchand feed bodies); the job field isworkplace_type. There is nois_remoteparameter or field. - A filter on one of these fields leaves out jobs where the field is
null. The feed’sexclude_employment_types,exclude_work_modelsandexclude_experience_levelsare the exception: they keep jobs whose value is unknown.
Employment type
Workplace type
Experience level
Source
source is the provider_id of the platform the posting came from, one of 200+ listed on Source platforms. Unlike the enums, the value is the same on input and output: pass it to the sources filter, and facets.sources buckets use it.
New platforms are added regularly, so treat the set as open. Don’t hard-code it as a closed enum.
Nested objects
Company
A slim preview of the employer. For the full profile, followdetails_url or call GET /api/companies/{id}, which returns the company object.
Location
Compensation
Amounts are as the employer published them. Salary filters compare a USD-annualized value that Jobo derives from them; that value isn’t returned.
Occupation
A job has at most one occupation, and
occupation is null when the job isn’t classified. It describes the kind of work, not the posting: the employer’s title stays in title and normalized_title, and seniority in experience_level. “Senior Backend Engineer” and “Backend Engineer II” get the same occupation.
onet_soc_codes usually holds one code, several where O*NET splits the occupation, and [] when O*NET has no equivalent (as for jobo: occupations). The first seven characters of a code are its SOC 2018 code: 15-1252.00 is SOC 15-1252.
Search and POST /api/jobs/feed filter on the taxonomy:
GET /api/occupations lists every category, subcategory and occupation; Search recipes has examples. Occupations come from the ESCO classification and O*NET-SOC codes from the O*NET database; see Open data licenses and attribution.
Qualifications
qualifications always has two buckets, must_have and preferred, each with these fields:
Skill type
Complete example
A fully populated job object:Returned by
GET /api/jobsandPOST /api/jobs/search, in thejobsarrayGET /api/jobs/{id}, as the whole response bodyGET /api/companies/{id}/jobs, in thejobsarrayPOST /api/jobs/feed,POST /api/jobs/feed/managedandPOST /api/jobs/feed/{feed_id}, in thejobsarray

