- highTwo different API patterns exist (SmartApply vs PCSX)
- 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.
- highListings API returns empty job descriptions
- 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.
- mediumWrong domain parameter causes 404 errors
- 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.
- mediumCustom / vanity hosts require special handling
- 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.
- mediumRate limiting on high-volume requests
- 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.
- lowSitemap job count differs from API totalJobs
- 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.
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.
What's in every response.
Data fields, real-world applications, and the companies already running on Eightfold.
Data fields
- Full HTML Job Descriptions
- Department & Business Unit
- Work Location & Remote Flexibility
- Posting & Expiry Dates
- ATS & Display Job IDs
- Apply Redirect URLs
Use cases
- 01Enterprise Job Aggregation
- 02Competitive Hiring Intelligence
- 03Talent Market Research
- 04Job Board Syndication
Trusted by
- American Express
- Starbucks
- PayPal
- Siemens
- Bayer
- Eaton
- Morgan Stanley
- NTT Data
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"))- 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"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 Eightfold
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.