Data reference / Enums & vocabularies

Enums & vocabularies

Every fixed value the API accepts and returns, with live counts and the filter each one belongs to.

The complete vocabularies behind the enum filters on /v1/jobs. If a filter just returned a 400, the hint in the body names the values on this page.

A wrong value is a 400 now, not an empty list

Enum filters — seniority, work_arrangement, experience_level, employment_type, education, taxonomies, continent — are still case-sensitive equality, but a typo, a wrong case, or a value from the wrong vocabulary is rejected with a 400 whose hint lists the allowed values. A typo'd filter can never masquerade as an empty result.

BASH
curl "…/v1/jobs?seniority=Mid-Senior level"   # ✓ 198,375 jobs
curl "…/v1/jobs?seniority=mid-senior level"   # ✗ 400 — wrong case
curl "…/v1/jobs?seniority=Senior"             # ✗ 400 — not a value
curl "…/v1/jobs?seniority=5-10"               # ✗ 400 — that's experience_level

The exceptions are the open vocabularies — skill, source, language, and city aren't enums, so an unknown value there still matches nothing rather than erroring.

Counts below are from a recent snapshot. They're here to show you the shape of each distribution — don't hardcode them. For a live count of any value, send the filter with limit=1 and read total:

BASH
# The live number of Director-level jobs, without transferring any of them.
curl -G https://api.hyperjobs.io/v1/jobs \
  -H "Authorization: Bearer $HYPERJOBS_KEY" \
  --data-urlencode "seniority=Director" --data-urlencode "limit=1"
# → { "data": [ … ], "total": 26465, "limit": 1, "offset": 0 }

/v1/meta gives live totals for jobs, companies, the source breakdown, and per-field fill rates (coverage_pct) — but no per-enum counts for the vocabularies below.

Quick reference#

FilterVocabularyMatching
seniority6 valuesExact, case-sensitive · comma = OR
experience_level4 valuesExact, case-sensitive · comma = OR
work_arrangement3 valuesExact, case-sensitive · comma = OR
employment_type8 valuesToken in a list, comma = OR
education5 valuesToken in a list, comma = OR (overlap)
taxonomies28 valuesToken in a list, comma = OR (overlap)
continent7 valuesExact, case-sensitive · comma = OR
visa_sponsorship2 valuesSingle value
source64 live slugs, live via /v1/metaExact, case-sensitive · comma = OR
languageFull name or ISO-639-1Exact · comma = OR
skillOpenToken in a list, comma = AND — the exception

Comma = OR on every multi-value filter, with one exception: skill, where every listed value is required. That's the one inversion to keep straight — see below. country and company_country aren't enums but take a full name or an ISO-2 code, comma = OR — ?country=US,Germany works, and an unrecognised ISO-2 code is a 400.

seniority#

Six values. Filter: ?seniority=. Field: job.seniority.

ValueJobs
Mid-Senior level198,375
Associate99,023
Entry level78,095
Management28,749
Director26,465
Internship11,603

Note the shape: Mid-Senior level is nearly half of everything classified, so it's a weak filter on its own. Management and Director are distinct values, not synonyms — a query for leadership roles needs both, which is one request: ?seniority=Director,Management (comma = OR).

experience_level#

Four bands, in years. Filter: ?experience_level=. Field: job.experience_level.

ValueJobs
5-10170,565
2-5112,024
0-2100,549
10+42,798

The values are literal strings — ?experience_level=5-10, not 5 and not 5–10 with an en-dash. There's no range syntax and no numeric comparison.

`experience_level` and `seniority` are separate fields

They're related but derived independently, and a posting can carry a combination that looks inconsistent. The rough correspondence is:

experience_levelAligns with
0-2Entry level
2-5Associate
5-10Mid-Senior level
10+Mid-Senior level

Both bands above 5 years map to the same seniority, so seniority cannot distinguish a 6-year role from a 15-year one — use experience_level when that difference matters. And because neither field is computed from the other at read time, don't filter on both expecting a narrower result: you'll get the intersection of two independent judgements, which is often much smaller than either alone.

work_arrangement#

Three values. Filter: ?work_arrangement=. Field: job.work_arrangement.

