- 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.
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.
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
- 01Hourly & Frontline Hiring Data
- 02Retail & QSR Job Aggregation
- 03Location-Based Job Feeds
- 04Salary & Contract Benchmarking
Trusted by
- McDonald's
How to scrape Paradox.
Step-by-step guide to extracting jobs from Paradox-powered career pages—endpoints, authentication, and working code.
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",
}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]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 hitsdef 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))]- 1Confirm the target Paradox site exposes a search-index API before building — many tenants are WAF-gated and browser-only.
- 2Discover the Algolia application ID, search-only key, and index name from the site's live network requests; they differ per customer.
- 3Send Origin and Referer headers that match the career site to satisfy Algolia's allowed-origins check.
- 4Throttle to roughly two requests per second (about 500ms apart).
- 5Persist ats_requisition_id as the stable job identity; treat objectID and the apply-URL job_id as secondary metadata.
- 6Stop paginating once page + 1 reaches nbPages to avoid redundant empty calls.
One endpoint. All Paradox jobs. No scraping, no sessions, no maintenance.
Get API accesscurl "https://connect.jobo.world/api/jobs?sources=paradox" \
-H "X-Api-Key: YOUR_KEY"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.
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.