# OpenAPI and SDKs

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

Source: https://docs.betterjobs.cc/platform/openapi-and-sdks/

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

## Download the spec

The spec is served at **[/openapi.yaml](/openapi.yaml)**:

```bash
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:

```yaml
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](https://docs.betterjobs.cc/concepts/credits-and-billing.md).

## Generate a client

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](https://docs.betterjobs.cc/platform/versioning.md) changes.

### TypeScript

[openapi-typescript](https://openapi-ts.dev/) turns the spec into types. [openapi-fetch](https://openapi-ts.dev/openapi-fetch/) is a small typed `fetch` wrapper that uses them.

```bash
npm install openapi-fetch
npx openapi-typescript https://docs.betterjobs.cc/openapi.yaml -o src/betterjobs.d.ts
```

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

### Python

[OpenAPI Generator](https://openapi-generator.tech/) has a `python` generator. Operation names come from `operationId` and API classes from tags, so `searchJobs` under the `Jobs` tag becomes `JobsApi.search_jobs`.

```bash
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
```

```python
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,
        )
    )
```

> Check the generator's OpenAPI 3.1 support
>
> The spec uses OpenAPI 3.1 features such as `type: [string, 'null']` for nullable fields. Generators differ in how fully they support 3.1. Generate, then check that nullable fields come out as optional types (`str | None`, `string | null`) and that models accept unknown properties. New fields can appear within a version. See [Versioning](https://docs.betterjobs.cc/platform/versioning.md#what-can-change-within-a-version).

### Other languages

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

## Import into Postman

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](/api/operations/sandboxsearchjobs/) `POST /v1/sandbox/jobs/search`. It returns fixed illustrative data and charges nothing.

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

## Related

- [API reference](/api/)
- [Versioning](https://docs.betterjobs.cc/platform/versioning.md)
- [llms.txt and Markdown pages](https://docs.betterjobs.cc/agents/llms-txt.md) for feeding these docs to an agent
