Paradox Jobs API.

Reach the high-volume hourly and frontline roles that Olivia recruits for — including McDonald's — by reading the Algolia search index behind Paradox career sites and normalizing every hit to clean JSON.

Get API access

What's in every response.

Data fields, real-world applications, and the companies already running on Paradox.

Data fields

  • Full Job Descriptions
  • Native Requisition IDs
  • Location & Address Data
  • Department & Contract Type
  • Displayed Salary Ranges
  • Direct Apply URLs

Use cases

  1. 01Hourly & Frontline Hiring Data
  2. 02Retail & QSR Job Aggregation
  3. 03Location-Based Job Feeds
  4. 04Salary & Contract Benchmarking

Trusted by

  • McDonald's
DIY GUIDE

How to scrape Paradox.

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

API type
Hybrid
Difficulty
advanced
Rate limit
No published limit; self-throttle to ~2 requests/sec (500ms delay)
Authentication
No auth

Locate the deployment's Algolia credentials

Generic *.paradox.ai tenant boards are AWS-WAF protected and expose no anonymous browserless API, so target a deployment that fronts a search index instead (for example McDonald's UK). Read the application ID, search-only key, and index name from the site's live network requests — the search-only key is safe to expose client-side.

Step 1: Locate the deployment's Algolia credentials
import requests

# Per-deployment values, read from the career site's browser network calls.
APP_ID = "RVMOB42DFH"                            # McDonald's UK deployment
SEARCH_KEY = "0a69e536b78a0eb7abf95cf3331caf64"  # anonymous search-only key
INDEX = "production__mcdscare2501__sort-rank"

ENDPOINT = (
    f"https://{APP_ID.lower()}-dsn.algolia.net/1/indexes/*/queries"
    f"?x-algolia-api-key={SEARCH_KEY}&x-algolia-application-id={APP_ID}"
)
HEADERS = {
    "Accept": "application/json",
    "Content-Type": "application/json",
    "Origin": "https://people.mcdonalds.co.uk",
    "Referer": "https://people.mcdonalds.co.uk/job-search",
}

Query the Algolia multi-query endpoint

POST an Algolia multi-query request wrapping the index name and a URL-encoded params string. The response carries results[0] with a hits array, plus nbHits and nbPages totals. Send Origin and Referer headers matching the career site or Algolia's allowed-origins check will reject the call.

Step 2: Query the Algolia multi-query endpoint
from urllib.parse import urlencode

def fetch_page(page: int) -> dict:
    params = urlencode({
        "facetFilters": '[["country:United Kingdom"]]',
        "facets": '["business_area","contract_type","country"]',
        "getRankingInfo": "false",
        "highlightPostTag": "",
        "highlightPreTag": "",
        "hitsPerPage": 20,
        "maxValuesPerFacet": 10,
        "page": page,   # Algolia pages are 0-based
        "query": "",
    })
    body = {"requests": [{"indexName": INDEX, "params": params}]}
    resp = requests.post(ENDPOINT, json=body, headers=HEADERS, timeout=30)
    resp.raise_for_status()
    return resp.json()["results"][0]

Page through the full index

Algolia's page index starts at 0. Walk pages until page + 1 reaches nbPages, then stop to avoid empty over-fetching. Sleep ~500ms between calls.

Step 3: Page through the full index
import time

def fetch_all_hits() -> list[dict]:
    hits, page = [], 0
    while True:
        result = fetch_page(page)
        hits.extend(result.get("hits", []))
        if page + 1 >= result.get("nbPages", 0):
            break
        page += 1
        time.sleep(0.5)   # self-throttle
    return hits

Map hits to normalized job records

Use ats_requisition_id as the stable external identity and jd_url / apply_url for the listing and apply links. Drop any hit missing a native requisition ID or a valid apply URL rather than emitting partial jobs.

Step 4: Map hits to normalized job records
def map_hit(hit: dict) -> dict | None:
    req_id = hit.get("ats_requisition_id")
    apply_url = hit.get("apply_url")
    if not req_id or not apply_url:   # defensive: skip incomplete rows
        return None
    return {
        "external_id": req_id,
        "title": (hit.get("title") or "").strip(),
        "description": (hit.get("description") or "").strip(),
        "listing_url": hit.get("jd_url"),
        "apply_url": apply_url,
        "location": hit.get("display_address") or hit.get("display_location"),
        "department": hit.get("department"),
        "contract_type": hit.get("contract_type"),
        "salary": hit.get("display_salary"),
    }

jobs = [j for h in fetch_all_hits() if (j := map_hit(h))]
Common issues
highGeneric *.paradox.ai tenant boards return AWS WAF challenge assets and expose no anonymous, browserless job API or DOM.
Only scrape deployments that front a search index (like the McDonald's Algolia endpoint) server-side; treat other paradox.ai tenants as browser-only and out of scope for a plain HTTP client.
highThe Algolia application ID, search-only key, and index name are unique to each Paradox customer and change between deployments.
Extract these values from the target site's live network traffic (the multi-query request URL and payload) before scraping — never assume one tenant's credentials work for another.
mediumRequests without matching Origin and Referer headers can be refused by Algolia's allowed-origins configuration.
Always send Origin and Referer headers that match the career site (e.g. https://people.mcdonalds.co.uk).
mediumAlgolia pagination is 0-based, so off-by-one loops either skip the first page or over-fetch empty pages past the end.
Start at page 0 and stop once page + 1 reaches the nbPages value returned in the response.
lowSome hits omit ats_requisition_id or carry a non-Paradox apply URL, which breaks stable job identity.
Skip rows lacking a native requisition ID and a valid Paradox/McHire apply URL rather than emitting partial records.
Best practices
  1. 1Confirm the target Paradox site exposes a search-index API before building — many tenants are WAF-gated and browser-only.
  2. 2Discover the Algolia application ID, search-only key, and index name from the site's live network requests; they differ per customer.
  3. 3Send Origin and Referer headers that match the career site to satisfy Algolia's allowed-origins check.
  4. 4Throttle to roughly two requests per second (about 500ms apart).
  5. 5Persist ats_requisition_id as the stable job identity; treat objectID and the apply-URL job_id as secondary metadata.
  6. 6Stop paginating once page + 1 reaches nbPages to avoid redundant empty calls.
Or skip the complexity

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

Get API access
cURL
curl "https://connect.jobo.world/api/jobs?sources=paradox" \
  -H "X-Api-Key: YOUR_KEY"
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.

Ready to integrate

Access Paradox
job data today.

One API call. Structured data. No scraping infrastructure to build or maintain — start with the $5 free starting balance and scale as you grow.

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