DirectEmployers / JobSync Jobs API.
Tap the JobSync Solr feed behind DirectEmployers member microsites to pull every open requisition — full descriptions, locations, and apply URLs — as clean JSON.
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 DirectEmployers / JobSync.
- Full Job Descriptions
- Structured Location Data
- Requisition IDs & GUIDs
- Direct Apply URLs
- Category, Type & Shift
- Posted & Updated Dates
- 01Enterprise Careers Aggregation
- 02Compliance Feed Monitoring
- 03Requisition Change Tracking
- 04Apply URL Resolution
How to scrape DirectEmployers / JobSync.
Step-by-step guide to extracting jobs from DirectEmployers / JobSync-powered career pages—endpoints, authentication, and working code.
import requests
BASE = "https://prod-search-api.jobsyn.org/api/v1/solr/search"
HEADERS = {"Accept": "application/json", "X-Origin": "pearson.jobs"}
resp = requests.get(BASE, params={"page": 1, "num_items": 15}, headers=HEADERS, timeout=15)
resp.raise_for_status()
data = resp.json()
print(data["pagination"]["total"], "jobs total")
print(len(data["jobs"]), "jobs on this page")import time
import requests
BASE = "https://prod-search-api.jobsyn.org/api/v1/solr/search"
HEADERS = {"Accept": "application/json", "X-Origin": "pearson.jobs"}
def iter_jobs():
page = 1
while True:
resp = requests.get(BASE, params={"page": page, "num_items": 15},
headers=HEADERS, timeout=15)
resp.raise_for_status()
payload = resp.json()
yield from payload.get("jobs", [])
if not payload.get("pagination", {}).get("has_more_pages"):
break
page += 1
time.sleep(2) # single request at a time, ~2s apartdef normalize(job):
guid = (job.get("guid") or "").upper()
if len(guid) != 32:
return None # JobSync rows without a GUID key are not real listings
title_slug = job.get("title_slug") or "job"
slab = (job.get("city_slab_exact") or "").split("::", 1)[0].strip("/")
if slab.lower().endswith("/jobs"):
slab = slab[:-5]
location_slug = slab.replace("/", "-").lower() if slab else "global"
return {
"external_id": guid,
"title": (job.get("title_exact") or "").strip(),
"company": (job.get("company_exact") or "").strip(),
"location": job.get("location_exact"),
"description": job.get("description"),
"requisition_id": job.get("reqid"),
"posted_at": job.get("date_new"),
"updated_at": job.get("date_updated"),
"listing_url": f"https://pearson.jobs/{location_slug}/{title_slug}/{guid}/job/",
"apply_url": f"https://rr.jobsyn.org/{guid}10", # 10 = Pearson view-site id (vsid)
}from time import sleep
import requests
BASE = "https://prod-search-api.jobsyn.org/api/v1/solr/search"
HEADERS = {"Accept": "application/json", "X-Origin": "pearson.jobs"}
def fetch_page(page, retries=3):
for attempt in range(retries):
resp = requests.get(BASE, params={"page": page, "num_items": 15},
headers=HEADERS, timeout=15)
if resp.status_code == 401:
raise RuntimeError("X-Origin header missing or rejected by JobSync")
if resp.status_code in (403, 429):
sleep(5 * (attempt + 1)) # IP blocked or rate limited — back off
continue
resp.raise_for_status()
return resp.json()
raise RuntimeError(f"JobSync kept throttling page {page}")JobSync requires an X-Origin header naming the member microsite (e.g. 'pearson.jobs'). Send it on every request; without it the Solr API rejects the call and the feed is empty.
Datacenter or aggressive IPs get blocked. Crawl single-threaded, keep ~2s between requests, and route through residential or rotating egress if a fixed IP starts returning 403.
The endpoint expects one request at a time. Serialize page fetches with a ~2 second delay and add exponential backoff on 429 instead of retrying immediately.
Some Solr rows omit the guid key and are not resolvable jobs. Treat guid as the native job identity and drop any row whose guid is absent or not exactly 32 characters.
The rr.jobsyn.org redirect needs the microsite's view-site id appended to the guid (10 for Pearson). Using the guid alone or the wrong vsid produces an invalid apply URL.
- 1Always send X-Origin set to the member microsite host (e.g. pearson.jobs).
- 2Crawl single-threaded with ~2 seconds between requests to avoid 403/429.
- 3Use the 32-character guid as the stable job key; skip rows that lack it.
- 4Descriptions ship inline with listings — no separate detail request is needed.
- 5Follow pagination.has_more_pages instead of guessing total_pages.
- 6Keep reqid and buid as metadata; the guid, not reqid, is the native key.
One endpoint. All DirectEmployers / JobSync jobs. No scraping, no sessions, no maintenance.
Get API accesscurl "https://connect.jobo.world/api/jobs?sources=directemployers / jobsync" \
-H "X-Api-Key: YOUR_KEY" Access DirectEmployers / JobSync
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.