Eightfold Jobs API.
Pull enterprise job listings, full descriptions, and department data from Eightfold's REST APIs, auto-detecting whether a tenant runs the SmartApply or the newer PCSX endpoint pattern.
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 Eightfold.
- Full HTML Job Descriptions
- Department & Business Unit
- Work Location & Remote Flexibility
- Posting & Expiry Dates
- ATS & Display Job IDs
- Apply Redirect URLs
- 01Enterprise Job Aggregation
- 02Competitive Hiring Intelligence
- 03Talent Market Research
- 04Job Board Syndication
How to scrape Eightfold.
Step-by-step guide to extracting jobs from Eightfold-powered career pages—endpoints, authentication, and working code.
import requests
company = "aexp" # American Express
domain = "aexp.com"
base_url = f"https://{company}.eightfold.ai"
# Try SmartApply API first
smartapply_url = f"{base_url}/api/apply/v2/jobs?domain={domain}&hl=en&start=0"
headers = {"Accept": "application/json", "Content-Type": "application/json"}
response = requests.get(smartapply_url, headers=headers)
if response.status_code == 200:
data = response.json()
jobs = data.get("positions", [])
total = data.get("totalJobs", 0)
print(f"SmartApply: Found {total} jobs, showing {len(jobs)}")
else:
# Try PCSX API as fallback
pcsx_url = f"{base_url}/api/pcsx/search?domain={domain}&query=&location=&start=0"
response = requests.get(pcsx_url, headers=headers)
if response.status_code == 200:
data = response.json().get("data", {})
jobs = data.get("positions", [])
print(f"PCSX: Found {len(jobs)} jobs")import requests
import re
import xml.etree.ElementTree as ET
company = "aexp"
domain = "aexp.com"
sitemap_url = f"https://{company}.eightfold.ai/careers/sitemap.xml?domain={domain}"
response = requests.get(sitemap_url)
root = ET.fromstring(response.content)
# Extract job IDs from URLs
job_ids = []
namespace = {"ns": "http://www.sitemaps.org/schemas/sitemap/0.9"}
for url in root.findall(".//ns:url/ns:loc", namespace):
match = re.search(r"/careers/job/(d+)", url.text)
if match:
job_ids.append(match.group(1))
print(f"Found {len(job_ids)} job IDs in sitemap")import requests
import time
company = "aexp"
domain = "aexp.com"
job_id = "39751247"
base_url = f"https://{company}.eightfold.ai"
headers = {"Accept": "application/json", "Content-Type": "application/json"}
# SmartApply details endpoint
details_url = f"{base_url}/api/apply/v2/jobs/{job_id}?domain={domain}&hl=en"
response = requests.get(details_url, headers=headers)
if response.status_code == 200:
job = response.json()
print({
"id": job.get("id"),
"title": job.get("name"),
"location": job.get("location"),
"department": job.get("department"),
"description": job.get("job_description", "")[:200] + "...",
"url": job.get("canonicalPositionUrl"),
})
# For PCSX, use: /api/pcsx/position_details?position_id={job_id}&domain={domain}import requests
import time
company = "aexp"
domain = "aexp.com"
base_url = f"https://{company}.eightfold.ai"
headers = {"Accept": "application/json", "Content-Type": "application/json"}
all_jobs = []
start = 0
batch_size = 10
while True:
url = f"{base_url}/api/apply/v2/jobs?domain={domain}&hl=en&start={start}"
response = requests.get(url, headers=headers)
if response.status_code != 200:
break
data = response.json()
positions = data.get("positions", [])
if not positions:
break
all_jobs.extend(positions)
print(f"Fetched {len(positions)} jobs (total: {len(all_jobs)})")
start += batch_size
time.sleep(0.5) # Rate limiting
print(f"Total jobs collected: {len(all_jobs)}")import re
import requests
from urllib.parse import urlparse
# Known company -> domain overrides where the subdomain differs from the domain
KNOWN_DOMAINS = {
"aexp": "aexp.com",
"americanexpress": "aexp.com",
"starbucks": "starbucks.com",
"paypal": "paypal.com",
"siemens": "siemens.com",
}
def infer_domain(careers_url: str) -> str | None:
"""Best-effort domain guess from an eightfold.ai subdomain."""
host = urlparse(careers_url).netloc.lower()
if host.endswith(".eightfold.ai"):
slug = host[: -len(".eightfold.ai")]
return KNOWN_DOMAINS.get(slug, f"{slug}.com")
return None # custom/vanity host -> resolve from the page instead
def extract_embedded_domain(careers_url: str) -> str | None:
"""Vanity hosts embed the real domain param in the careers page HTML."""
html = requests.get(careers_url).text
match = re.search(r'"domain"s*:s*"([A-Za-z0-9.-]+)"', html)
return match.group(1).lower() if match else None
def resolve_domain(careers_url: str) -> str | None:
return infer_domain(careers_url) or extract_embedded_domain(careers_url)
# Standard subdomain
print(resolve_domain("https://aexp.eightfold.ai/careers")) # aexp.com
# Custom / vanity host (e.g. talent.bayer.com) -> read from page HTML
print(resolve_domain("https://talent.bayer.com/careers"))Probe the SmartApply endpoint first (/api/apply/v2/jobs). If it 404s or returns an unexpected shape, fall back to the PCSX endpoint (/api/pcsx/search). Tenants run one or the other, so cache the detected type per company.
The listings endpoint omits descriptions. Make a separate call to the job details endpoint (/api/apply/v2/jobs/{id} or /api/pcsx/position_details) for each job to retrieve the full HTML description.
The domain query param must match the tenant's configured domain, not the subdomain slug. Read it from the sitemap URL in robots.txt (it carries ?domain=...) or from the "domain" value embedded in the careers page HTML.
Some tenants run on vanity hosts (e.g. talent.bayer.com) instead of a *.eightfold.ai subdomain. Match against an allowlist of approved hosts and resolve the domain param from the page rather than guessing it from host segments.
Space requests out (200ms+ between calls) and cap concurrent detail fetches. Use the sitemap to collect all IDs first, then pull details at a controlled rate to avoid 403/429 responses.
The sitemap and API refresh on different schedules, so counts drift slightly (e.g. 529 vs 534). Use the sitemap for discovery and treat API totalJobs as a soft validation check.
- 1Use the sitemap for complete job discovery without pagination
- 2Probe SmartApply first, fall back to PCSX, and cache the detected type per tenant
- 3Fetch job details separately since listings omit descriptions
- 4Add a 200ms+ delay between requests and cap concurrent detail fetches
- 5Resolve the domain param from robots.txt or the embedded page value, not the slug
- 6Handle both standard eightfold.ai subdomains and approved vanity hosts
One endpoint. All Eightfold jobs. No scraping, no sessions, no maintenance.
Get API accesscurl "https://connect.jobo.world/api/jobs?sources=eightfold" \
-H "X-Api-Key: YOUR_KEY" Access Eightfold
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.