Developer docs · v1

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.

Base URLhttps://api.jobless.dev/partners/v1

Quickstart

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:

bash
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"
javascript
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");
python
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:

credits = ai_tokens + data_tokens
  • data_tokens: the size of the JSON we send you, counted with the public o200k tokenizer on the response body without its usage block. You can reproduce it with tiktoken. 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 usage block and the X-Credits-Charged and X-Credits-Remaining headers.

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:

CallAI tokensData tokensCreditsStarterGrowthScale
GET /jobs, 10 per page0~2,7202,720$0.0026$0.0018$0.0011
GET /jobs, 25 per page (typical / high)06,725 / 7,3506.7k / 7.4k$0.0065 / $0.0071$0.0044 / $0.0049$0.0027 / $0.0029
GET /jobs/{id} (typical / high / max)01,356 / 2,224 / ~5,0001.4k / 2.2k / 5k$0.0013 / $0.0022 / $0.0049$0.0009 / $0.0015 / $0.0033$0.0005 / $0.0009 / $0.0020
GET /stats0~1,4001.4k$0.0014$0.0009$0.0006
GET /jobs/ai-search, 10, cached0 to 10~2,8702.9k$0.0028$0.0019$0.0011
GET /jobs/ai-search, 10, new query~2,410~2,8705.3k$0.0051$0.0035$0.0021
GET /jobs/ai-search, 25, new query~2,410~6,8759.3k$0.0090$0.0061$0.0037
POST /resumes/audit (typical)~16,500~60017.1k$0.0166$0.0113$0.0068
POST /resumes/audit (long CV)~26,000~70027k$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

GET/v1/jobs· data tokens only, about 2.7k credits for 10 jobs

Structured 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.

ParamTypeAllowedDefault
qstring1 to 200 chars. Matches title, company, summary and skills.none
countryCSV ISO-21 to 10 codes, e.g. DE,NL. Matches jobs located in, or open to applicants from, these countries. Unknown codes return 400.none
cityCSV1 to 5 city names, 2 to 64 chars each. On-site and hybrid jobs in that city.none
remote_worldwideboolAdds jobs open to applicants anywhere.false
workplaceCSVremote, hybrid, on_sitenone
employment_typeCSVFULL_TIME, PART_TIME, CONTRACT, INTERNSHIP (case-insensitive)none
seniorityCSVentry, mid, senior, staff, manager, director, executivenone
role_familyCSV1 to 5 role family slugs, e.g. software_engineering, data_analyticsnone
skillCSV1 to 3 skill names, all requirednone
posted_within_daysint1 to 365all active jobs
sortenumrelevance, newestrelevance with q, else newest
pageint>= 1, and page x per_page <= 10001
per_pageint1 to 2510
fieldsCSVCompact 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.

