Access CleanJobData from Python for data science, analytics, and AI training.
Install the requests library.
pip install requestsFetch job listings with a GET request.
import requests
res = requests.get('https://api.cleanjobdata.com/jobs',
headers={'X-API-Key': 'your-key'},
params={'query': 'software engineer', 'limit': 50})
jobs = res.json()['data']Load job data into pandas for analysis.
import pandas as pd
df = pd.DataFrame(jobs)
print(df['salary'].describe())Real-world patterns beyond the basic setup.
Walks every page with the cursor the API returns, loads the full result set into a DataFrame, and does basic salary analysis — copy-pasteable end to end.
import requests
import pandas as pd
API_KEY = "your-key"
BASE_URL = "https://api.cleanjobdata.com/jobs"
def fetch_all_jobs(title: str, limit: int = 100) -> list[dict]:
jobs = []
params = {"title": title, "limit": limit}
cursor = None
while True:
if cursor:
params["cursor"] = cursor
res = requests.get(BASE_URL, headers={"X-API-Key": API_KEY}, params=params)
res.raise_for_status()
payload = res.json()
jobs.extend(payload["data"])
cursor = payload.get("pagination", {}).get("next_page")
if not cursor:
break
return jobs
jobs = fetch_all_jobs("software engineer")
df = pd.DataFrame(jobs)
# Basic salary analysis on postings that disclose pay
priced = df.dropna(subset=["salary_min", "salary_max"])
print(priced[["salary_min", "salary_max"]].describe())
print(f"{len(priced)}/{len(df)} postings disclosed a salary range")Keep the key out of source control.
# .env
CLEANJOBDATA_API_KEY=cjd_live_your_api_key_hereYes — it's plain REST/JSON, so httpx.AsyncClient or aiohttp work exactly like requests: `async with httpx.AsyncClient() as client: res = await client.get('https://api.cleanjobdata.com/jobs', headers={'X-API-Key': API_KEY}, params={'title': 'engineer', 'remote': 'true'})`. This matters most if you're fanning out requests across several filters or cursors concurrently — watch `X-RateLimit-Remaining`, since concurrent calls burn the per-second bucket faster than sequential ones.
Not a dedicated package — the API is a handful of plain JSON endpoints, so a `requests.Session` with your key set as a default header covers it: `session = requests.Session(); session.headers.update({'X-API-Key': API_KEY})`, then `session.get('https://api.cleanjobdata.com/jobs', params={'limit': 50}).json()['data']`. That's usually all the abstraction a Python project needs on top of the REST API.
The API returns a `next_page` cursor token inside a `pagination` object, not page numbers. Loop: send the request, read `payload['pagination']['next_page']`, and if it's not `None`, pass it back as the `cursor` param on the next request. Stop when it comes back `None` — that's the last page. Don't try to construct or decode the cursor yourself, and don't reuse one across a different filter set.
A 429 response includes `X-RateLimit-Remaining` and `Retry-After` headers. A simple pattern for a long-running pagination loop is to check the response status, and on 429, sleep for `Retry-After` seconds before retrying the same request — don't advance the cursor until the retry succeeds, or you'll silently skip a page.