Job Opportunities API

Check the data. Then trust it.

endpoints

Key and metadata

Four endpoints that describe the API rather than return rows: your key, the filter vocabularies, the freshness report and the provider list.

Last verified 2026-08-22 · Assumes: Authentication. · Markdown copy

1GET /v1/me

Your key, its plan, its limits and today's usage. Costs no records, so it is safe to poll and safe to call on start-up.

In depthWhy it exists, what it is not, what people get wrong

The first call to make with a new key, and the call to make when something is confusing. It answers “is this key alive”, “which plan is it on” and “how much have I used today” in one round trip, and it does not touch the record meter.

It reports request usage, not record usage — the record counters live in the X-RateLimit-Records-* response headers on any row-returning call. That split is worth remembering: this endpoint tells you about the throttle, the headers tell you about the licence.

Exact contractTypes, defaults, ranges, errors, edge cases
CodeWhat it means
200Key status.
401Missing, unknown, revoked or expired key. All four are reported identically.
429Over your plan's per-minute or per-day limit. Retry-After says how long.
The response, from a real call
{
  "created": "2026-08-22",
  "key_prefix": "joadocs",
  "plan": "signal",
  "status": "active",
  "limits": { "per_day": 400000, "per_minute": 300 },
  "renews_or_expires": null,
  "usage_today": 1
}
key_prefix
A short, non-secret label for the key. Safe to log and to show a user; it is how you tell two of your own keys apart.
status
active is the usable state. A revoked or expired key does not reach this endpoint — it gets a 401.
renews_or_expires
The next billing date on a subscription, or null on a plan that does not renew.
usage_today
Requests made today, against limits.per_day. Resets at UTC midnight.
Check a key
$ curl -s -H "Authorization: Bearer $JOA_KEY" https://api.jobopportunitiesapi.org/v1/me | jq
{
  "created": "2026-08-22",
  "key_prefix": "joadocs",
  "plan": "signal",
  "status": "active",
  "limits": { "per_day": 400000, "per_minute": 300 },
  "renews_or_expires": null,
  "usage_today": 1
}
Captured 2026-08-22 from a throwaway Signal key created for this build.

2GET /v1/meta/facets

Every filter value with its live count. This is the list to enumerate at run time instead of hard-coding a vocabulary.

In depthWhy it exists, what it is not, what people get wrong

One response carries every controlled vocabulary the listing endpoints accept — categories, countries, cities, remote statuses, employment types, seniorities, source types and providers — each with the number of live rows behind it. It is the single most useful call for building a filter UI, and the correct defence against a value that has left the vocabulary.

Exact contractTypes, defaults, ranges, errors, edge cases
CodeWhat it means
200Facet counts.
401Missing, unknown, revoked or expired key. All four are reported identically.
429Over your plan's per-minute or per-day limit. Retry-After says how long.
Facet groupFiltersNote
familycategory, exclude_categoryNamed family in the facet response and category in the filter.
countrycountry, exclude_countryISO-3166 alpha-2.
citycityResolved city names; thinner coverage than country.
remoteremoteremote, hybrid, on_site, not_stated.
employmentemployment_typeIncludes not_stated.
seniorityseniorityIncludes not_stated.
source_typesource_type, exclude_source_typeaggregator and agency are reserved and match nothing.
providerprovider, exclude_providerAlso available in richer form at /public/providers.
Value to sendLabelLive rows
EngineeringEngineering434,035
HealthcareHealthcare417,776
Operations & AdminOperations & Admin370,013
SalesSales285,637
Skilled TechnicianSkilled Technician220,289
RetailRetail206,182
uncategorisedUncategorised192,724
HospitalityHospitality187,079
FinanceFinance178,985
Logistics & TransportLogistics & Transport108,760
MarketingMarketing96,981
Customer SupportCustomer Support95,756
EducationEducation84,382
ManufacturingManufacturing83,836
HR & RecruitingHR & Recruiting71,093
Data & AnalyticsData & Analytics65,005
Consulting & StrategyConsulting & Strategy62,272
Construction & TradesConstruction & Trades51,899
Legal & ComplianceLegal & Compliance47,148
ProductProduct37,156
DesignDesign36,386
SecuritySecurity30,377
Science & ResearchScience & Research28,812
ProcurementProcurement19,981
Safety & EnvironmentSafety & Environment14,347

25 values, rendered from /public/facets (keyless) — the same data as /v1/meta/facets. Enumerate it at run time rather than hard-coding this list.

