Skip to content
API v1 preview — endpoints and fields may change before general availability.

OpenAPI and SDKs

Where is the OpenAPI spec, and how do I generate a client from it?

View .md

One OpenAPI 3.1 file describes the whole BetterJobs API: every path, field, enum, error code and credit cost. The API reference on this site is generated from it. You can generate a typed client from the same file, or import it into Postman.

The spec is served at /openapi.yaml:

Terminal window
curl -O https://docs.betterjobs.cc/openapi.yaml

It is the source of truth. When a page on this site and the spec disagree, the spec wins. Please report the mismatch.

Every operation and the webhook carry an x-credit-cost extension that says what the call costs:

x-credit-cost:
credits: 1
per: unique_job
free: [duplicates, already_paid_jobs, empty_pages, dry_run]
note: 1 credit per unique job returned that you have not already paid for.

Agents and internal tools can read it to estimate spend before calling. See Credits and billing.

The preview has no hand-written SDK packages. Generate a client from the spec instead. It stays in sync because you regenerate it from the same file whenever the version changes.

openapi-typescript turns the spec into types. openapi-fetch is a small typed fetch wrapper that uses them.

Terminal window
npm install openapi-fetch
npx openapi-typescript https://docs.betterjobs.cc/openapi.yaml -o src/betterjobs.d.ts
import createClient from "openapi-fetch";
import type { paths } from "./betterjobs";
const betterjobs = createClient<paths>({
baseUrl: "https://api.betterjobs.cc/v1",
headers: {
Authorization: `Bearer ${process.env.BETTERJOBS_API_KEY}`,
"BetterJobs-Version": "2026-10-01",
},
});
const { data, error } = await betterjobs.POST("/jobs/search", {
body: {
filters: { title_or: ["Head of RevOps"], country_code_or: ["DE", "AT", "CH"], posted_within_days: 7 },
limit: 25,
},
});
if (error) throw new Error(`${error.error.code}: ${error.error.message}`);
// The 200 body is a search result, or an estimate when dry_run is true.
if ("estimate" in data) throw new Error("expected results, got an estimate");
console.log(data.metadata.credits_charged, data.data.map((job) => job.title));

Paths, bodies and responses are typed. A filter typo such as job_title_or fails at compile time, before the API would return 400 unknown_filter.

OpenAPI Generator has a python generator. Operation names come from operationId and API classes from tags, so searchJobs under the Jobs tag becomes JobsApi.search_jobs.

Terminal window
npx @openapitools/openapi-generator-cli generate \
-i https://docs.betterjobs.cc/openapi.yaml \
-g python \
-o betterjobs-client \
--package-name betterjobs_client
pip install ./betterjobs-client
import os
import betterjobs_client
from betterjobs_client.models import SearchFilters, SearchRequest
config = betterjobs_client.Configuration(
host="https://api.betterjobs.cc/v1",
access_token=os.environ["BETTERJOBS_API_KEY"],
)
with betterjobs_client.ApiClient(config) as client:
client.set_default_header("BetterJobs-Version", "2026-10-01")
jobs_api = betterjobs_client.JobsApi(client)
result = jobs_api.search_jobs(
SearchRequest(
filters=SearchFilters(title_or=["Head of RevOps"], country_code_or=["DE", "AT", "CH"], posted_within_days=7),
limit=25,
)
)

The same spec works with any OpenAPI 3.1 tool. Run npx @openapitools/openapi-generator-cli list to see every generator, for example go, java, ruby or csharp.

  1. In Postman, click Import.
  2. Paste https://docs.betterjobs.cc/openapi.yaml and confirm. Postman builds a collection with one request per operation.
  3. On the collection, open Authorization, choose Bearer Token, and paste your API key.
  4. Add a BetterJobs-Version: 2026-10-01 header to the requests you use.
  5. Check that the collection’s base URL is https://api.betterjobs.cc/v1.

To try requests without a key, call the sandbox endpoint POST /v1/sandbox/jobs/search. It returns fixed illustrative data and charges nothing.

Insomnia, Bruno and most other API clients import the same URL.