- highSite ID not found in page source
- The domain_id may be in different locations depending on the page version. Try multiple regex patterns or extract from API URL patterns in network requests.
- mediumgetParams JSON encoding issues
- The getParams query parameter must be URL-encoded JSON. Use json.dumps() then urllib.parse.quote() to properly format the parameter.
- highJob descriptions missing from listings response
- Descriptions require a separate API call per job to the /job-details endpoint. The listings API only returns metadata, not full descriptions.
- mediumSame job ID exists across different companies
- Job IDs are scoped by site ID. Always use the combination of site_id + job_id to uniquely identify jobs across different iSolved tenants.
- mediumHTTP 403/429 responses under heavy request volume
- iSolved rate limits are undocumented but a 403 (blocked) or 429 (rate limited) signals you are moving too fast. Keep a ~200ms delay between requests, cap concurrent detail fetches at 3, and back off exponentially on 429.
- lowDisabled metadata fields return empty
- Some companies disable optional fields like benefits or video. Check for empty strings and null values gracefully in your parsing logic.
iSolved Jobs API.
Pull structured job listings from iSolved Hire's public JSON API, complete with pay ranges, workplace type, and department metadata that many job boards never expose.
What's in every response.
Data fields, real-world applications, and the companies already running on iSolved.
Data fields
- Structured Pay Ranges
- Workplace Type (Remote/Hybrid/Onsite)
- Employment Type & Classification
- Department & Org Unit
- Full HTML Job Descriptions
- Benefits & Closing Dates
Use cases
- 01Compensation Benchmarking
- 02Mid-Market Job Aggregation
- 03Multi-Tenant Company Discovery
- 04HCM Job Feed Extraction
Trusted by
- BecTech
- Barrett-Jackson
- Davidson Oil
- Infinisource
DIY GUIDE
How to scrape iSolved.
Step-by-step guide to extracting jobs from iSolved-powered career pages—endpoints, authentication, and working code.
Step 1: Extract the site ID from the careers page
import requests
import re
subdomain = "bectech"
careers_url = f"https://{subdomain}.isolvedhire.com/jobs/"
response = requests.get(careers_url)
html = response.text
# Extract domain_id (site ID) from page source
match = re.search(r'"domain_id"\s*:\s*"(\d+)"', html)
if match:
site_id = match.group(1)
print(f"Found site ID: {site_id}")
else:
print("Could not find site ID")Step 2: Fetch job listings with metadata
import requests
import json
from urllib.parse import quote
subdomain = "bectech"
site_id = "2332"
# Build the listings URL with getParams
get_params = json.dumps({"isInternal": 0}) # 0 = external jobs only
encoded_params = quote(get_params)
listings_url = f"https://{subdomain}.isolvedhire.com/core/jobs/{site_id}"
params = {"getParams": encoded_params}
headers = {
"Accept": "application/json, text/plain, */*",
"Referer": f"https://{subdomain}.isolvedhire.com/jobs/"
}
response = requests.get(listings_url, params=params, headers=headers)
data = response.json()
jobs = data.get("data", {}).get("jobs", [])
print(f"Found {len(jobs)} jobs")
for job in jobs[:3]: # Show first 3 jobs
print(f" - {job['title']} | {job.get('jobLocation')} | {job.get('employmentType')}")Step 3: Parse job metadata from listings
for job in jobs:
# Parse salary information
salary_info = None
if job.get("minSalary") and job.get("maxSalary"):
salary_info = {
"min": int(job["minSalary"]),
"max": int(job["maxSalary"]),
"pay_type": job.get("payType", "Annual"),
"period": job.get("payTypeFrame", "per year")
}
job_data = {
"id": job["id"],
"title": job["title"],
"location": job.get("jobLocation"),
"city": job.get("city"),
"state": job.get("stateName"),
"country": job.get("iso3"),
"department": job.get("orgTitle"),
"classification": job.get("classification"),
"employment_type": job.get("employmentType"),
"workplace_type": job.get("workplaceType"),
"salary": salary_info,
"url": job.get("jobUrl"),
"start_date": job.get("startDateRef"),
"end_date": job.get("endDateRef"),
}
print(job_data)Step 4: Fetch full job details
import requests
subdomain = "bectech"
site_id = "2332"
job_id = "1417382"
details_url = f"https://{subdomain}.isolvedhire.com/core/jobs/{site_id}/{job_id}/job-details"
headers = {
"Accept": "application/json, text/plain, */*",
"Referer": f"https://{subdomain}.isolvedhire.com/jobs/{job_id}"
}
response = requests.get(details_url, headers=headers)
data = response.json()
if data.get("success"):
job_details = data.get("data", {})
print({
"id": job_details.get("id"),
"title": job_details.get("title"),
"city": job_details.get("city"),
"description_plain": job_details.get("advertisingDescription", "")[:200],
"description_html": job_details.get("advertisingDescriptionHtml", "")[:200],
"benefits": job_details.get("benefits"),
"zip_code": job_details.get("jobBoardZip"),
})Step 5: Discover all iSolved companies via sitemap
import requests
import xml.etree.ElementTree as ET
sitemap_index_url = "https://feeds.isolvedhire.com/site_map_index.xml"
response = requests.get(sitemap_index_url)
root = ET.fromstring(response.content)
# Extract company subdomains from sitemap
companies = []
ns = {"sm": "http://www.sitemaps.org/schemas/sitemap/0.9"}
for sitemap in root.findall("sm:sitemap", ns):
loc = sitemap.find("sm:loc", ns)
if loc is not None:
url = loc.text
# Extract subdomain from URL like: https://company.isolvedhire.com/job_site_map.xml
if "isolvedhire.com" in url:
subdomain = url.split("//")[1].split(".")[0]
companies.append(subdomain)
print(f"Found {len(companies)} iSolved companies")
print(f"Examples: {companies[:5]}")Step 6: Handle rate limiting and errors
import requests
import time
from urllib.parse import quote
import json
def fetch_all_jobs(subdomain: str, site_id: str, delay: float = 0.2) -> list:
"""Fetch all jobs with rate limiting and error handling."""
base_url = f"https://{subdomain}.isolvedhire.com"
get_params = quote(json.dumps({"isInternal": 0}))
listings_url = f"{base_url}/core/jobs/{site_id}"
headers = {"Accept": "application/json"}
try:
# Fetch listings
response = requests.get(
listings_url,
params={"getParams": get_params},
headers=headers,
timeout=15
)
response.raise_for_status()
jobs = response.json().get("data", {}).get("jobs", [])
# Fetch details for each job
full_jobs = []
for i, job in enumerate(jobs):
job_id = job["id"]
details_url = f"{base_url}/core/jobs/{site_id}/{job_id}/job-details"
try:
details_resp = requests.get(details_url, headers=headers, timeout=10)
details_resp.raise_for_status()
details = details_resp.json().get("data", {})
full_jobs.append({**job, "description": details.get("advertisingDescriptionHtml")})
except requests.RequestException as e:
print(f"Error fetching job {job_id}: {e}")
time.sleep(delay) # Rate limit
return full_jobs
except requests.RequestException as e:
print(f"Error fetching listings: {e}")
return []
# Usage
jobs = fetch_all_jobs("bectech", "2332")
print(f"Retrieved {len(jobs)} complete job listings") Common issues
Best practices
- 1Cache the site ID after extraction to avoid repeated page fetches
- 2Use the sitemap index to discover all iSolved companies systematically
- 3Fetch listings first for metadata, then fetch details only for jobs you need
- 4Keep a ~200ms delay between requests and cap concurrent detail fetches at 3
- 5Handle missing optional fields gracefully (benefits, video, description variants)
- 6Use the advertisingDescriptionHtml field for rich HTML job descriptions
Or skip the complexity
One endpoint. All iSolved jobs. No scraping, no sessions, no maintenance.
Get API accesscURL
curl "https://connect.jobo.world/api/jobs?sources=isolved" \
-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 iSolved
Access iSolved
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