ValueJobs
On-site333,870
Remote127,895
Hybrid73,706

On-site carries a hyphen and a lower-case s. Onsite, On-Site, and onsite all return zero rows.

`work_arrangement=Remote` and `remote=true` are different filters

They read different fields and return different sets — 127,895 postings have work_arrangement=Remote, while 85,069 have location.remote true. Neither number is wrong; they're separate signals derived from separate evidence.

  • ?work_arrangement=Remote is the classification. It's the more precise of the two, and the only one that can express Hybrid.
  • ?remote= filters on location.remote. It takes true/false/1/0false means on-site/hybrid only — and anything else is a 400 (a pre-release draft silently ignored remote=true; that trap never shipped).

Prefer work_arrangement. Use both only if you want the stricter intersection.

continent#

Seven values. Filter: ?continent=, comma = OR. Field: job.location.continent.

Africa · Antarctica · Asia · Europe · North America · Oceania · South America

Title Case, with the space in the two-word names — North America, not north-america. A value outside the seven is a 400 naming the list.

employment_type#

Filter: ?employment_type=. Field: job.employment_type.

ValueJobs
FULL_TIME429,457
PART_TIMEQuery total
CONTRACTORQuery total
INTERNQuery total
TEMPORARYQuery total
PER_DIEMQuery total
VOLUNTEERQuery total
OTHERQuery total

FULL_TIME dominates so heavily that the rest are a rounding error by comparison; for a current count of any one of them, send it with limit=1 and read total.

SCREAMING_SNAKE_CASE, always. Full-time, full_time, and FULLTIME all return zero rows.

It's an array, not a value#

A posting can carry several types at once, so the field holds a list["FULL_TIME","TEMPORARY"] for a full-time contract role, and 20+ combinations occur in the data. The filter matches a value inside that list, so ?employment_type=FULL_TIME finds both ["FULL_TIME"] and ["FULL_TIME","TEMPORARY"] — and ?employment_type=FULL_TIME,CONTRACTOR matches either type (comma = OR).

A real array

A pre-release draft of this API returned this field as a raw JSON string — "[\"FULL_TIME\"]" — and you had to JSON.parse it. It's a real array, like skills and taxonomies beside it:

JS
job.employment_type.includes("FULL_TIME");     // ✓ — just an array

If you shipped a defensive parse, delete it. The same applies to education_requirements and benefits. See the Job object.

education_requirements#

Filter: ?education= — note the parameter is education, not education_requirements, though the field it filters is job.education_requirements.

ValueJobs
bachelor degree49,367
high school30,156
postgraduate degree22,749
professional certificate5,318
associate degree4,053

All lower-case, all with a space, none pluralised. Bachelor degree, bachelors degree, and Bachelor's are a 400 naming the five values. Comma = OR (overlap match): ?education=bachelor degree,postgraduate degree matches postings listing either.

Also an array field — a real array, not a raw JSON string — and also sparse: the five values together cover well under a third of the dataset, because most postings state no education requirement at all. A null means the posting was silent, not that no degree is required, so filtering on education excludes the large majority of jobs that simply didn't say.

taxonomies#

28 job families. Filter: ?taxonomies=. Field: job.taxonomies. Title Case, with spaced ampersands in the compound names.

Administrative · Agriculture · Art & Design · Construction · Consulting · Creative & Media · Customer Service & Support · Data & Analytics · Education · Energy · Engineering · Finance & Accounting · Healthcare · Hospitality · Human Resources · Legal · Logistics · Management & Leadership · Manufacturing · Marketing · Sales · Science & Research · Security & Safety · Social Services · Software · Technology · Trades · Transportation

Per-taxonomy counts aren't published anywhere. For the distribution, query each value with limit=1 and read total — 28 cheap requests, well within a minute's rate limit.

Present on about 64% of postings — a job with no taxonomy isn't uncategorisable, it just wasn't classified.

The & needs URL-encoding as %26, or the query string ends at the ampersand and the rest of your filters vanish:

BASH
# The shell and the URL both need to leave `&` alone. --data-urlencode does both.
curl -G https://api.hyperjobs.io/v1/jobs \
  -H "Authorization: Bearer $HYPERJOBS_KEY" \
  --data-urlencode "taxonomies=Finance & Accounting"

