Hireology Jobs API.
Pull full descriptions, per-store locations, and employment status for multi-location retail, automotive, and healthcare employers straight from one JSON listings endpoint.
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 Hireology.
- Full Job Descriptions
- Per-Store Locations
- Employment Status
- Job Family & Organization
- Remote & Blind-Posted Flags
- Job Post Dates
- 01Multi-Location Hiring Feeds
- 02Retail & Automotive Job Boards
- 03Healthcare Staffing Aggregation
- 04Local Job Alerts
How to scrape Hireology.
Step-by-step guide to extracting jobs from Hireology-powered career pages—endpoints, authentication, and working code.
import re
import requests
CAREERS_HOST = "https://careers.hireology.com"
API_BASE = "https://api.hireology.com/v2"
# The short-lived JWT sits in the inline startingData blob on the SSR page.
API_TOKEN_RE = re.compile(
r'"apiToken"\s*:\s*"(eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+)"'
)
def bootstrap_token(slug: str) -> str:
resp = requests.get(f"{CAREERS_HOST}/{slug}", timeout=30)
resp.raise_for_status()
match = API_TOKEN_RE.search(resp.text)
if not match:
raise RuntimeError(f"Could not extract apiToken for {slug}")
return match.group(1)
token = bootstrap_token("theindigoroadhospitalitygroup")
print(f"Bootstrapped token: {token[:12]}...")PAGE_SIZE = 10 # server-fixed: larger pageSize values are ignored
def fetch_page(slug: str, token: str, page: int) -> dict:
url = f"{API_BASE}/public/careers/{slug}"
headers = {
"Authorization": f"Bearer {token}",
"Accept": "application/json",
}
resp = requests.get(
url,
params={"page": page, "pageSize": PAGE_SIZE},
headers=headers,
timeout=30,
)
resp.raise_for_status()
return resp.json()
body = fetch_page("theindigoroadhospitalitygroup", token, 1)
print(f"count={body.get('count')} page_size={body.get('page_size')}")
print(f"jobs on page 1: {len(body.get('data') or [])}")import time
def scrape_all(slug: str) -> list:
token = bootstrap_token(slug)
page, seen, jobs = 1, 0, []
while True:
try:
body = fetch_page(slug, token, page)
except requests.HTTPError as exc:
if exc.response is not None and exc.response.status_code == 401:
token = bootstrap_token(slug) # JWT expired mid-run
body = fetch_page(slug, token, page)
else:
raise
batch = body.get("data") or []
jobs.extend(batch)
total = body.get("count") or 0
page_size = body.get("page_size") or PAGE_SIZE
seen += len(batch)
# Stop on the reported total, or on a short/empty page when count is absent.
if (total and seen >= total) or len(batch) < page_size:
break
page += 1
time.sleep(2) # single worker, ~1 request / 2s
return jobs
all_jobs = scrape_all("theindigoroadhospitalitygroup")
print(f"Collected {len(all_jobs)} jobs")def normalize(job: dict, slug: str) -> dict:
job_id = str(job["id"])
listing_url = (
job.get("career_site_url")
or (CAREERS_HOST + job["career_site_path"] if job.get("career_site_path") else None)
or f"{CAREERS_HOST}/{slug}/{job_id}/description"
)
apply_url = (
CAREERS_HOST + job["application_path"]
if job.get("application_path")
else f"{CAREERS_HOST}/careers/{job_id}/application"
)
locations = [
", ".join(p for p in (loc.get("address"), loc.get("city"), loc.get("state")) if p)
for loc in (job.get("locations") or [])
]
return {
"external_id": job_id,
"title": (job.get("name") or "").strip(),
"description": job.get("job_description") or "",
"listing_url": listing_url,
"apply_url": apply_url,
"locations": [loc for loc in locations if loc],
"company": (job.get("organization") or {}).get("name"),
"employment_status": job.get("employment_status"),
"remote": job.get("remote"),
"posted_at": job.get("created_at"),
}
records = [normalize(job, "theindigoroadhospitalitygroup") for job in all_jobs]
print(f"Normalized {len(records)} records")https://api.hireology.com/v2/public/careers/{slug} is bearer-protected. GET the SSR career page first and regex the apiToken JWT out of the inline `var startingData` blob before calling the API.
Tokens are short-lived (roughly 24h). A cached token can 401 mid-pagination — on a 401, invalidate the token, re-bootstrap from the career page, and retry the same page once before failing.
The server fixes the page size at 10; passing pageSize=50 still returns 10. Walk the page parameter and stop on the reported count instead of requesting one large page.
On multi-location tenants career_site_url may point at a child-store slug that differs from the parent slug you enumerated (e.g. parent theindigoroadhospitalitygroup vs child o-ku-charleston). Trust the server-returned URL rather than rebuilding it.
Hammering the API returns 403 (blocked) or 429 (rate limited). Keep a single worker with a ~2s delay between requests; treat 404 as an unknown tenant slug rather than a transient error.
- 1Bootstrap the apiToken once per slug and cache it; decode the JWT exp claim and refresh a couple of minutes early
- 2Read job_description straight from the listings payload — no per-job detail request is needed
- 3Prefer server-supplied career_site_url and application_path over hand-built URLs
- 4Treat a 401 as an expired token: re-bootstrap from the career page and retry the page once
- 5Keep to a single worker with a ~2s delay; back off on 403/429 responses
- 6Stop paginating on the reported count, or on a short page when count is missing
One endpoint. All Hireology jobs. No scraping, no sessions, no maintenance.
Get API accesscurl "https://connect.jobo.world/api/jobs?sources=hireology" \
-H "X-Api-Key: YOUR_KEY" Access Hireology
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.