# Oracle Cloud HCM Jobs API

> Query jobs from Oracle Cloud HCM boards through one API with ?source=oracle — plus how Oracle's finder-string pagination really behaves.

Source: https://hyperjobs.io/ats/oracle

Oracle Cloud HCM is the recruiting half of Oracle's HR suite, branded **Candidate
Experience** on the career sites it renders. It's what a large share of the
Fortune 500 runs hiring on, and it's one of the biggest sources in this dataset at
146,650 postings — a number that moves hourly, so read it from
[`/v1/meta`](/docs/api-reference/meta) rather than trusting this sentence. Every
customer gets its own `*.oraclecloud.com` host and its own site code, and the REST
API behind all of them is unauthenticated.

## Try it

```bash
curl -G https://api.hyperjobs.io/v1/jobs \
  -H "Authorization: Bearer $HYPERJOBS_KEY" \
  --data-urlencode "source=oracle" \
  --data-urlencode "time_frame=7d" \
  --data-urlencode "limit=2"
```

```json title="Illustrative — the shape, not a live response"
{
  "data": [
    {
      "id": "oracle:acmeglobal_43219",
      "title": "Senior Financial Analyst",
      "company": {
        "slug": "acme-global",
        "name": "Acme Global",
        "domain": "acmeglobal.com",
        "website": "https://acmeglobal.com",
        "logo": "https://media.licdn.com/dms/image/…",
        "industry": "Industrial Machinery",
        "employee_count": 31000
      },
      "location": {
        "remote": false,
        "countries": ["United Arab Emirates"],
        "cities": ["Dubai"],
        "regions": null,
        "raw": ["Dubai, United Arab Emirates"]
      },
      "employment_type": ["FULL_TIME"],
      "work_arrangement": "On-site",
      "seniority": null,
      "salary": null,
      "skills": ["ifrs", "financial modelling"],
      "taxonomies": ["Finance"],
      "apply_url": "https://acmeglobal.fa.em2.oraclecloud.com/hcmUI/CandidateExperience/en/sites/CX_1/job/43219",
      "url": "https://acmeglobal.fa.em2.oraclecloud.com/hcmUI/CandidateExperience/en/sites/CX_1/job/43219",
      "source": "oracle",
      "posted_at": "2026-07-09T00:00:00.000Z",
      "expires_at": "2026-09-01T00:00:00.000Z",
      "updated_at": "2026-07-14T02:18:44.000Z"
    }
  ],
  "total": 2,
  "limit": 2,
  "offset": 0
}
```

## How Oracle Cloud boards actually work

Two unauthenticated REST endpoints, list then detail:

```bash
# 1. List — thin, 200 per page
curl "https://<host>/hcmRestApi/resources/latest/recruitingCEJobRequisitions\
?onlyData=true&expand=requisitionList&finder=findReqs;siteNumber=CX_1,limit=200,offset=0"

# 2. Detail — where the real data is
curl "https://<host>/hcmRestApi/resources/latest/recruitingCEJobRequisitionDetails\
?expand=all&onlyData=true&finder=ById;Id=\"43219\",siteNumber=CX_1"
```

### `limit` and `offset` are not query parameters

This is the thing that catches people. Oracle's pagination lives **inside the
`finder` string**, not alongside it:

```
finder=findReqs;siteNumber=CX_1,limit=200,offset=200
```

Append `&limit=200&offset=200` the way you would anywhere else and Oracle ignores
them, hands back the first page again, and returns `200 OK` while doing it. A loop
written on that assumption spins forever on page one, or dedups itself down to 200
jobs and calls the board finished.

### The list is thin, and on many tenants the description is simply absent

The list gives you titles, ids and a primary location. On a lot of tenants it
ships an **empty** description, employment type and category, and the detail call
is the only place the real structured data exists: `JobSchedule`,
`Category`/`JobFunction`, `RequisitionType`, coordinates, `StudyLevel`, the
posting end date, and salary in flexfields.

So Oracle is an N+1, like [Workday](/ats/workday). We only pay it for postings
we've never seen: the list reports the complete live key set first, so board
diffing and expiry run off the cheap call, and only new requisitions get a detail
fetch.

### The description arrives in three pieces

There's no single body field. You assemble it:

| Field | What it holds |
| --- | --- |
| `ShortDescriptionStr` | The opening pitch |
| `ExternalResponsibilitiesStr` | The responsibilities section |
| `ExternalQualificationsStr` | The qualifications section |

Read only the first and you get an intro with no requirements, which looks like a
populated field, so nothing alerts you.

<Note title="The one structured skills field in enterprise ATS">
  Oracle's detail can carry a real `skills[]` taxonomy — `Java`, `Python`,
  `Terraform` — as ground truth rather than prose mined out of a description. It
  only appears on tenants that switched Oracle Recruiting's skills feature on,
  which is rare, but it's the only structured skills field found across any of the
  big enterprise platforms. Where it's absent, [skills are
  derived](/docs/guides/skills-and-taxonomies) like everywhere else.
</Note>

## What bites you

<Warning title="Requisition ids are only unique within a tenant">
  Oracle `Id`s are tenant-local. Key on the bare id and one company's requisition
  silently overwrites another's. Ours is scoped by subdomain, which is why the ids
  on this source read `oracle:acmeglobal_43219`.
</Warning>

**A raw job URL is not a board.** This one cost real money to learn. The careers
URL and a link to a single posting differ only by a `/job/{id}` suffix, so a
discovery run that stores whatever URL it found treats **every requisition as its
own board**. One import produced 5,244 Oracle "boards" for roughly 1,056 real
tenant sites. The board token has to be canonicalised down to host plus site code
before it's stored.

**Salary flexfield prompts are employer-typed, and vary wildly.** Oracle puts pay
in `requisitionFlexFields`, where the *prompt* is whatever the tenant's admin
named it: `Salary Minimum`, `Base Salary Min`, `Salary Range Minimum`,
`Minimum Pay Rate`, or one combined string like `$148,000 - $186,000 annually`.
Match one spelling and most tenants return nothing.

**`JobSchedule` is null on roughly 58% of details.** It's the reliable employment
signal when it's there. When it isn't, the answer is in `ContractType`, `JobType`,
or a flexfield the employer used instead.

**Locations drop their country.** `PrimaryLocation` is frequently city-only, with
the country sitting separately in `PrimaryLocationCountry` as an ISO-2 code. We
append it only when the display string doesn't already end in a country.

**`StudyLevel` is messy free text.** `High School Graduate`, `Associate's Degree/College
Diploma (±13 years)`, and a curly-quoted `Bachelor's degree` that won't match a
straight apostrophe.

## Or use ours

The fetch is two endpoints. The work is everything around them.

You get descriptions already assembled from their three fields, tenant-scoped ids
that stop one company's `43219` from clobbering another's, the country appended
from Oracle's own structured field, salary read across the flexfield prompt
spellings employers actually use, and the [company
join](/docs/objects/company) — an Oracle board tells you a subdomain and a site
code, not who that is. Descriptions land on 92.9% of the Oracle rows we serve.

Oracle sits in the heavy refresh tier, attempted every third cycle rather than
every hour, because the N+1 makes it expensive. A new posting can take several
hours to appear and a removed one takes as long to reap;
[coverage](/docs/reference/coverage) is explicit about that, so don't build
anything assuming uniform latency across [sources](/docs/reference/sources).

Query it with `?source=oracle`.
[Quickstart](/docs/quickstart) · [Pricing](/pricing)