`Software`, `Technology`, and `Engineering` are three separate values

They overlap in ordinary usage but not here, and none implies another. A backend role might carry ["Software"], ["Software","Technology"], or ["Engineering"] depending on how the posting reads.

Since commas mean OR, ?taxonomies=Software,Technology,Engineering covers all three in one request — an overlap match: any listed value qualifies. And a value outside the 28 is a 400 naming the list, so a typo can't silently empty the result. Skills & taxonomies works through it.

visa_sponsorship#

Two values. Filter: ?visa_sponsorship=. A single value, not a comma list.

ValueMeaning
onlyOnly jobs that mention visa sponsorship
excludeHide jobs that mention it

Anything else — including true — is a 400.

language#

Filter: ?language=, comma = OR. Field: job.job_language.

The detected language of the posting. The field stores the short lower-case ISO-639-1 code — en is by far the most common — but the filter takes either the full name or the code: ?language=English and ?language=en are the same query.

33 codes are supported:

en · fr · de · nl · es · it · pt · pl · sv · zh · ja · hu · id · no · cs · el · da · fi · uk · ro · tr · bg · sk · ko · ru · hr · ar · vi · sl · sr · th · he · ms

Not a validated enum — a value outside the list matches nothing rather than erroring.

source#

64 public platform slugs return rows, documented in full on the sources page. There is no second tier: every platform we list is a value you can filter on. The filter takes the slug — lever, not the stored lever.co (the stored value is still accepted as input) — and comma = OR. exclude_source takes the same values to filter platforms out.

The keys of /v1/meta.sources are this list, live, with counts. It's the only vocabulary the API will enumerate for you — and worth reading, because source is a csv filter rather than an enum: an unknown value matches nothing instead of returning a 400.

source_type#

ValueMeans
atsA board the employer authors and manages applicants on — the canonical record. Includes join.com.
jobboardA third-party board the employer posted to — LinkedIn, Wellfound, …
aggregatorA re-listing site that copied the posting from somewhere else.

Comma = OR, and this one is a checked enum — a value off the list is a 400. It filters provenance, not duplication: cross-source mirrors are already folded, so source_type=ats selects employer-authored records rather than removing copies. The same value appears as source_type on every Job object.

skill#

An open vocabulary of canonical tokens, not an enum — far too many values to list, and it grows.

The trap is that displayed skills don't always round-trip: you see Machine Learning, but the filter wants machinelearning. A few aliases (c++cpp, c#csharp, .netdotnet, node.js/nodejsnode, react.jsreact, vue.jsvue, golanggo) are translated for you; multi-word names are not. See skills & taxonomies.

Commas mean OR — except skill#

Every multi-value filter treats a comma-separated list as OR:

BASH
curl "…/v1/jobs?taxonomies=Software,Healthcare"   # either family
curl "…/v1/jobs?employment_type=FULL_TIME,INTERN" # either type
curl "…/v1/jobs?seniority=Director,Management"    # leadership, one request

The one exception is skill, where comma = AND — every listed skill must be present, because "needs Python and AWS" is what a skill list means. For OR across skills, run one request per value and merge on id.

Shape parameters#

Two more closed vocabularies live on the shape side of the query string, and they're validated the same way:

  • description_formattext | html. Omit the parameter and the response has no description key at all (smaller payloads).
  • collapsetrue | title,organization. Both name the same grouping; see duplicates & collapsing.

Any other value is a 400.

Validating before you send#

The server does this for you now: a bad enum value is a 400 whose hint lists the allowed values, so a client-side lookup table is a UX nicety rather than a correctness requirement. The one list still worth checking dynamically is source — it's a csv filter, not an enum (unknown values match nothing), and it's the one vocabulary that changes on its own:

JS
const { sources } = await (
  await fetch("https://api.hyperjobs.io/v1/meta", { headers })
).json();
 
if (!(input in sources)) {
  throw new Error(`Unknown source: ${input}. Valid: ${Object.keys(sources).join(", ")}`);
}
Was this page helpful?