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.
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
| Code | What it means |
|---|---|
200 | Key status. |
400 | A parameter the endpoint does not take. unknown_parameter: the name
is not a parameter of this endpoint; the message names it and, when a
valid parameter is within a small edit distance, suggests it
("unknown parameter: 'countyr'; did you mean 'country'?").
unsupported_parameter: a real parameter of another endpoint that has
no meaning here (for example limit on /v1/export). Refused before
the meter: nothing is charged. |
401 | Missing, unknown, revoked or expired key. All four are reported identically. |
429 | Over your plan's per-minute or per-day limit. Retry-After says how long (seconds to the next minute boundary, or to UTC midnight on a daily breach). The body says exactly when the limit resets (resets_at, resets_at_text) and, when a higher plan exists, links a same-key upgrade (upgrade_url). |
{
"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.
statusactiveis 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.
$ 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 }
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
| Code | What it means |
|---|---|
200 | Facet counts. |
400 | A parameter the endpoint does not take. unknown_parameter: the name
is not a parameter of this endpoint; the message names it and, when a
valid parameter is within a small edit distance, suggests it
("unknown parameter: 'countyr'; did you mean 'country'?").
unsupported_parameter: a real parameter of another endpoint that has
no meaning here (for example limit on /v1/export). Refused before
the meter: nothing is charged. |
401 | Missing, unknown, revoked or expired key. All four are reported identically. |
429 | Over your plan's per-minute or per-day limit. Retry-After says how long (seconds to the next minute boundary, or to UTC midnight on a daily breach). The body says exactly when the limit resets (resets_at, resets_at_text) and, when a higher plan exists, links a same-key upgrade (upgrade_url). |
| Facet group | Filters | Note |
|---|---|---|
family | category, exclude_category | Named family in the facet response and category in the filter. |
country | country, exclude_country | ISO-3166 alpha-2. |
city | city | Resolved city names; thinner coverage than country. |
remote | remote | remote, hybrid, on_site, not_stated. |
employment | employment_type | Includes not_stated. |
engagement | engagement | Arrays counted per element; unknown = not established by the posting. |
contract_category | contract_category | Arrays counted per element; includes unknown. |
agency | agency | Includes unknown. |
working_schedule | working_schedule | Includes unknown. Distinct from the frozen employment group. |
seniority | seniority | Includes not_stated. |
source_type | source_type, exclude_source_type | aggregator and agency are reserved and match nothing. |
provider | provider, exclude_provider | Also available in richer form at /public/providers. |
| Value to send | Label | Live rows |
|---|---|---|
Healthcare | Healthcare | 349,929 |
Engineering | Engineering | 274,086 |
Operations & Admin | Operations & Admin | 261,392 |
Sales | Sales | 213,370 |
Skilled Technician | Skilled Technician | 171,169 |
uncategorised | Uncategorised | 159,934 |
Retail | Retail | 151,910 |
Hospitality | Hospitality | 133,041 |
Finance | Finance | 111,334 |
Logistics & Transport | Logistics & Transport | 79,320 |
Customer Support | Customer Support | 66,253 |
Education | Education | 64,600 |
Manufacturing | Manufacturing | 57,892 |
Marketing | Marketing | 56,928 |
HR & Recruiting | HR & Recruiting | 42,478 |
Consulting & Strategy | Consulting & Strategy | 38,418 |
Construction & Trades | Construction & Trades | 37,056 |
Data & Analytics | Data & Analytics | 36,905 |
Legal & Compliance | Legal & Compliance | 28,378 |
Design | Design | 21,041 |
Product | Product | 19,659 |
Science & Research | Science & Research | 19,336 |
Security | Security | 19,331 |
Procurement | Procurement | 12,534 |
Safety & Environment | Safety & Environment | 9,849 |
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.
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
| Figure | Value | Meaning |
|---|---|---|
measured_at | 27 October 2026, 10:52 UTC | When the snapshot behind this report was taken. |
generated_at | 27 October 2026, 11:13 UTC | When this response was assembled. |
live_listings | 2,914,565 | Rows /v1/jobs will serve. |
ledger_rows | 12,334,935 | Live + withheld + closed. |
closures.last_24h | 55,708 | Roles that left their source in the last day. |
closures.last_7d | 440,884 | …and in the last week. |
closures.total | 8,653,347 | Retained: kept indefinitely. |
Measured 27 October 2026, 10: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.
| Code | What it means |
|---|---|
200 | Freshness and coverage report. |
400 | A parameter the endpoint does not take. unknown_parameter: the name
is not a parameter of this endpoint; the message names it and, when a
valid parameter is within a small edit distance, suggests it
("unknown parameter: 'countyr'; did you mean 'country'?").
unsupported_parameter: a real parameter of another endpoint that has
no meaning here (for example limit on /v1/export). Refused before
the meter: nothing is charged. |
401 | Missing, unknown, revoked or expired key. All four are reported identically. |
429 | Over your plan's per-minute or per-day limit. Retry-After says how long (seconds to the next minute boundary, or to UTC midnight on a daily breach). The body says exactly when the limit resets (resets_at, resets_at_text) and, when a higher plan exists, links a same-key upgrade (upgrade_url). |
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
| provider | Label | Live rows | With a description | source_class | source_type |
|---|---|---|---|---|---|
workday | Workday | 814,982 | 801,106 (98.2%) | employer_ats | ats |
company_site | Company career site | 807,441 | 722,160 (89.4%) | direct | career_site |
oracle | Oracle HCM | 178,152 | 170,539 (95.7%) | employer_ats | ats |
greenhouse | Greenhouse | 92,381 | 92,197 (99.8%) | employer_ats | ats |
smartrecruiters | SmartRecruiters | 65,096 | 64,928 (99.7%) | employer_ats | ats |
icims | iCIMS | 55,200 | 55,200 (100%) | employer_ats | ats |
teamtailor | Teamtailor | 46,774 | 46,760 (99.9%) | employer_ats | ats |
paylocity | Paylocity | 37,770 | 37,587 (99.5%) | employer_ats | ats |
workable | Workable | 34,098 | 34,089 (99.9%) | employer_ats | ats |
ashby | Ashby | 32,956 | 32,946 (99.9%) | employer_ats | ats |
softgarden | softgarden | 30,676 | 21,350 (69.5%) | employer_ats | ats |
recruitee | Recruitee | 27,211 | 27,202 (99.9%) | employer_ats | ats |
breezy | Breezy HR | 26,340 | 25,801 (97.9%) | employer_ats | ats |
adp | ADP Workforce Now | 25,327 | 25,238 (99.6%) | employer_ats | ats |
lever | Lever | 25,303 | 24,913 (98.4%) | employer_ats | ats |
personio | Personio | 23,957 | 23,787 (99.2%) | employer_ats | ats |
dayforce | Dayforce | 19,885 | 19,816 (99.6%) | employer_ats | ats |
ukg | UKG Pro | 18,776 | 18,775 (99.9%) | employer_ats | ats |
bamboohr | BambooHR | 15,379 | 14,972 (97.3%) | employer_ats | ats |
pinpoint | Pinpoint | 10,312 | 10,299 (99.8%) | employer_ats | ats |
join | join.com | 9,225 | 9,202 (99.7%) | employer_ats | ats |
rippling | Rippling | 9,156 | 9,048 (98.8%) | employer_ats | ats |
isolved | isolved Hire | 8,045 | 8,045 (100%) | employer_ats | ats |
teaching_vacancies | Teaching Vacancies (DfE) | 6,991 | 6,991 (100%) | government | public_agency |
jobtech_sweden | JobTech Sweden | 6,670 | 6,670 (100%) | government | public_agency |
jobvite | Jobvite | 2,635 | 2,632 (99.8%) | employer_ats | ats |
paycom | Paycom | 2,460 | 2,457 (99.8%) | employer_ats | ats |
eploy | eploy | 1,546 | 1,546 (100%) | employer_ats | ats |
pageup | PageUp | 691 | 691 (100%) | employer_ats | ats |
af_employer_direct | Employer career site (Sweden) | 350 | 346 (98.8%) | direct | career_site |
dvinci | d.vinci | 117 | 20 (17%) | employer_ats | ats |
freshteam | Freshteam | 92 | 92 (100%) | employer_ats | ats |
exeter | Exeter employers (own sites) | 89 | 89 (100%) | direct | career_site |
kcl | King's College London (own site) | 60 | 60 (100%) | direct | career_site |
34 providers with at least one served row (the providers count). The provider and exclude_provider filters accept these and the licensed sources listed under not_serving in the same response; a name in neither 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.
| Code | What it means |
|---|---|
200 | The provider list. |
400 | A parameter the endpoint does not take. unknown_parameter: the name
is not a parameter of this endpoint; the message names it and, when a
valid parameter is within a small edit distance, suggests it
("unknown parameter: 'countyr'; did you mean 'country'?").
unsupported_parameter: a real parameter of another endpoint that has
no meaning here (for example limit on /v1/export). Refused before
the meter: nothing is charged. |
401 | Missing, unknown, revoked or expired key. All four are reported identically. |
429 | Over your plan's per-minute or per-day limit. Retry-After says how long (seconds to the next minute boundary, or to UTC midnight on a daily breach). The body says exactly when the limit resets (resets_at, resets_at_text) and, when a higher plan exists, links a same-key upgrade (upgrade_url). |
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 27 October 2026, 11:28 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.