Oracle Taleo Jobs API.
Pull structured job listings from Oracle Taleo's per-tenant career sections through its REST job-board API, then enrich each role with full descriptions from server-rendered detail pages.
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 Oracle Taleo.
- Job Titles & Contest Numbers
- Requisition IDs
- Structured Locations
- Posting Dates
- Full Job Descriptions
- Hot Job Flags
- 01Enterprise Job Aggregation
- 02Global Company Tracking
- 03Large-Scale Talent Sourcing
- 04Multi-Region Job Monitoring
How to scrape Oracle Taleo.
Step-by-step guide to extracting jobs from Oracle Taleo-powered career pages—endpoints, authentication, and working code.
import requests
# Company configuration
company = "hdr"
section = "ex" # Common sections: ex, 1, 2, 10000
portal_id = "101430233" # Found in network requests
base_url = f"https://{company}.taleo.net"
search_url = f"{base_url}/careersection/rest/jobboard/searchjobs?lang=en&portal={portal_id}"
print(f"Taleo instance: {base_url}")
print(f"Career section: {section}")import requests
url = f"https://{company}.taleo.net/careersection/rest/jobboard/searchjobs?lang=en&portal={portal_id}"
headers = {
"Content-Type": "application/json",
"X-Requested-With": "XMLHttpRequest",
"Accept": "application/json, text/javascript, */*; q=0.01",
"Referer": f"https://{company}.taleo.net/careersection/{section}/jobsearch.ftl",
}
payload = {
"multilineEnabled": False,
"sortingSelection": {
"sortBySelectionParam": "1",
"ascendingSortingOrder": "false"
},
"fieldData": {
"fields": {
"KEYWORD": "",
"LOCATION": "",
"CATEGORY": ""
},
"valid": True
},
"pageNo": 1
}
response = requests.post(url, json=payload, headers=headers)
data = response.json()
jobs = data.get("requisitionList", [])
total_count = data.get("pagingData", {}).get("totalCount", 0)
print(f"Found {total_count} total jobs, fetched {len(jobs)} on page 1")import json
for job in jobs:
job_id = job.get("jobId")
contest_no = job.get("contestNo") # Use this for URLs
columns = job.get("column", [])
parsed_job = {
"job_id": job_id,
"contest_no": contest_no,
"title": columns[0] if len(columns) > 0 else None,
"location": json.loads(columns[1]) if len(columns) > 1 else [],
"posted_date": columns[2] if len(columns) > 2 else None,
"is_hot_job": job.get("hotJob", False),
"detail_url": f"https://{company}.taleo.net/careersection/{section}/jobdetail.ftl?job={contest_no}"
}
print(f"{parsed_job['title']} - {parsed_job['location']}")import requests
import time
import math
all_jobs = []
page_no = 1
page_size = 25
total_count = None
while True:
payload["pageNo"] = page_no
response = requests.post(url, json=payload, headers=headers)
data = response.json()
jobs = data.get("requisitionList", [])
paging = data.get("pagingData", {})
total_count = paging.get("totalCount", 0)
page_size = paging.get("pageSize", page_size)
all_jobs.extend(jobs)
total_pages = math.ceil(total_count / page_size) if page_size else 1
print(f"Page {page_no}: {len(jobs)} jobs (collected {len(all_jobs)}, total <= {total_count})")
# Stop once every declared page has been fetched, or a page comes back empty.
if not jobs or page_no >= total_pages:
break
page_no += 1
time.sleep(0.5) # Rate limiting
print(f"Total jobs collected: {len(all_jobs)}")import requests
from bs4 import BeautifulSoup
def fetch_job_details(contest_no: str) -> dict:
detail_url = f"https://{company}.taleo.net/careersection/{section}/jobdetail.ftl?job={contest_no}"
response = requests.get(detail_url)
soup = BeautifulSoup(response.text, "html.parser")
# Extract job details from HTML
# Note: Selectors may vary by Taleo instance
title_elem = soup.find("h1") or soup.find(class_="title")
location_elem = soup.find(class_="location")
# Description is typically in main content area
description_elem = soup.find(class_="jobdescription")
if not description_elem:
description_elem = soup.find(id="jobdescription")
return {
"contest_no": contest_no,
"title": title_elem.get_text(strip=True) if title_elem else None,
"location": location_elem.get_text(strip=True) if location_elem else None,
"description": description_elem.get_text(strip=True) if description_elem else None,
"url": detail_url
}
# Fetch details for first job
if all_jobs:
first_job = all_jobs[0]
details = fetch_job_details(first_job["contestNo"])
print(f"Title: {details['title']}")import requests
def safe_fetch_jobs(company: str, section: str, portal_id: str) -> list:
url = f"https://{company}.taleo.net/careersection/rest/jobboard/searchjobs"
params = {"lang": "en", "portal": portal_id}
try:
response = requests.post(url, params=params, json=payload, headers=headers, timeout=30)
response.raise_for_status()
# Check for unavailable section
if "Career Section Unavailable" in response.text:
print(f"Career section '{section}' unavailable for {company}")
return []
data = response.json()
return data.get("requisitionList", [])
except requests.exceptions.Timeout:
print(f"Timeout fetching jobs for {company}")
return []
except requests.exceptions.RequestException as e:
print(f"Error fetching jobs for {company}: {e}")
return []
except ValueError as e:
print(f"JSON parse error for {company}: {e}")
return []
# Test with known working instance
jobs = safe_fetch_jobs("hdr", "ex", "101430233")
print(f"Fetched {len(jobs)} jobs")Taleo instances may be offline or use different section names. Try common sections (ex, 1, 2, 10000) and verify the portal ID matches the company. Some instances also live on tbe.taleo.net rather than taleo.net.
The portal ID is required for API calls. Fetch the jobsearch.ftl page and look for the portal parameter in the embedded search config (portalNo, portal, or siteNumber markers). It is typically a 4-12 digit number.
The API returns both jobId (internal requisition ID) and contestNo (public). Always use contestNo when constructing jobdetail.ftl and jobapply.ftl URLs, as this is the public-facing job identifier.
Taleo's totalCount includes suppressed rows, so it is an upper bound rather than the exact number of visible jobs, and individual pages often return fewer than pageSize rows. Do not stop on a short page or treat the total-vs-fetched gap as an error; paginate until currentPageNo reaches ceil(totalCount / pageSize).
The searchjobs API only returns basic job info. You must fetch jobdetail.ftl pages and parse the server-rendered HTML to get full descriptions. There is no anonymous REST endpoint that returns descriptions.
Taleo instances may return HTTP 403/429 under aggressive request volume. Add 200ms-1s delays between requests, cap concurrent detail fetches, and implement retry logic with exponential backoff for timeouts.
Different Taleo versions and company customizations affect the detail page structure. Test your selectors against each target instance and fall back to the server-rendered content when your primary selector misses.
The location column contains a JSON array encoded as a string. Use json.loads() to parse it into a proper list before processing, and skip rows where the value is empty.
- 1Use the REST searchjobs API for listings and reserve HTML parsing for full descriptions
- 2Extract the portal ID from the jobsearch.ftl page before calling the search API
- 3Use contestNo (not jobId) when building jobdetail.ftl and jobapply.ftl URLs
- 4Parse the location column with json.loads() since it arrives as a JSON array string
- 5Keep paginating until currentPageNo equals ceil(totalCount / pageSize); short pages are normal
- 6Throttle to ~200ms between requests and handle 'Career Section Unavailable' errors gracefully
One endpoint. All Oracle Taleo jobs. No scraping, no sessions, no maintenance.
Get API accesscurl "https://connect.jobo.world/api/jobs?sources=oracle taleo" \
-H "X-Api-Key: YOUR_KEY" Access Oracle Taleo
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.