All platforms

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.

Get API access
Hireology
Live
<3haverage discovery time
1hrefresh interval
Developer tools

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.

Data fields
  • Full Job Descriptions
  • Per-Store Locations
  • Employment Status
  • Job Family & Organization
  • Remote & Blind-Posted Flags
  • Job Post Dates
Use cases
  1. 01Multi-Location Hiring Feeds
  2. 02Retail & Automotive Job Boards
  3. 03Healthcare Staffing Aggregation
  4. 04Local Job Alerts
DIY GUIDE

How to scrape Hireology.

Step-by-step guide to extracting jobs from Hireology-powered career pages—endpoints, authentication, and working code.

HybridadvancedNo published limit; self-throttle to a single request every ~2s. Hireology answers bursts with HTTP 403 (blocked) or 429 (rate limited).Auth required

Bootstrap the apiToken from the career page

The public v2 API is bearer-protected, but the token is minted in the server-rendered career page. GET https://careers.hireology.com/{slug} and regex the JWT out of the inline `var startingData = {...,"apiToken":"eyJ..."}` blob.

Step 1: Bootstrap the apiToken from the career page
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]}...")

Fetch a page of listings

Call the public listings endpoint with the bearer token. The response already carries the full job_description, locations, and organization for every job, so no per-job detail request is needed. pageSize is fixed at 10 server-side.

Step 2: Fetch a page of listings
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 [])}")

Paginate and refresh the token on 401

Walk the page parameter until you reach the reported count (or hit a short page when count is absent). If a cached token expires mid-run the API returns 401 — re-bootstrap once and retry the same page. Space requests ~2s apart with a single worker.

Step 3: Paginate and refresh the token on 401
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")

Normalize each job record

Prefer the server-supplied career_site_url and application_path, falling back to synthesized URLs. Flatten address/city/state into location strings and lift employment_status, organization, and posted date out of the payload.

Step 4: Normalize each job record
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")
Common issues
highThe v2 API returns 401 without a bearer token

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.

mediumCached JWT expires part-way through a scrape

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.

mediumpageSize is ignored and always returns 10 jobs

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.

lowMulti-store tenants use a different child slug in job URLs

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.

mediumBursts trigger 403 blocks or 429 throttling

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.

Best practices
  1. 1Bootstrap the apiToken once per slug and cache it; decode the JWT exp claim and refresh a couple of minutes early
  2. 2Read job_description straight from the listings payload — no per-job detail request is needed
  3. 3Prefer server-supplied career_site_url and application_path over hand-built URLs
  4. 4Treat a 401 as an expired token: re-bootstrap from the career page and retry the page once
  5. 5Keep to a single worker with a ~2s delay; back off on 403/429 responses
  6. 6Stop paginating on the reported count, or on a short page when count is missing
Or skip the complexity

One endpoint. All Hireology jobs. No scraping, no sessions, no maintenance.

Get API access
cURL
curl "https://connect.jobo.world/api/jobs?sources=hireology" \
  -H "X-Api-Key: YOUR_KEY"
Ready to integrate

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.

99.9%API uptime
<200msAvg response
50M+Jobs processed