The vocabularies, keylessly
curl -s https://api.jobopportunitiesapi.org/public/facets \
  | jq '{groups: (.data | keys), categories: (.data.family[:5])}'

3GET /v1/meta/freshness

How recently the ledger was verified, how much of it carries each field, and how many roles closed in the last day and week.

In depthWhy it exists, what it is not, what people get wrong

Use this to decide whether the data behind an answer is current enough for what you are doing with it, and to show a “last updated” line honestly. It also carries the indicative currency rates used to compute salary_min_annual_eur.

For the broader picture — the three populations, the reconciliation, the excluded sources — use /public/coverage instead. This endpoint is about time; that one is about composition.

Exact contractTypes, defaults, ranges, errors, edge cases
FigureValueMeaning
measured_at12 September 2026, 01:52 UTCWhen the snapshot behind this report was taken.
generated_at12 September 2026, 02:25 UTCWhen this response was assembled.
live_listings3,426,911Rows /v1/jobs will serve.
ledger_rows9,357,945Live + withheld + closed.
closures.last_24h42,113Roles that left their source in the last day.
closures.last_7d1,640,300…and in the last week.
closures.total5,707,085Retained: kept indefinitely.

Measured 12 September 2026, 01:52 UTC · rendered from /public/freshness, which needs no key. These figures move; the endpoint is always right and this page is only as fresh as its last build.

CodeWhat it means
200Freshness and coverage report.
401Missing, unknown, revoked or expired key. All four are reported identically.
429Over your plan's per-minute or per-day limit. Retry-After says how long.

4GET /v1/meta/providers

Every source the ledger is built from, with its live row count and its description coverage.

In depthWhy it exists, what it is not, what people get wrong

The provider list is both a vocabulary and a data-quality report. Description coverage varies from around 60% to nearly 100% depending on which system published the row, so if your product needs advert bodies, this table tells you which providers to prefer.

Exact contractTypes, defaults, ranges, errors, edge cases
providerLabelLive rowsWith a descriptionsource_classsource_type
company_siteCompany career site1,895,3151,590,079 (83.8%)directcareer_site
workdayWorkday635,841275,365 (43.3%)employer_atsats
oracleOracle HCM235,520203,871 (86.5%)employer_atsats
greenhouseGreenhouse159,168159,150 (99.9%)employer_atsats
smartrecruitersSmartRecruiters102,021100,895 (98.8%)employer_atsats
paylocityPaylocity85,39480,054 (93.7%)employer_atsats
teamtailorTeamtailor69,88769,825 (99.9%)employer_atsats
ashbyAshby68,65268,652 (100%)employer_atsats
workableWorkable63,32063,318 (99.9%)employer_atsats
leverLever49,92449,203 (98.5%)employer_atsats
personioPersonio48,41444,066 (91%)employer_atsats
recruiteeRecruitee35,91235,892 (99.9%)employer_atsats
breezyBreezy HR35,18034,495 (98%)employer_atsats
bamboohrBambooHR25,62825,036 (97.6%)employer_atsats
joinjoin.com16,40316,368 (99.7%)employer_atsats
ripplingRippling16,06315,721 (97.8%)employer_atsats
pinpointPinpoint14,62714,627 (100%)employer_atsats
ukgUKG Pro11,30911,309 (100%)employer_atsats
jobtech_swedenJobTech Sweden10,23510,235 (100%)governmentpublic_agency
af_employer_directEmployer career site (Sweden)887885 (99.7%)directcareer_site
nav_norwayNAV Norge1440 (0%)governmentpublic_agency
extension_submissionDirect submission65 (83.3%)directcareer_site

22 providers. This list is the vocabulary the provider and exclude_provider filters accept: a name that is not here is a 422, never a silently empty page. Rendered from /public/providers. Read it at request time rather than pinning this list — on 2026-08-15 three names left it and a documented example became a 422 the next morning.

CodeWhat it means
200The provider list.
401Missing, unknown, revoked or expired key. All four are reported identically.
429Over your plan's per-minute or per-day limit. Retry-After says how long.

The keyless mirror is /public/providers, with the same shape. Both list only redistributable providers — the sources we fetch and do not republish are named separately, under excluded_sources on /public/coverage. See Sourcing and refusals.

This page was rendered 12 September 2026, 02:25 UTC. Every figure on it comes from the endpoint named beside it, and every published request is re-sent against the live API before this site is allowed to build. If something here is wrong, the thumbs-down above reaches a person.