Search jobs
Search jobs. Synchronous search across the providers chosen by the waterfall strategy.
Cost: 1 credit / unique job1 credit per unique job returned that you have not already paid for.
const url = 'https://api.betterjobs.cc/v1/jobs/search';const options = { method: 'POST', headers: { 'BetterJobs-Version': '2026-10-01', 'Idempotency-Key': '7b8f2c4e-1a3d-4f5b-9c6e-0d2a4b6c8e1f', Authorization: 'Bearer <token>', 'Content-Type': 'application/json' }, body: '{"filters":{"title_or":["Head of RevOps","Head of Revenue Operations"],"country_code_or":["DE","AT","CH"],"posted_within_days":7},"waterfall":{"strategy":"cheapest_first","max_credits":100,"timeout_ms":10000},"limit":25}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request POST \ --url https://api.betterjobs.cc/v1/jobs/search \ --header 'Authorization: Bearer <token>' \ --header 'BetterJobs-Version: 2026-10-01' \ --header 'Content-Type: application/json' \ --header 'Idempotency-Key: 7b8f2c4e-1a3d-4f5b-9c6e-0d2a4b6c8e1f' \ --data '{ "filters": { "title_or": [ "Head of RevOps", "Head of Revenue Operations" ], "country_code_or": [ "DE", "AT", "CH" ], "posted_within_days": 7 }, "waterfall": { "strategy": "cheapest_first", "max_credits": 100, "timeout_ms": 10000 }, "limit": 25 }'Synchronous search across the providers chosen by the waterfall strategy. Returns up to
100 canonical jobs per page. Page with next_cursor.
Send dry_run: true to get a free estimate (expected_unique_jobs_range, providers_planned,
credits_range) without fetching anything.
Unknown filter fields return 400 unknown_filter. If a provider fails or timeout_ms is reached,
you still get 200 with metadata.status: partial and are billed only for returned jobs.
Authorizations
Section titled “Authorizations”Parameters
Section titled “ Parameters ”Header Parameters
Section titled “Header Parameters”Example
2026-10-01Date-pinned API version. Changes within a version are additive only. Defaults to your account’s pinned version.
Example
7b8f2c4e-1a3d-4f5b-9c6e-0d2a4b6c8e1fUnique key (for example a UUID). Retrying with the same key and body returns the first response and never charges twice. Same key with a different body returns 409 idempotency_conflict.
Request Bodyrequired
Section titled “Request Bodyrequired”object
Filter grammar with suffix operators. Unknown fields return 400 unknown_filter.
object
Match any of these title keywords.
Exclude titles containing any of these.
ISO 3166-1 alpha-2 codes.
Jobs first seen within this many days.
true remote only, false non-remote only, null or omitted = any.
Keep jobs whose salary.min is at least this value (yearly, in the job’s currency). Jobs with salary null are excluded when set.
Include jobs with status: closed.
How BetterJobs routes the request across providers.
object
cheapest_first stops once enough unique jobs are found, starting with the BetterJobs index.
freshest_first tries the sources with the fastest refresh first.
max_coverage queries every provider on your plan.
consensus returns only jobs seen by at least min_sources sources.
own_only uses the BetterJobs index only.
Override the providers to query. Must be enabled on your plan.
Hard cap on credits this request may charge. Results stop at the cap.
Per-request time budget. Slow providers are dropped and the result is partial.
Minimum agreeing sources for consensus.
next_cursor from the previous page.
Return a free estimate without fetching.
Examples
Head of RevOps in DACH, last 7 days
{ "filters": { "title_or": [ "Head of RevOps", "Head of Revenue Operations" ], "country_code_or": [ "DE", "AT", "CH" ], "posted_within_days": 7 }, "waterfall": { "strategy": "cheapest_first", "max_credits": 100, "timeout_ms": 10000 }, "limit": 25}Free estimate before fetching
{ "filters": { "title_or": [ "Head of RevOps" ], "country_code_or": [ "DE", "AT", "CH" ], "posted_within_days": 7 }, "waterfall": { "strategy": "max_coverage" }, "limit": 100, "dry_run": true}Responses
Section titled “ Responses ”Search results, or an estimate when dry_run is true.
object
A canonical job: one real opening, merged from every source that saw it.
Null semantics: null = unknown; [] = verified none; field omitted = not part of this payload
(for example the lifecycle-only job in job.closed and job.reposted events).
object
Canonical job id. Stable across sources and requests.
Company reference embedded in jobs, events and profiles.
object
Primary web domain. null when unknown.
object
ISO 3166-1 alpha-2.
true remote, false on-site or hybrid, null unknown.
Plain-text job description.
Date the employer posted the job, if known.
Earliest time any source saw the job.
Latest time any source saw the job.
Latest time the job was confirmed live at its origin.
Why the job closed. null while open.
Times the same job was re-listed.
Probability (0-1) that the job is a real open req, from cross-source corroboration.
One provider’s view of the canonical job.
object
Source slug. betterjobs is the BetterJobs index.
Job fields this source contributed to the canonical record.
object
You may show this job to your end users.
You may resell or redistribute this job as data.
Pass as cursor for the next page. null = last page.
object
partial = at least one provider failed or timed out. You are billed only for returned jobs.
Returned jobs you had already paid for (free).
Provider records merged into existing canonical jobs (free).
object
Providers that returned at least one match.
object
Source slug. betterjobs is the BetterJobs index.
provider_timeout = the provider missed timeout_ms. provider_error = the provider returned an error.
Unique jobs each provider contributed first.
object
Fraction (0-1) of returned jobs with a non-null value, per field.
object
Returned when dry_run is true. Free.
object
object
All unique jobs matching filters under this waterfall, across every page. Independent of limit.
object
Credits the same request without dry_run would charge for one page of limit jobs, after already-paid jobs and waterfall.max_credits.
object
Examples
Complete result
{ "data": [ { "id": "job_01JC8X4M2Q7RV3T9KD5W6YH0AB", "title": "Head of Revenue Operations", "company": { "id": "cmp_4Rk7TzP1aQ", "name": "Acme Robotics", "domain": "acme-robotics.example" }, "location": { "city": "Berlin", "region": "Berlin", "country_code": "DE", "remote": false }, "employment_type": "full_time", "seniority": "lead", "job_family": "operations", "salary": { "min": 110000, "max": 135000, "currency": "EUR", "period": "year", "origin": "declared" }, "description": "Acme Robotics is hiring a Head of Revenue Operations to own forecasting, CRM hygiene and the GTM tool stack across DACH.", "apply_url": "https://jobs.acme-robotics.example/revops-lead/apply", "posted_at": "2026-10-08T00:00:00Z", "first_seen_at": "2026-10-08T06:40:00Z", "last_seen_at": "2026-10-11T06:10:00Z", "last_verified_at": "2026-10-11T06:10:00Z", "status": "open", "closed_reason": null, "repost_count": 0, "p_real": 0.94, "sources": [ { "provider": "betterjobs", "provider_job_id": "bj_idx_5521907", "url": "https://jobs.acme-robotics.example/revops-lead", "first_seen_at": "2026-10-08T06:40:00Z", "last_seen_at": "2026-10-11T06:10:00Z", "fields": [ "title", "description", "apply_url", "location", "employment_type", "posted_at" ] }, { "provider": "theirstack", "provider_job_id": "ts_88213377", "url": "https://jobs.acme-robotics.example/revops-lead", "first_seen_at": "2026-10-08T07:12:00Z", "last_seen_at": "2026-10-11T04:02:00Z", "fields": [ "salary", "seniority" ] }, { "provider": "techmap", "provider_job_id": "tm_3f9a2c71", "url": "https://boards.example/acme-robotics/revops-lead", "first_seen_at": "2026-10-08T09:30:00Z", "last_seen_at": "2026-10-10T23:40:00Z", "fields": [ "job_family" ] } ], "license": { "display": true, "resale": false } } ], "next_cursor": "cur_8fJ2kQ", "metadata": { "request_id": "req_7Hc2LmQ9xT", "status": "complete", "credits_charged": 1, "jobs_already_paid": 0, "duplicates_merged": 2, "credits_remaining": 9841, "providers": { "tried": [ "betterjobs", "techmap", "theirstack" ], "hit": [ "betterjobs", "techmap", "theirstack" ], "failed": [], "contributions": { "betterjobs": 1, "techmap": 0, "theirstack": 0 } }, "field_coverage": { "salary": 1, "seniority": 1, "location.remote": 1, "description": 1 }, "latency_ms": 1840 }}Partial result (one provider timed out)
{ "data": [ { "id": "job_01JC8X4M2Q7RV3T9KD5W6YH0AB", "title": "Head of Revenue Operations", "company": { "id": "cmp_4Rk7TzP1aQ", "name": "Acme Robotics", "domain": "acme-robotics.example" }, "location": { "city": "Berlin", "region": "Berlin", "country_code": "DE", "remote": false }, "employment_type": "full_time", "seniority": "lead", "job_family": "operations", "salary": { "min": 110000, "max": 135000, "currency": "EUR", "period": "year", "origin": "declared" }, "description": "Acme Robotics is hiring a Head of Revenue Operations to own forecasting, CRM hygiene and the GTM tool stack across DACH.", "apply_url": "https://jobs.acme-robotics.example/revops-lead/apply", "posted_at": "2026-10-08T00:00:00Z", "first_seen_at": "2026-10-08T06:40:00Z", "last_seen_at": "2026-10-11T06:10:00Z", "last_verified_at": "2026-10-11T06:10:00Z", "status": "open", "closed_reason": null, "repost_count": 0, "p_real": 0.94, "sources": [ { "provider": "betterjobs", "provider_job_id": "bj_idx_5521907", "url": "https://jobs.acme-robotics.example/revops-lead", "first_seen_at": "2026-10-08T06:40:00Z", "last_seen_at": "2026-10-11T06:10:00Z", "fields": [ "title", "description", "apply_url", "location", "employment_type", "posted_at" ] }, { "provider": "theirstack", "provider_job_id": "ts_88213377", "url": "https://jobs.acme-robotics.example/revops-lead", "first_seen_at": "2026-10-08T07:12:00Z", "last_seen_at": "2026-10-11T04:02:00Z", "fields": [ "salary", "seniority" ] }, { "provider": "techmap", "provider_job_id": "tm_3f9a2c71", "url": "https://boards.example/acme-robotics/revops-lead", "first_seen_at": "2026-10-08T09:30:00Z", "last_seen_at": "2026-10-10T23:40:00Z", "fields": [ "job_family" ] } ], "license": { "display": true, "resale": false } } ], "next_cursor": null, "metadata": { "request_id": "req_9Qa4NvB2sE", "status": "partial", "credits_charged": 1, "jobs_already_paid": 0, "duplicates_merged": 1, "credits_remaining": 9840, "providers": { "tried": [ "betterjobs", "techmap", "theirstack", "coresignal" ], "hit": [ "betterjobs", "techmap", "theirstack" ], "failed": [ { "provider": "coresignal", "code": "provider_timeout" } ], "contributions": { "betterjobs": 1, "techmap": 0, "theirstack": 0 } }, "field_coverage": { "salary": 1, "seniority": 1, "location.remote": 1, "description": 1 }, "latency_ms": 10012 }}Dry run estimate (free)
{ "request_id": "req_3Kd8PwZ1uY", "estimate": { "expected_unique_jobs_range": { "min": 40, "max": 75 }, "providers_planned": [ "betterjobs", "reqbeat", "signalsapi", "theirstack", "jobspipe", "coresignal", "techmap" ], "credits_range": { "min": 40, "max": 75 } }}Headers
Section titled “Headers”Example
req_7Hc2LmQ9xTUnique id for this request. Quote it to support.
Example
1Credits charged by this request.
Example
9841Credits left on your account after this request.
Example
60Requests allowed in the current window.
Example
59Requests left in the current window.
Example
42Seconds until the window resets.
Example
2026-10-01API version used to serve this request.
Invalid request (invalid_request) or unknown filter field (unknown_filter).
object
object
Present on insufficient_credits.
Present on insufficient_credits and plan_required.
Offending field on invalid_request and unknown_filter.
Examples
unknown_filter
{ "error": { "type": "invalid_request_error", "code": "unknown_filter", "message": "Unknown filter field 'job_title_or'. Did you mean 'title_or'?", "param": "filters.job_title_or", "doc_url": "https://docs.betterjobs.cc/platform/errors/#unknown_filter", "request_id": "req_4Bn8CxV2zA" }}invalid_request
{ "error": { "type": "invalid_request_error", "code": "invalid_request", "message": "limit must be between 1 and 100.", "param": "limit", "doc_url": "https://docs.betterjobs.cc/platform/errors/#invalid_request", "request_id": "req_2Cm7DwB1yZ" }}Headers
Section titled “Headers”Example
req_7Hc2LmQ9xTUnique id for this request. Quote it to support.
Missing or invalid API key.
object
object
Present on insufficient_credits.
Present on insufficient_credits and plan_required.
Offending field on invalid_request and unknown_filter.
Examples
{ "error": { "type": "authentication_error", "code": "unauthorized", "message": "Missing or invalid API key. Send 'Authorization Bearer bj_live_...'.", "doc_url": "https://docs.betterjobs.cc/platform/errors/#unauthorized", "request_id": "req_5Dl6EvN0xY" }}Headers
Section titled “Headers”Example
req_7Hc2LmQ9xTUnique id for this request. Quote it to support.
Not enough credits for this request.
object
object
Present on insufficient_credits.
Present on insufficient_credits and plan_required.
Offending field on invalid_request and unknown_filter.
Examples
{ "error": { "type": "billing_error", "code": "insufficient_credits", "message": "This request needs at least 25 credits; 3 remain.", "credits_needed": 25, "upgrade_url": "https://betterjobs.cc/pricing", "doc_url": "https://docs.betterjobs.cc/platform/errors/#insufficient_credits", "request_id": "req_6Ek5FuM9wX" }}Headers
Section titled “Headers”Example
req_7Hc2LmQ9xTUnique id for this request. Quote it to support.
Your plan does not include this feature or provider.
object
object
Present on insufficient_credits.
Present on insufficient_credits and plan_required.
Offending field on invalid_request and unknown_filter.
Examples
{ "error": { "type": "permission_error", "code": "plan_required", "message": "Provider 'coresignal' is not enabled on your plan. Pro and above include all six providers.", "required_plan": "pro", "upgrade_url": "https://betterjobs.cc/pricing", "doc_url": "https://docs.betterjobs.cc/platform/errors/#plan_required", "request_id": "req_7Fj4GtL8vW" }}Headers
Section titled “Headers”Example
req_7Hc2LmQ9xTUnique id for this request. Quote it to support.
Same Idempotency-Key reused with a different body.
object
object
Present on insufficient_credits.
Present on insufficient_credits and plan_required.
Offending field on invalid_request and unknown_filter.
Examples
{ "error": { "type": "conflict_error", "code": "idempotency_conflict", "message": "Idempotency-Key '7b8f2c4e-1a3d-4f5b-9c6e-0d2a4b6c8e1f' was used with a different request body.", "doc_url": "https://docs.betterjobs.cc/platform/errors/#idempotency_conflict", "request_id": "req_9Hh2IrJ6tU" }}Headers
Section titled “Headers”Example
req_7Hc2LmQ9xTUnique id for this request. Quote it to support.
Too many requests. Wait Retry-After seconds.
object
object
Present on insufficient_credits.
Present on insufficient_credits and plan_required.
Offending field on invalid_request and unknown_filter.
Examples
{ "error": { "type": "rate_limit_error", "code": "rate_limited", "message": "Rate limit exceeded. Retry after 12 seconds.", "doc_url": "https://docs.betterjobs.cc/platform/errors/#rate_limited", "request_id": "req_0Ig1JqH5sT" }}Headers
Section titled “Headers”Example
req_7Hc2LmQ9xTUnique id for this request. Quote it to support.
Example
12Seconds to wait before retrying.
Example
60Requests allowed in the current window.
Example
59Requests left in the current window.
Example
42Seconds until the window resets.
Something failed on our side. Safe to retry with the same Idempotency-Key.
object
object
Present on insufficient_credits.
Present on insufficient_credits and plan_required.
Offending field on invalid_request and unknown_filter.
Examples
{ "error": { "type": "api_error", "code": "internal_error", "message": "Unexpected error. Retry with the same Idempotency-Key; you will not be charged twice.", "doc_url": "https://docs.betterjobs.cc/platform/errors/#internal_error", "request_id": "req_1Jf0KpG4rS" }}Headers
Section titled “Headers”Example
req_7Hc2LmQ9xTUnique id for this request. Quote it to support.