Jobless Partner API
Live job search across about two million active postings, natural-language search, job detail, market stats and CV audits, over one REST API. You pay per call in credits, only for successful calls.
https://api.jobless.dev/partners/v1Quickstart
1. Sign in to the partner portal and create a key. The full key is shown once. 2. Keep it on your server, in an environment variable. 3. Make a call:
export JOBLESS_API_KEY="jl_live_..."
curl -s "https://api.jobless.dev/partners/v1/jobs?q=python&country=DE&workplace=remote&per_page=10" \
-H "Authorization: Bearer $JOBLESS_API_KEY"const res = await fetch("https://api.jobless.dev/partners/v1/jobs/ai-search?q=" + encodeURIComponent("junior data analyst jobs in Amsterdam"), {
headers: { Authorization: `Bearer ${process.env.JOBLESS_API_KEY}` },
});
const body = await res.json();
if (!res.ok) throw new Error(`${body.error.code}: ${body.error.message} (${body.error.request_id})`);
console.log(body.data.length, "jobs,", body.usage.credits, "credits");import os, requests
r = requests.get(
"https://api.jobless.dev/partners/v1/stats",
headers={"Authorization": f"Bearer {os.environ['JOBLESS_API_KEY']}"},
timeout=30,
)
r.raise_for_status()
print(r.json()["total_jobs"], r.headers["X-Credits-Remaining"])Authentication
Every call needs a key, sent as Authorization: Bearer jl_live_... (or X-API-Key). Keys are server-side secrets: never put one in a browser, a mobile app or a public repository. If a key leaks, revoke it on the API keys page; it stops working on the next request. A missing or wrong key returns 401 invalid_key, a revoked one 401 revoked_key. Requests that carry a browser Origin header are refused with 403 browser_not_allowed.
All responses are compact JSON in UTF-8 with snake_case keys. Timestamps are ISO-8601 UTC, countries ISO 3166-1 alpha-2, currencies ISO 4217. A field we do not know is null, never missing. IDs are opaque, prefixed strings (job_, aud_, req_); do not parse them.
Credits and pricing
One balance pays for every endpoint. Each successful call costs:
- data_tokens: the size of the JSON we send you, counted with the public o200k tokenizer on the response body without its
usageblock. You can reproduce it withtiktoken. A job in a list is about 270 tokens. - ai_tokens: the model work your call needed (planning an AI search, reading and reviewing a CV). Zero for filter search, job detail and stats. Broken down per step in
usage.ai_breakdown. - No minimum charge, no per-call cap. Cache hits cost data tokens only, so a repeated AI search is as cheap as a filter search (see Caching).
- Smaller responses cost less:
fields=on the search endpoints returns only the keys you ask for (see Choosing fields). - Failed calls (any 4xx or 5xx) cost 0, including AI work done before a failure.
- Every charged response carries a
usageblock and theX-Credits-ChargedandX-Credits-Remainingheaders.
Packs: Starter 30M credits for $29 ($0.97 per million), Growth 150M for $99 ($0.66 per million), Scale 1B for $399 ($0.40 per million). Buy them on the billing page. Typical cost per call:
| Call | AI tokens | Data tokens | Credits | Starter | Growth | Scale |
|---|---|---|---|---|---|---|
| GET /jobs, 10 per page | 0 | ~2,720 | 2,720 | $0.0026 | $0.0018 | $0.0011 |
| GET /jobs, 25 per page (typical / high) | 0 | 6,725 / 7,350 | 6.7k / 7.4k | $0.0065 / $0.0071 | $0.0044 / $0.0049 | $0.0027 / $0.0029 |
| GET /jobs/{id} (typical / high / max) | 0 | 1,356 / 2,224 / ~5,000 | 1.4k / 2.2k / 5k | $0.0013 / $0.0022 / $0.0049 | $0.0009 / $0.0015 / $0.0033 | $0.0005 / $0.0009 / $0.0020 |
| GET /stats | 0 | ~1,400 | 1.4k | $0.0014 | $0.0009 | $0.0006 |
| GET /jobs/ai-search, 10, cached | 0 to 10 | ~2,870 | 2.9k | $0.0028 | $0.0019 | $0.0011 |
| GET /jobs/ai-search, 10, new query | ~2,410 | ~2,870 | 5.3k | $0.0051 | $0.0035 | $0.0021 |
| GET /jobs/ai-search, 25, new query | ~2,410 | ~6,875 | 9.3k | $0.0090 | $0.0061 | $0.0037 |
| POST /resumes/audit (typical) | ~16,500 | ~600 | 17.1k | $0.0166 | $0.0113 | $0.0068 |
| POST /resumes/audit (long CV) | ~26,000 | ~700 | 27k | $0.026 | $0.018 | $0.011 |
What $29 (30M credits) buys: about 11,000 pages of 10 jobs, or about 21,000 job details, or about 5,700 new AI searches, or about 1,750 CV audits. Figures are measured averages; the exact charge of each call is in its usage block.
Filter search
/v1/jobs· data tokens only, about 2.7k credits for 10 jobsStructured search over active jobs. With no q and no filter you get a browse list with at most one job per company. Unknown parameters return 400 unknown_parameter, so a typo never silently widens a search.
| Param | Type | Allowed | Default |
|---|---|---|---|
| q | string | 1 to 200 chars. Matches title, company, summary and skills. | none |
| country | CSV ISO-2 | 1 to 10 codes, e.g. DE,NL. Matches jobs located in, or open to applicants from, these countries. Unknown codes return 400. | none |
| city | CSV | 1 to 5 city names, 2 to 64 chars each. On-site and hybrid jobs in that city. | none |
| remote_worldwide | bool | Adds jobs open to applicants anywhere. | false |
| workplace | CSV | remote, hybrid, on_site | none |
| employment_type | CSV | FULL_TIME, PART_TIME, CONTRACT, INTERNSHIP (case-insensitive) | none |
| seniority | CSV | entry, mid, senior, staff, manager, director, executive | none |
| role_family | CSV | 1 to 5 role family slugs, e.g. software_engineering, data_analytics | none |
| skill | CSV | 1 to 3 skill names, all required | none |
| posted_within_days | int | 1 to 365 | all active jobs |
| sort | enum | relevance, newest | relevance with q, else newest |
| page | int | >= 1, and page x per_page <= 1000 | 1 |
| per_page | int | 1 to 25 | 10 |
| fields | CSV | Compact job keys to return, e.g. title,company,salary. id and url are always included. An unknown name returns 400 invalid_parameter. | all fields |
total is exact up to 10,000, then total_is_capped is true. You can page up to 1,000 results; there is no cursor. posted_within_days filters on the date we last saw the job fresh, which can differ from posted_at. /v1/stats counts posted_last_24h and posted_last_7d on the same date, so the two always agree.
{
"object": "list",
"data": [ {
"object": "job",
"id": "job_3q9f2k7d1m8x4v6b0c5n2p7r1t",
"title": "Senior Backend Engineer (Python)",
"company": { "name": "Northwind Analytics", "logo_url": "https://api.jobless.dev/partners/v1/logos/9f1c2a7e?s=b81d3c0a91fe", "website": "https://northwind.io" },
"location_label": "Berlin, Germany",
"locations": [{ "city": "Berlin", "country": "DE" }],
"workplace": "hybrid",
"remote_eligibility": null,
"employment_type": "FULL_TIME",
"seniority": "senior",
"role_family": "software_engineering",
"role_families": ["software_engineering"],
"skills": ["Python", "Django", "PostgreSQL", "AWS"],
"salary": { "min": 70000, "max": 90000, "currency": "EUR", "period": "year" },
"posted_at": "2026-10-02T09:14:00Z",
"posted_at_estimated": false,
"url": "https://jobless.dev/jobs/senior-backend-engineer-python-northwind-analytics-3f2a91c4?ref=your_slug"
} ],
"pagination": { "page": 1, "per_page": 10, "total": 412, "total_is_capped": false, "max_reachable": 1000, "has_more": true },
"usage": { "credits": 2721, "ai_tokens": 0, "data_tokens": 2721, "ai_breakdown": {}, "cached": false }
}Choosing fields
GET /v1/jobs, GET /v1/jobs/ai-search and GET /v1/jobs/batch take fields, a comma-separated list of compact job keys. Only those keys come back, plus id and url, which are always included. Because you pay per data token, a trimmed page costs less: a list that only needs a title, a salary and a link is often under half the credits of the full page. A name that is not a compact job key returns 400 invalid_parameter with param: "fields".
Allowed names: title, company, location_label, locations, workplace, remote_eligibility, employment_type, seniority, role_family, role_families, skills, salary, posted_at, posted_at_estimated.
curl -s "https://api.jobless.dev/partners/v1/jobs?q=python&country=DE&fields=title,salary" \
-H "Authorization: Bearer $JOBLESS_API_KEY"{
"object": "list",
"data": [
{
"object": "job",
"id": "job_3q9f2k7d1m8x4v6b0c5n2p7r1t",
"title": "Senior Backend Engineer (Python)",
"salary": { "min": 70000, "max": 90000, "currency": "EUR", "period": "year" },
"url": "https://jobless.dev/jobs/senior-backend-engineer-python-northwind-analytics-3f2a91c4?ref=your_slug"
}
],
"pagination": { "page": 1, "per_page": 10, "total": 412, "total_is_capped": false, "max_reachable": 1000, "has_more": true },
"usage": { "credits": 1104, "ai_tokens": 0, "data_tokens": 1104, "ai_breakdown": {}, "cached": false }
}Job detail
/v1/jobs/{id}· data tokens only, about 1.4k creditsThe full job: everything in the compact object plus summary, description (cleaned, capped at 20,000 chars), remote_eligibility.evidence, visa_sponsorship, relocation_support, the full company and status. Pass description_format=text to get plain text instead of markdown, which is smaller and cheaper. A malformed or unknown id returns 404 job_not_found; a closed job 410 job_closed, so drop it from your side.
| Param | Type | Allowed | Default |
|---|---|---|---|
| id (path) | string | A job_ id from a search result | none |
| description_format | enum | markdown, text | markdown |
Batch job lookup
/v1/jobs/batch· data tokens only, about 270 credits per compact jobFetch up to 25 jobs you already know in one call, for example to refresh a saved list. ids is a comma-separated list of 1 to 25 job_ ids; duplicates are collapsed. data keeps the order you asked for, and every id that is unknown or closed is listed in missing instead of failing the call, so drop those on your side. Jobs come back compact; pass full=true for full job objects, and fields to trim either. No AI work is involved, so only data tokens are charged.
| Param | Type | Allowed | Default |
|---|---|---|---|
| ids | CSV | Required. 1 to 25 job_ ids, comma separated | none |
| full | bool | Return full job objects, as on GET /v1/jobs/{id} | false |
| fields | CSV | Compact job keys to return, e.g. title,company,salary. With full=true also summary, description, visa_sponsorship, relocation_support and status. id and url are always included. An unknown name returns 400 invalid_parameter. | all fields |
curl -s "https://api.jobless.dev/partners/v1/jobs/batch?ids=job_3q9f2k7d1m8x4v6b0c5n2p7r1t,job_8f2kd0q7m1x9v3b6c4n5p2r7t1" \
-H "Authorization: Bearer $JOBLESS_API_KEY"{
"object": "list",
"data": [
{ "object": "job", "id": "job_3q9f2k7d1m8x4v6b0c5n2p7r1t", "title": "Senior Backend Engineer (Python)", "...": "compact job" },
{ "object": "job", "id": "job_8f2kd0q7m1x9v3b6c4n5p2r7t1", "title": "Data Engineer", "...": "compact job" }
],
"missing": ["job_1a2b3c4d5e6f7g8h9j0k1m2n3p"],
"usage": { "credits": 548, "ai_tokens": 0, "data_tokens": 548, "ai_breakdown": {}, "cached": true }
}AI search
/v1/jobs/ai-search· about 5.3k credits for a new query, 2.9k when cachedNatural-language search: q=junior data analyst jobs in Amsterdam, english speaking. We turn the sentence into filters, rank page 1 by meaning, and return how we read it in interpretation so you can show "searched for" chips. Explicit params win over places named in the sentence. When the query names no time window, the last 60 days are searched (posted_within_days_stated: false).
| Param | Type | Allowed | Default |
|---|---|---|---|
| q | string | Required. 2 to 160 chars, at most 24 words, plain language. No URLs, emails or markup. | none |
| country | CSV ISO-2 | 1 to 10 codes, e.g. DE,NL. Matches jobs located in, or open to applicants from, these countries. Unknown codes return 400. | none |
| city | CSV | 1 to 5 city names, 2 to 64 chars each. On-site and hybrid jobs in that city. | none |
| remote_worldwide | bool | Adds jobs open to applicants anywhere. | false |
| workplace | CSV | remote, hybrid, on_site | none |
| employment_type | CSV | FULL_TIME, PART_TIME, CONTRACT, INTERNSHIP (case-insensitive) | none |
| seniority | CSV | entry, mid, senior, staff, manager, director, executive | none |
| role_family | CSV | 1 to 5 role family slugs, e.g. software_engineering, data_analytics | none |
| posted_within_days | int | 1 to 365 | from the query, else 60 |
| page | int | >= 1, and page x per_page <= 1000 | 1 |
| per_page | int | 1 to 25 | 10 |
| fields | CSV | Compact job keys to return, e.g. title,company,salary. id and url are always included. An unknown name returns 400 invalid_parameter. | all fields |
Page 1 is ranked by meaning; pages 2 and later continue with the rest of the matching set, newest first. interpretation.filters replays the same search on GET /v1/jobs for free AI work and deeper paging. relaxed lists any filters we loosened to find results (skill, seniority, city, worldwide, date). A query that names only a role and no place (for example data analyst jobs) still gets a real total; it is null only in the rare case there is no honest count. If the query is not a job search (e.g. hello), you get data: [] and a clarification string. AI work that fails is never charged, but an account whose AI calls keep failing without a result hits a daily ceiling: AI endpoints then answer 429 ai_failure_limit_reached until 00:00 UTC, while the other endpoints keep working.
{
"object": "list",
"query": "senior python backend roles in germany, remote ok",
"interpretation": {
"role_family": "software_engineering",
"role_family_label": "Software Engineering",
"role_stated": true,
"skills": ["Python"],
"seniority": "senior",
"workplace": ["remote"],
"countries": ["DE"],
"cities": [],
"excluded_countries": [],
"worldwide": false,
"posted_within_days": 60,
"posted_within_days_stated": false,
"relaxed": ["skill"],
"filters": { "q": "python backend", "role_family": "software_engineering", "seniority": "senior", "country": "DE", "workplace": "remote", "posted_within_days": 60 }
},
"clarification": null,
"data": [ { "object": "job", "id": "job_8f2...", "title": "Senior Python Engineer (Remote, DE)", "...": "compact job" } ],
"pagination": { "page": 1, "per_page": 10, "total": 1284, "total_is_capped": false, "max_reachable": 1000, "has_more": true },
"usage": { "credits": 5288, "ai_tokens": 2412, "data_tokens": 2876, "ai_breakdown": { "query_plan": 2402, "query_embedding": 10 }, "cached": false }
}Market stats
/v1/stats· data tokens only, about 1.4k creditsTotals and breakdowns of the live index: by country (located in or open to applicants from), workplace, employment type, seniority and role family, plus how many jobs have each field unknown. Refreshed hourly. No parameters. posted_last_24h and posted_last_7d count on the same date that posted_within_days filters on (the date we last saw the job fresh), so posted_within_days=7 on /v1/jobs returns a matching total.
{
"object": "stats",
"updated_at": "2026-10-05T15:00:00Z",
"total_jobs": 1946340,
"posted_last_24h": 41210,
"posted_last_7d": 236904,
"remote_worldwide_jobs": 8597,
"by_country": [{ "value": "US", "count": 1074957 }, { "value": "DE", "count": 112893 }],
"by_workplace": [{ "value": "on_site", "count": 421894 }, { "value": "remote", "count": 214766 }, { "value": "hybrid", "count": 171126 }],
"by_employment_type": [{ "value": "FULL_TIME", "count": 1384002 }],
"by_seniority": [{ "value": "entry", "count": 725458 }, { "value": "mid", "count": 679317 }, { "value": "senior", "count": 427914 }],
"by_role_family": [{ "value": "software_engineering", "count": 121905 }],
"unknown": { "workplace": 1138554, "seniority": 4973, "role_family": 0 },
"usage": { "credits": 1402, "ai_tokens": 0, "data_tokens": 1402, "ai_breakdown": {}, "cached": true }
}CV audit
/v1/resumes/audit· about 17k creditsSend one CV, get a 0 to 100 score, a summary, strengths, gaps with fixes, and red flags. Stateless: the CV is processed in memory and never stored. Every quoted evidence is a verbatim substring of the CV. Typical latency is about 20 seconds; set your client timeout to at least 90.
| Field | Type | Rule |
|---|---|---|
| file | multipart | .pdf or .docx with a text layer, up to 5 MB |
| text | JSON string | Plain or markdown CV text, 300 to 40,000 chars (instead of file) |
| target_role | string | Optional, 2 to 100 chars. Judges relevance to this role. |
| end_user_ref | string | Optional, up to 64 chars. Echoed back, never stored. |
curl -s -X POST "https://api.jobless.dev/partners/v1/resumes/audit" \
-H "Authorization: Bearer $JOBLESS_API_KEY" \
-H "Idempotency-Key: audit-cand_19284-1" \
-F "file=@cv.pdf" -F "target_role=Backend Engineer"Scanned PDFs return 422 unreadable_cv, documents that are not a CV 422 cv_not_a_resume, both free. We store no response body: repeating an Idempotency-Key within 15 minutes returns 409 already_processed with the original request_id, so resubmit to get the result again.
{
"object": "cv_audit",
"id": "aud_01J9ZQ4M2T8B",
"end_user_ref": "cand_19284",
"created_at": "2026-10-05T10:02:11Z",
"target_role": "Backend Engineer",
"score": 68,
"summary": "Clear backend profile with six years of relevant Python work, held back by unquantified bullets, no summary and an unexplained 10-month gap in 2021.",
"strengths": [
{ "title": "Recent, relevant backend experience", "detail": "Three years building Python payment services at Acme Pay matches the target role directly.", "evidence": "Built Django services processing 2M payments a day on AWS ECS" }
],
"gaps": [
{ "code": "unquantified", "title": "Results are not measured", "detail": "4 of 9 bullets in the latest role describe tasks with no outcome.", "severity": "medium", "section": "experience", "fix": "Add one number per bullet: volume, time saved, error rate or revenue." }
],
"red_flags": [
{ "code": "employment_gap", "title": "Unexplained 10-month gap", "detail": "No role between Delta Soft (ended 2021-02) and Acme Pay (started 2021-12).", "severity": "medium", "section": "experience", "evidence": "Delta Soft, Software Developer, 2019-01 to 2021-02" }
],
"cv": { "language": "en", "parse_quality": "good", "years_experience": 6.5 },
"usage": { "credits": 17112, "ai_tokens": 16524, "data_tokens": 588, "ai_breakdown": { "cv_parse": 9140, "cv_review": 7384 }, "cached": false }
}Usage and balance
/v1/usage· freeYour balance, limits and what you spent in a period, from the same ledger as the portal Usage page. Never charged. from and to are optional dates (YYYY-MM-DD, UTC); the default is the last 30 days and a period can span at most 90.
| Param | Type | Allowed | Default |
|---|---|---|---|
| from | date | YYYY-MM-DD, on or before to | 29 days before to |
| to | date | YYYY-MM-DD | today (UTC) |
{
"object": "usage",
"partner": "your_slug",
"environment": "live",
"balance_credits": 18240500,
"rate_limit": { "per_minute": 120, "daily_credit_cap": 5000000 },
"period": { "from": "2026-09-06T00:00:00Z", "to": "2026-10-05T23:59:59Z" },
"totals": { "requests": 1840, "credits_charged": 1912300, "ai_tokens": 402100, "data_tokens": 1510200 },
"by_endpoint": [{ "endpoint": "GET /v1/jobs", "requests": 1500, "credits": 1500000 }],
"daily": [{ "date": "2026-10-04", "requests": 212, "credits": 230100 }]
}Company logos
/v1/logos/{key}?s={sig}· free, no key neededcompany.logo_url is a signed link on our API, ready to use as an img source. It needs no API key, so you can put it straight into a web page or an app, and it is never charged. Use the URL exactly as we return it: the signature covers the key, and a changed or made-up URL returns 404. Responses are images only, size-capped and sent with a long Cache-Control, so browsers and CDNs keep them. logo_url is null when we have no logo for the company; show the company name or initials instead.
Caching
We cache identical calls for a short time, so repeats are faster. The cache never mixes partners' bills: every call is still charged on its own.
| Endpoint | Cached for | Keyed by |
|---|---|---|
| GET /v1/jobs | 120 seconds | the normalized query parameters |
| GET /v1/jobs/{id} | 600 seconds | job id and description_format |
| GET /v1/jobs/batch | 600 seconds per job | reuses the job detail cache for each id |
| GET /v1/jobs/ai-search | 1 hour (page 1) | the normalized query and every filter param |
| GET /v1/stats | 1 hour | no params |
| POST /v1/resumes/audit | never cached | CVs are not kept |
A cache hit still costs its data tokens, because you still receive the data, but ai_tokens is 0 and usage.cached is true. AI search caches only which jobs matched and re-checks each one on every call, so a job that closed drops out even on a cache hit. Filter search results can be up to 120 seconds old.
Errors
Errors share one envelope. type is a closed list; code may gain values, so handle unknown codes by their type. No error is ever charged.
{
"error": {
"type": "invalid_request",
"code": "invalid_parameter",
"message": "per_page must be between 1 and 25.",
"param": "per_page",
"request_id": "req_01J9ZK3T6B2W",
"doc_url": "https://partners.jobless.dev/docs/errors#invalid_parameter"
}
}| HTTP | code | type |
|---|---|---|
| 400 | invalid_parametermissing_parameterinvalid_jsonunknown_parameterinvalid_querypage_limit_exceeded | invalid_request |
| 401 | invalid_keyrevoked_key | authentication |
| 402 | insufficient_credits | billing |
| 403 | scope_not_allowedpartner_suspendedbrowser_not_allowed | permission |
| 404 | job_not_foundnot_found | not_found |
| 409 | idempotency_conflictrequest_in_progressalready_processedbilling_email_in_use | conflict |
| 410 | job_closed | not_found |
| 413 | file_too_large | invalid_request |
| 415 | unsupported_file_type | invalid_request |
| 422 | unreadable_cvcv_not_a_resumetext_too_shorttext_too_long | invalid_request |
| 429 | rate_limitedconcurrency_limiteddaily_credit_cap_reachedai_failure_limit_reached | rate_limit |
| 502 | upstream | |
| 503 | upstream_unavailable | upstream |
| 504 | timeout | upstream |
| 500 | internal_error | internal |
Retry 429 and 503 after Retry-After seconds, and 502/500 with backoff. Do not retry other 4xx without changing the request. 402 insufficient_credits means your balance is empty: top up on the billing page.
429 ai_failure_limit_reached(AI endpoints only): too many of your AI calls failed today without a result. Failed calls are free, so this ceiling protects shared capacity. AI search and CV audit reopen at 00:00 UTC; filter search, job detail, batch and stats keep working.503 upstream_unavailable: a service we depend on is down. This also covers the case where we cannot guarantee that your request's data stays out of our model logs; we refuse the call rather than log it. Retry afterRetry-After.409 billing_email_in_usecomes from the portal, not the API: a checkout on the billing page was refused because your billing email already belongs to another Jobless account at our payment provider. Email us to set a different billing email.
Limits
| Limit | Default |
|---|---|
| Requests per minute | Search, detail, stats 120; AI search 30; CV audit 10 |
| Concurrent requests | CV audit 2, AI search 4 |
| Results per page | 1 to 25 |
| Paging depth | page x per_page <= 1,000, no cursor |
| Multi-value params | country 10, city 5, role_family 5, skill 3 |
| Batch lookup | 1 to 25 ids per call |
| CV input | file up to 5 MB (pdf, docx); text 300 to 40,000 chars |
| Daily credit cap | Set per organisation (see Overview); 429 daily_credit_cap_reached when hit |
Your organisation's actual limits are on the portal Overview. Ask us if you need more.
Headers
| Header | Direction | Meaning |
|---|---|---|
| Authorization: Bearer jl_live_... | request | Required. X-API-Key: jl_live_... also works. |
| Idempotency-Key | request | POST only. 8 to 128 chars of [A-Za-z0-9_-]. Recommended for audits. |
| X-Request-Id | response | Our id for the call (req_...). Also in every error body. Quote it to support. |
| X-Credits-Charged | response | Credits charged for this call. 0 on errors. |
| X-Credits-Breakdown | response | ai_tokens=2412; data_tokens=2876 |
| X-Credits-Remaining | response | Balance after this call. |
| X-RateLimit-Limit / -Remaining / -Reset | response | Per-minute window for this endpoint group. Reset is epoch seconds. |
| Retry-After | response | Seconds to wait, on 429 and 503. |
| Jobless-Api-Version | response | Major version served (1). |
Versioning: the major version is in the path. Within v1 we only add things: new endpoints, optional params, response fields and values in open enums. Anything breaking ships as /v2, with v1 kept running for at least 12 months and Deprecation/Sunset headers during the overlap. Price changes are announced 30 days ahead.
Data quality
- Reliable: copied from the posting or computed from facts: title, description, company name, salary when present.
- Best-effort: judged by a model or rules, may be null or wrong:
role_family,seniority,skills,workplace,employment_type,remote_eligibility,visa_sponsorship. Do not hard-filter people out on these alone. - Estimated:
posted_atwhenposted_at_estimatedis true (the source had no date, so it is the day we first saw the job). workplaceis empty on many postings because the posting does not say; filtering on it excludes those./v1/statspublishes the unknown counts.
Terms of use
- Attribution: every job has a
urlon jobless.dev with your?ref=. Link apply actions to it and show "via Jobless" near job content. We never expose the employer's ATS link. - Display only: do not store job objects for more than 24 hours. Refresh, or drop on 404 and 410.
- No redistribution, resale or building a competing job index. No bulk export or deep crawling.
- CVs you send for audit are processed in memory and not stored by us. You are responsible for having your users' consent.
- Keys are yours to protect. Calls made with your key are charged to your balance.
Questions or a higher limit: hello@jobless.dev. Include the request_id for anything about a specific call.