json
{
  "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.

bash
curl -s "https://api.jobless.dev/partners/v1/jobs?q=python&country=DE&fields=title,salary" \
  -H "Authorization: Bearer $JOBLESS_API_KEY"
json
{
  "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

GET/v1/jobs/{id}· data tokens only, about 1.4k credits

The 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.

ParamTypeAllowedDefault
id (path)stringA job_ id from a search resultnone
description_formatenummarkdown, textmarkdown

Batch job lookup

GET/v1/jobs/batch· data tokens only, about 270 credits per compact job

Fetch 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.

ParamTypeAllowedDefault
idsCSVRequired. 1 to 25 job_ ids, comma separatednone
fullboolReturn full job objects, as on GET /v1/jobs/{id}false
fieldsCSVCompact 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
bash
curl -s "https://api.jobless.dev/partners/v1/jobs/batch?ids=job_3q9f2k7d1m8x4v6b0c5n2p7r1t,job_8f2kd0q7m1x9v3b6c4n5p2r7t1" \
  -H "Authorization: Bearer $JOBLESS_API_KEY"
json
{
  "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 }
}
GET/v1/jobs/ai-search· about 5.3k credits for a new query, 2.9k when cached

Natural-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).

ParamTypeAllowedDefault
qstringRequired. 2 to 160 chars, at most 24 words, plain language. No URLs, emails or markup.none
countryCSV ISO-21 to 10 codes, e.g. DE,NL. Matches jobs located in, or open to applicants from, these countries. Unknown codes return 400.none
cityCSV1 to 5 city names, 2 to 64 chars each. On-site and hybrid jobs in that city.none
remote_worldwideboolAdds jobs open to applicants anywhere.false
workplaceCSVremote, hybrid, on_sitenone
employment_typeCSVFULL_TIME, PART_TIME, CONTRACT, INTERNSHIP (case-insensitive)none
seniorityCSVentry, mid, senior, staff, manager, director, executivenone
role_familyCSV1 to 5 role family slugs, e.g. software_engineering, data_analyticsnone
posted_within_daysint1 to 365from the query, else 60
pageint>= 1, and page x per_page <= 10001
per_pageint1 to 2510
fieldsCSVCompact 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.

json
{
  "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

GET/v1/stats· data tokens only, about 1.4k credits

Totals 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.

json
{
  "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

POST/v1/resumes/audit· about 17k credits

Send 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.

FieldTypeRule
filemultipart.pdf or .docx with a text layer, up to 5 MB
textJSON stringPlain or markdown CV text, 300 to 40,000 chars (instead of file)
target_rolestringOptional, 2 to 100 chars. Judges relevance to this role.
end_user_refstringOptional, up to 64 chars. Echoed back, never stored.
bash
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.

json
{
  "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

GET/v1/usage· free

Your 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.

ParamTypeAllowedDefault
fromdateYYYY-MM-DD, on or before to29 days before to
todateYYYY-MM-DDtoday (UTC)
json
{
  "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

GET/v1/logos/{key}?s={sig}· free, no key needed

company.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.

EndpointCached forKeyed by
GET /v1/jobs120 secondsthe normalized query parameters
GET /v1/jobs/{id}600 secondsjob id and description_format
GET /v1/jobs/batch600 seconds per jobreuses the job detail cache for each id
GET /v1/jobs/ai-search1 hour (page 1)the normalized query and every filter param
GET /v1/stats1 hourno params
POST /v1/resumes/auditnever cachedCVs 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.

json
{
  "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"
  }
}
HTTPcodetype
400invalid_parametermissing_parameterinvalid_jsonunknown_parameterinvalid_querypage_limit_exceededinvalid_request
401invalid_keyrevoked_keyauthentication
402insufficient_creditsbilling
403scope_not_allowedpartner_suspendedbrowser_not_allowedpermission
404job_not_foundnot_foundnot_found
409idempotency_conflictrequest_in_progressalready_processedbilling_email_in_useconflict
410job_closednot_found
413file_too_largeinvalid_request
415unsupported_file_typeinvalid_request
422unreadable_cvcv_not_a_resumetext_too_shorttext_too_longinvalid_request
429rate_limitedconcurrency_limiteddaily_credit_cap_reachedai_failure_limit_reachedrate_limit
502upstream_unavailableupstream
503unavailableai_unavailableupstream_unavailableupstream
504timeoutupstream
500internal_errorinternal

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 after Retry-After.
  • 409 billing_email_in_use comes 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

LimitDefault
Requests per minuteSearch, detail, stats 120; AI search 30; CV audit 10
Concurrent requestsCV audit 2, AI search 4
Results per page1 to 25
Paging depthpage x per_page <= 1,000, no cursor
Multi-value paramscountry 10, city 5, role_family 5, skill 3
Batch lookup1 to 25 ids per call
CV inputfile up to 5 MB (pdf, docx); text 300 to 40,000 chars
Daily credit capSet 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

HeaderDirectionMeaning
Authorization: Bearer jl_live_...requestRequired. X-API-Key: jl_live_... also works.
Idempotency-KeyrequestPOST only. 8 to 128 chars of [A-Za-z0-9_-]. Recommended for audits.
X-Request-IdresponseOur id for the call (req_...). Also in every error body. Quote it to support.
X-Credits-ChargedresponseCredits charged for this call. 0 on errors.
X-Credits-Breakdownresponseai_tokens=2412; data_tokens=2876
X-Credits-RemainingresponseBalance after this call.
X-RateLimit-Limit / -Remaining / -ResetresponsePer-minute window for this endpoint group. Reset is epoch seconds.
Retry-AfterresponseSeconds to wait, on 429 and 503.
Jobless-Api-VersionresponseMajor 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_at when posted_at_estimated is true (the source had no date, so it is the day we first saw the job).
  • workplace is empty on many postings because the posting does not say; filtering on it excludes those. /v1/stats publishes the unknown counts.

Terms of use

  • Attribution: every job has a url on 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.