# Quick start

## 1. Get an API key

Self-service sign-up is not open yet. To get a key, [contact us](https://jobs-database.de/contact).
Plans and allowances are listed on the [pricing page](https://jobs-database.de/pricing).

Keep the key secret: it is shown to you once, and anyone holding it uses your allowance. The
examples below read it from an environment variable:

```bash
export JOBS_API_KEY="your-api-key"
```

## 2. Make your first request

```bash
curl -i -H "X-API-Key: $JOBS_API_KEY" \
  "https://api.jobs-database.de/v1/jobs?limit=2"
```

The response is a page of jobs:

```json
{
  "items": [
    {
      "id": 536359,
      "title": "Technical Recruiter",
      "organization": "range",
      "url": "https://jobs.ashbyhq.com/range/25b43a09-331a-4a70-a60a-d56473b3287b",
      "date_posted": "2026-10-02T18:07:25.386000+00:00",
      "countries_derived": ["United States"],
      "employment_type": ["FULL_TIME"],
      "ai_work_arrangement": "On-site"
    }
  ],
  "has_more": true,
  "next_cursor": "NTM2MzY1"
}
```

(Shortened — every job carries all 25 fields of the [Field reference](/api-documentation/field-reference).)

The `X-Jobs-Remaining` response header tells you how many jobs are left in this month's
allowance.

## 3. Page through all jobs

Keep requesting with the `next_cursor` of the previous page until `has_more` is `false`.
Use `limit=100` (the maximum) to make the fewest requests.

### Python

```python
import os
import time

import requests

BASE = "https://api.jobs-database.de"
session = requests.Session()
session.headers["X-API-Key"] = os.environ["JOBS_API_KEY"]


def all_jobs(limit: int = 100):
    cursor = None
    while True:
        params = {"limit": limit}
        if cursor:
            params["cursor"] = cursor
        r = session.get(f"{BASE}/v1/jobs", params=params, timeout=30)
        if r.status_code == 429 and "Retry-After" in r.headers:
            time.sleep(int(r.headers["Retry-After"]))  # per-minute rate limit
            continue
        r.raise_for_status()
        page = r.json()
        yield from page["items"]
        if not page["has_more"]:
            return
        cursor = page["next_cursor"]


for job in all_jobs():
    print(job["id"], job["title"], job["organization"])
```

### JavaScript (Node.js 18+)

```javascript
const BASE = "https://api.jobs-database.de";
const headers = { "X-API-Key": process.env.JOBS_API_KEY };

async function* allJobs(limit = 100) {
  let cursor = null;
  while (true) {
    const url = new URL("/v1/jobs", BASE);
    url.searchParams.set("limit", String(limit));
    if (cursor) url.searchParams.set("cursor", cursor);

    const res = await fetch(url, { headers });
    if (res.status === 429 && res.headers.has("Retry-After")) {
      // per-minute rate limit
      await new Promise((r) => setTimeout(r, Number(res.headers.get("Retry-After")) * 1000));
      continue;
    }
    if (!res.ok) throw new Error(`HTTP ${res.status}: ${await res.text()}`);

    const page = await res.json();
    yield* page.items;
    if (!page.has_more) return;
    cursor = page.next_cursor;
  }
}

for await (const job of allJobs()) {
  console.log(job.id, job.title, job.organization);
}
```

A `429` **without** `Retry-After` means the monthly allowance is used up; waiting a minute will
not help. See [Errors & limits](/api-documentation/errors-and-limits).

Call the API from your server or script, not from JavaScript running in a web page: the API
does not send CORS headers, and a key in browser code is visible to every visitor.
