Authentication
How do I authenticate, and what is the difference between live, test and keyless sandbox access?
Every request except the sandbox sends an API key as a Bearer token:
Authorization: Bearer bj_live_...A missing or invalid key returns 401 unauthorized. See Errors.
Three ways in
Section titled “Three ways in”| Access | Key | Data | Credits | Use it for |
|---|---|---|---|---|
| Keyless sandbox | None | Fixed illustrative data | Never charged | A first call, demos, docs examples. Only POST /v1/sandbox/jobs/search. |
| Test key | bj_test_... |
Sandbox data | Never charged | Development, CI, integration tests. |
| Live key | bj_live_... |
Live results from the BetterJobs index and the providers on your plan | Charged per unique job | Production. |
All three use the same base URL, https://api.betterjobs.cc/v1, and the same request and response shapes. Switching from test to live is a key change, not a code change.
Send the key
Section titled “Send the key”Read the key from an environment variable. Also pin the API version with BetterJobs-Version, so new versions never change your responses. See Versioning.
export BETTERJOBS_API_KEY="bj_live_..."
curl https://api.betterjobs.cc/v1/account \ -H "Authorization: Bearer $BETTERJOBS_API_KEY" \ -H "BetterJobs-Version: 2026-10-01"import osimport requests
session = requests.Session()session.headers.update({ "Authorization": f"Bearer {os.environ['BETTERJOBS_API_KEY']}", "BetterJobs-Version": "2026-10-01",})
account = session.get("https://api.betterjobs.cc/v1/account")account.raise_for_status()print(account.json()["plan"], account.json()["credits_remaining"])const apiKey = process.env.BETTERJOBS_API_KEY;if (!apiKey) throw new Error('BETTERJOBS_API_KEY is not set');
const resp = await fetch('https://api.betterjobs.cc/v1/account', { headers: { Authorization: `Bearer ${apiKey}`, 'BetterJobs-Version': '2026-10-01', },});if (!resp.ok) throw new Error(`BetterJobs ${resp.status}`);const account = await resp.json();console.log(account.plan, account.credits_remaining);GET /v1/account is free. It returns your plan, credits left, when they reset, the providers your key can use and your rate limit. Use it as a health check after you set or rotate a key. See Get your account.
Keep keys server-side
Section titled “Keep keys server-side”A live key spends your credits. Anyone who has it can run up your bill.
- Never put a key in browser JavaScript, a mobile app, a public repo or a shared spreadsheet.
- Call BetterJobs from your backend. If a frontend needs job data, proxy the request through your server and keep the key there.
- No-code tools such as Clay, n8n, Make and Zapier store the key in their server-side connection or header settings. Put it there, not in a cell or a URL.
- Set
waterfall.max_creditson every search, so even a leaked key cannot drain your account in one request.
Rotate a key
Section titled “Rotate a key”Rotate on a schedule and whenever someone with access leaves.
- Create a new key in your dashboard. The old one keeps working.
- Deploy the new key to every place that uses the old one.
- Call
GET /v1/accountwith the new key to confirm it works. - Revoke the old key in your dashboard.
If a key leaks, revoke it first and rotate after. Then check GET /v1/billing/ledger for charges you do not recognise. Each entry names the request_id and operation that charged it. See Get the billing ledger.
Agents and MCP
Section titled “Agents and MCP”The MCP server takes the same key as a Bearer token, or OAuth where your client supports it. See MCP server.