Breezy HR Jobs API.
Pull an entire careers board in one public JSON request — full HTML descriptions, salary strings, and remote flags included, with no auth and no pagination to work around.
Try the API.
Test Jobs and Feed endpoints against https://connect.jobo.world with live request/response examples, then copy ready-to-use curl commands.
What's in every response.
Data fields, real-world applications, and the companies already running on Breezy HR.
- Full HTML Job Descriptions
- Free-Text Salary Strings
- Department & Team Labels
- Multi-Location Arrays
- Remote Work Flags
- Employment Type & Published Date
- 01SMB Job Tracking
- 02Startup Recruiting Data
- 03Remote Job Aggregation
- 04Salary Benchmarking
How to scrape Breezy HR.
Step-by-step guide to extracting jobs from Breezy HR-powered career pages—endpoints, authentication, and working code.
import re
def extract_company_slug(careers_url: str) -> str | None:
"""Extract company slug from a Breezy HR URL."""
pattern = r"https?://([^.]+).breezy.hr"
match = re.search(pattern, careers_url)
return match.group(1) if match else None
# Example usage
url = "https://new-incentives.breezy.hr/"
slug = extract_company_slug(url)
print(f"Company slug: {slug}") # Output: new-incentivesimport requests
def fetch_breezy_jobs(company_slug: str) -> list[dict]:
"""Fetch all jobs from a Breezy HR company."""
url = f"https://{company_slug}.breezy.hr/json"
params = {"verbose": "true"}
response = requests.get(url, params=params, timeout=10)
response.raise_for_status()
return response.json()
# Example usage
jobs = fetch_breezy_jobs("new-incentives")
print(f"Found {len(jobs)} active jobs")def parse_job(job: dict) -> dict:
"""Parse a Breezy job object into a clean format."""
location = job.get("location", {}) or {}
return {
"id": job.get("id"),
"friendly_id": job.get("friendly_id"),
"title": job.get("name"),
"department": job.get("department"),
"location": location.get("name", "Not specified"),
"is_remote": location.get("is_remote", False),
"employment_type": job.get("type", {}).get("name"),
"salary": job.get("salary"),
"description_html": job.get("description", ""),
"url": job.get("url"),
"published_date": job.get("published_date"),
"company_name": job.get("company", {}).get("name"),
}
# Parse all jobs
parsed_jobs = [parse_job(job) for job in jobs]
for job in parsed_jobs[:3]:
print(f"{job['title']} - {job['location']}")import requests
def validate_company(company_slug: str) -> dict | None:
"""Validate if a Breezy HR company exists."""
url = f"https://{company_slug}.breezy.hr/json"
params = {"verbose": "false"}
try:
response = requests.get(url, params=params, timeout=10)
if response.status_code == 404:
return None
response.raise_for_status()
jobs = response.json()
# Get company name from first job if available
if jobs:
return {"name": jobs[0].get("company", {}).get("name"), "job_count": len(jobs)}
return {"name": None, "job_count": 0}
except requests.RequestException:
return None
# Example usage
result = validate_company("new-incentives")
if result:
print(f"Valid company with {result['job_count']} jobs")
else:
print("Company not found")import time
import requests
def fetch_with_retry(company_slug: str, max_retries: int = 3) -> list[dict]:
"""Fetch jobs with retry logic and rate limiting."""
url = f"https://{company_slug}.breezy.hr/json"
params = {"verbose": "true"}
for attempt in range(max_retries):
try:
response = requests.get(url, params=params, timeout=10)
if response.status_code == 404:
print(f"Company '{company_slug}' not found")
return []
if response.status_code in (403, 429):
wait_time = (attempt + 1) * 2
print(f"Throttled ({response.status_code}), waiting {wait_time}s...")
time.sleep(wait_time)
continue
response.raise_for_status()
return response.json()
except requests.RequestException as e:
if attempt == max_retries - 1:
print(f"Failed after {max_retries} attempts: {e}")
return []
time.sleep(attempt + 1)
return []
# Pace requests between companies (~2s matches the reference scraper)
companies = ["new-incentives", "duolingo"]
for company in companies:
jobs = fetch_with_retry(company)
print(f"{company}: {len(jobs)} jobs")
time.sleep(2)Add verbose=true to the query string. Without it, /json returns job metadata but omits the HTML description entirely. Use https://{company}.breezy.hr/json?verbose=true for full scrapes.
The subdomain slug is wrong, or the company has moved off Breezy. Confirm the slug against the live careers URL. Invalid subdomains can also redirect to the breezy.hr main site instead of returning 404.
Breezy publishes no rate limit but will throttle bursts. Send one request at a time, add a ~2 second delay between companies, back off exponentially on 403/429, and cache results to cut call volume.
These fields depend on what each company fills out, and salary is a free-text string (e.g. '$6.00 - $8.00 / hr') rather than a structured object. Use .get() with fallbacks and never assume a field is present.
A job may expose a single 'location' object, a 'locations' array, or neither, and remote data lives under is_remote and remote_details. Check which key exists before reading nested country/state/remote fields.
Breezy exposes no company index. Maintain your own slug list built from job-board crawling, and backfill via Wayback Machine or Common Crawl queries against *.breezy.hr/* to find historical subdomains.
- 1Always request /json?verbose=true — descriptions are dropped without it
- 2Use /json?verbose=false for lightweight company validation
- 3Send one request at a time with a ~2s delay, matching the reference scraper config
- 4Cache responses to cut redundant calls and overall request volume
- 5Null-check optional fields like department, salary, and location before use
- 6Derive the company slug from the subdomain to build the JSON URL
One endpoint. All Breezy HR jobs. No scraping, no sessions, no maintenance.
Get API accesscurl "https://connect.jobo.world/api/jobs?sources=breezy hr" \
-H "X-Api-Key: YOUR_KEY" Access Breezy HR
job data today.
One API call. Structured data. No scraping infrastructure to build or maintain — start with the free tier and scale as you grow.