Oracle Recruiting Jobs API: Get Oracle Cloud HCM Jobs as JSON
TL;DR
Oracle Recruiting Cloud (part of Oracle Fusion Cloud HCM) career sites load jobs from a keyless REST resource, recruitingCEJobRequisitions, with paging set inside the finder parameter. You need the tenant host and the career site number, and a wrong site number returns a different job set instead of an error. The list has titles, dates and locations but no description, so each job needs a recruitingCEJobRequisitionDetails call. There's no salary field, and the remote flag is only filled on some postings.
On this page
To get a company's jobs from Oracle Recruiting Cloud, call https://{host}/hcmRestApi/resources/latest/recruitingCEJobRequisitions?onlyData=true&finder=findReqs;siteNumber={site},limit=100,offset=0 with no API key. The list returns titles, dates and locations, and each job's full description comes from one recruitingCEJobRequisitionDetails request.
That's the DIY route, and it works for a handful of companies you already know. If you'd rather not build and maintain an Oracle Recruiting scraper, CleanJobData already ingests ATS platforms and company career sites and serves the jobs normalized through one REST API — see the Jobs API docs.
At a Glance
| Endpoint | GET https://{host}/hcmRestApi/resources/latest/recruitingCEJobRequisitions?onlyData=true&finder=findReqs;siteNumber={site},limit=100,offset=0 |
| Detail endpoint | GET https://{host}/hcmRestApi/resources/latest/recruitingCEJobRequisitionDetails?expand=all&onlyData=true&finder=ById;Id={id},siteNumber={site} |
| API key | None |
| Pagination | limit and offset inside finder, 100 per page |
| Full description | Detail call, split across three HTML fields |
| Salary field | No |
| Remote | WorkplaceType, filled on some postings only |
Finding the Host and Site Number
Oracle's candidate-facing career sites have URLs like:
https://acme.fa.us2.oraclecloud.com/hcmUI/CandidateExperience/en/sites/CX_1/
You need two values from that URL:
- Host.
acme.fa.us2.oraclecloud.com. Thefa.us2part is a shared Oracle Cloud pod, and other regions such asfa.us6andfa.ca2exist too. - Site number. The segment after
/sites/, hereCX_1. Sites are namedCX,CX_1,CX_2,CX_1001and so on, and some have names likejobsearch.
Copy the site number exactly. A wrong one doesn't fail: on one tenant, the real site listed 86 jobs while a made-up site number returned 200 with 150 jobs drawn from the tenant's other career sites. If a company embeds the career site on its own domain, open the Network tab and look for requests to hcmRestApi.
The List Request
curl "https://acme.fa.us2.oraclecloud.com/hcmRestApi/resources/latest/recruitingCEJobRequisitions?onlyData=true&expand=requisitionList.secondaryLocations&finder=findReqs;siteNumber=CX_1,limit=100,offset=0,sortBy=POSTING_DATES_DESC"The paging parameters go inside finder, separated by commas. expand=requisitionList.secondaryLocations adds a job's extra locations, and sortBy=POSTING_DATES_DESC puts the newest jobs first.
The response is a search envelope: items[0] holds the search settings and the total, and the jobs sit in items[0].requisitionList.
{
"items": [
{
"SiteNumber": "CX_1",
"TotalJobsCount": 842,
"requisitionList": [
{
"Id": "753717",
"Title": "Store Associate",
"PostedDate": "2026-11-01",
"PrimaryLocation": "Bristol, VT, United States",
"PrimaryLocationCountry": "US",
"ShortDescriptionStr": "7 MAIN ST 05443 BRISTOL,VT UNITED STATES",
"WorkplaceType": "",
"JobSchedule": null,
"secondaryLocations": []
}
]
}
],
"count": 1,
"hasMore": false
}| Field | What it is |
|---|---|
Id | Requisition ID, the same value in the list and the detail call. Not always numeric |
Title | Job title |
PostedDate | Posting date, date only |
PrimaryLocation | Comma-separated location text |
PrimaryLocationCountry | ISO alpha-2 country code |
secondaryLocations | Extra locations with Name, CountryCode, Latitude, Longitude (needs expand) |
ShortDescriptionStr | Short text set by the tenant, not the description. On one tenant it was the street address |
WorkplaceType | For example "Hybrid", often an empty string |
JobSchedule | For example "Full time" or "Part time", when set |
TotalJobsCount | On items[0], the total for this site |
The outer count and hasMore describe the envelope, not the jobs: a site with thousands of jobs still reports "count": 1 and "hasMore": false. Use TotalJobsCount instead.
Pagination
Increase offset inside the finder by the number of rows you actually received, and stop on an empty requisitionList or when offset reaches TotalJobsCount. A page size of 100 works well.
Dedupe by Id. Offset paging over a date sort isn't stable: a job posted while you're paging shifts the window, and the same Id can show up on two pages.
The Detail Request
curl "https://acme.fa.us2.oraclecloud.com/hcmRestApi/resources/latest/recruitingCEJobRequisitionDetails?expand=all&onlyData=true&finder=ById;Id=753717,siteNumber=CX_1"The job is items[0]. Fields worth knowing:
ExternalDescriptionStr,ExternalResponsibilitiesStr,ExternalQualificationsStr. The posting body, as HTML. Join whichever are non-empty, in that order. The last two are often empty strings.CorporateDescriptionStr,OrganizationDescriptionStr. Company boilerplate that repeats on every job for the tenant. Leave them out unless you want it.ExternalPostedStartDate. A full ISO timestamp such as2026-11-01T11:31:07+00:00. Prefer it over the list's date-onlyPostedDate.ExternalPostedEndDate. The scheduled end of the posting. Treat it as a hint and detect closed jobs by comparing lists.JobSchedule,JobLevel,Category,JobFunction. Classification fields, filled per tenant.primaryLocationCoordinates. Latitude and longitude forPrimaryLocation, as strings.workLocation. A facility record. It can name a different place from the advertised location, so don't use it as the job's location.requisitionFlexFields. Tenant-specificPrompt/Valuepairs.
The API has no apply link. The candidate-facing job page is:
https://{host}/hcmUI/CandidateExperience/en/sites/{site}/job/{Id}
Fetching Jobs (Python)
import requests
def list_oracle_jobs(host: str, site: str) -> list[dict]:
base = f"https://{host}/hcmRestApi/resources/latest"
seen, jobs, offset = set(), [], 0
while True:
finder = f"findReqs;siteNumber={site},limit=100,offset={offset},sortBy=POSTING_DATES_DESC"
resp = requests.get(
f"{base}/recruitingCEJobRequisitions",
params={"onlyData": "true", "expand": "requisitionList.secondaryLocations", "finder": finder},
timeout=25,
)
resp.raise_for_status()
envelope = (resp.json().get("items") or [{}])[0]
rows = envelope.get("requisitionList") or []
total = envelope.get("TotalJobsCount") or 0
if not rows:
break
for row in rows:
job_id = str(row["Id"])
if job_id not in seen:
seen.add(job_id)
jobs.append(row)
offset += len(rows)
if total and offset >= total:
break
return jobs
def get_oracle_detail(host: str, site: str, job_id: str) -> dict | None:
resp = requests.get(
f"https://{host}/hcmRestApi/resources/latest/recruitingCEJobRequisitionDetails",
params={"expand": "all", "onlyData": "true", "finder": f"ById;Id={job_id},siteNumber={site}"},
timeout=20,
)
resp.raise_for_status()
items = resp.json().get("items") or []
if not items:
return None # the requisition is gone
d = items[0]
parts = [d.get(k) for k in ("ExternalDescriptionStr", "ExternalResponsibilitiesStr", "ExternalQualificationsStr")]
return {
"id": job_id,
"title": d.get("Title"),
"description_html": "\n".join(p for p in parts if p and p.strip()),
"published": d.get("ExternalPostedStartDate"),
"location": d.get("PrimaryLocation"),
"country": d.get("PrimaryLocationCountry"),
"workplace_type": d.get("WorkplaceType") or None,
"url": f"https://{host}/hcmUI/CandidateExperience/en/sites/{site}/job/{job_id}",
}requests URL-encodes the finder value, and Oracle accepts it either way.
Gotchas
PrimaryLocation comes in three shapes. "Abilene, TX, United States" is city, region and country. "United States" is a country alone. Two parts are ambiguous: "TX, United States" is a region, but "Suva, Fiji" is a city. Take the country from PrimaryLocationCountry and check the first segment against a gazetteer before you call it a city. See the location normalization guide.
Coordinates change type. Latitude and Longitude are numbers in the list and strings in the detail response, so convert them before comparing.
IDs aren't always numbers. Some tenants use IDs like W734354. Store Id as a string.
Gone jobs return 200. A closed requisition's detail call answers 200 with an empty items array, not a 404.
One tenant can expose the same jobs on several sites. Some hosts publish the same requisitions under more than one site number. Opening a job page under a duplicate site answers with a 302 redirect to the main site, but the listing page doesn't redirect, so check a few job pages. Other sites mix shared jobs with jobs of their own. Dedupe by host plus Id.
WorkplaceType is patchy. It's set to values like "Hybrid" on some postings and empty on others from the same tenant, so also check the title and location for "remote" or "hybrid".
No employer name or salary. The payloads don't include the company name or any pay field, so you supply the name yourself and look for pay in the description text.
Candidate Experience API vs Oracle's Authenticated REST API
These resources sit inside Oracle's HCM REST API. Most of that API needs an authenticated user from the employer's Oracle Cloud instance and covers internal HR and recruiting data. The candidate-experience resources used here answer anonymous requests because the public career site depends on them, and they only return published jobs. If you want postings from companies you don't work with, these are the ones you can use.
When This Is Enough
For one employer whose host and site number you know, the list call plus one detail call per new job is straightforward, and large tenants page cleanly at 100 rows.
When It Isn't
Across many Oracle tenants you'll deal with duplicate sites, ambiguous locations and per-tenant field use, alongside other platforms like Workday and SmartRecruiters. See which ATS have a public job postings API for how they compare.
CleanJobData reads Oracle Recruiting sites alongside other platforms and returns one schema, with locations resolved to cities, states and countries and a remote type. There's no salary in Oracle's data, so that field can be empty. Try it in the Playground or see the API Documentation.
Skip the scraper: get this data from the CleanJobData API
If you'd rather not maintain an Oracle Recruiting collector, you can query normalized jobs from ATS platforms and company career sites with a single request. The response has structured city/state/country locations, parsed salary when the employer publishes one, a remote type, and company metadata, and you page through results with the cursor in pagination.next_page.
curl "https://api.cleanjobdata.com/jobs?title=engineer&domain=acme.fa.us2.oraclecloud.com&limit=20" \
-H "Authorization: Bearer $CLEANJOBDATA_API_KEY"The domain filter is an exact match on the host of each job's application_url. Oracle hosts belong to one tenant, so this scopes the query to that company; company_website_url and employer_id work too. Coverage depends on which employers are in the dataset, so check that the companies you care about appear before you rely on it.
- Jobs API reference for every filter and response field
- Playground to run queries without writing code
- Pricing — the Trial is free with no card required
FAQ
Does Oracle Recruiting Cloud have a public jobs API?
Yes. Career sites built on Oracle Recruiting load jobs from https://{host}/hcmRestApi/resources/latest/recruitingCEJobRequisitions, which needs no API key. You pass the career site number and paging values inside the finder parameter.
How do I find the Oracle site number?
It's the segment after /sites/ in the career site URL, for example CX_1 in /hcmUI/CandidateExperience/en/sites/CX_1/. Copy it exactly, because a wrong site number returns a different set of jobs instead of an error.
How do I get the full job description from Oracle?
Call recruitingCEJobRequisitionDetails with finder=ById;Id={id},siteNumber={site}. Join ExternalDescriptionStr, ExternalResponsibilitiesStr and ExternalQualificationsStr, skipping the empty ones.
Does the Oracle Recruiting API include salary?
No field we read carries pay. If an employer publishes a range, it's in the description HTML.