# Taleo Jobs API

> Query jobs from Taleo boards through one API with ?source=taleo — plus how Taleo's sharded searchResults endpoint really behaves.

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

Taleo is the ATS Oracle bought in 2012, and the name covers **two different
products** that share a brand and almost nothing else. We cover one of them:
Taleo Business Edition, the SMB and mid-market tier that lives on
`tbe.taleo.net`. It's in this dataset at 712 postings, 710 of them carrying a
description, which are numbers that move hourly, so read them from
[`/v1/meta`](/docs/api-reference/meta) rather than trusting this sentence.

We do not cover Taleo Enterprise. That asterisk leads this page rather than
sitting in a footnote, because "we support Taleo" means nothing until you know
which Taleo the employer you want is running.

## Which Taleo is it

| | Taleo Business Edition (TBE) | Taleo Enterprise Edition (TEE) |
| --- | --- | --- |
| Board URL | `<shard>.tbe.taleo.net/<inst>/ats/careers/v2/…` | `<company>.taleo.net/careersection/…` |
| Sells to | SMB and mid-market | Large enterprise |
| In this dataset | **Yes** — `?source=taleo` | **No** — see below |

The board URL is the whole check. If the employer you want serves
`careersection`, nothing on this page covers them, and the fix is usually
[`?source=oracle`](/ats/oracle) rather than anything Taleo-branded.

### Why Enterprise isn't the target

TEE is a stateful JSF application, and it doesn't behave like a site that happens
to be scrapeable. Its `jobsearch.ftl` returns a healthy `200` containing **zero**
job links: the shell renders, and the jobs arrive later through application state
a plain client isn't carrying. The REST endpoint the competitor guides document
needs a per-tenant `portal` id discovered from that tenant's own `jobsearch.ftl`,
so it isn't enumerable. Those guides' own worked example, `hdr.taleo.net`, now
returns a `500`. The one portal id we did find turned out to be a template
default shared across four unrelated tenants, which is worse than no id, because
it looks like a discovery.

<Warning title="200-with-an-empty-shell is not bot-blocking">
  A `403`, a challenge page or a CAPTCHA means the server decided you're a robot,
  and the answer is a better TLS fingerprint or a residential IP. TEE does none of
  that. It returns a real `200` and a real page that genuinely has no jobs in it
  yet. Nothing is refusing us. There is simply nothing to parse until an
  application runs.

  So impersonation tiers and residential proxies are the **wrong tools** here, and
  buying more of them would spend money to receive the same empty shell faster.
  TEE needs a browser. That's a different tier, and it isn't built.
</Warning>

There's a second reason not to fight it: most of the TEE base has **migrated to
Oracle Cloud HCM**, which [`?source=oracle`](/ats/oracle) already covers and which
is one of the largest sources in this dataset. The Taleo Enterprise tenants worth
having are mostly Oracle Cloud tenants now, so TBE plus Oracle Cloud spans the
reachable Taleo universe and standalone TEE is skipped on purpose rather than by
accident.

## Try it

<Note>
  The value is `taleo`, and it means Business Edition. `?source=taleo` returns TBE
  rows and only TBE rows. It is not an error and not a gap in the crawl if a
  `careersection` employer you expected isn't among them.

  ```bash
  curl "…/v1/jobs?source=taleo"   # ✓ 712 postings, all Business Edition
  ```
</Note>

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

```json title="Illustrative — the shape, not a live response"
{
  "data": [
    {
      "id": "taleo:NORTHWIND2_4137",
      "title": "Staff Accountant",
      "company": {
        "slug": "northwind-associates",
        "name": "Northwind Associates",
        "domain": "northwind.com",
        "website": "https://northwind.com",
        "industry": "Financial Services",
        "employee_count": 340
      },
      "location": {
        "remote": false,
        "countries": ["United States"],
        "cities": ["Wakefield"],
        "regions": ["Massachusetts"],
        "raw": ["Wakefield, MA, Massachusetts, US"]
      },
      "employment_type": ["FULL_TIME"],
      "work_arrangement": null,
      "seniority": null,
      "salary": null,
      "taxonomies": ["Finance"],
      "apply_url": "https://phh.tbe.taleo.net/phh02/ats/careers/v2/viewRequisition?org=NORTHWIND2&cws=1&rid=4137",
      "url": "https://phh.tbe.taleo.net/phh02/ats/careers/v2/viewRequisition?org=NORTHWIND2&cws=1&rid=4137",
      "source": "taleo",
      "posted_at": "2026-06-26T00:00:00.000Z",
      "expires_at": null,
      "updated_at": "2026-07-14T11:20:03.000Z"
    }
  ],
  "total": 2,
  "limit": 2,
  "offset": 0
}
```

Three shape notes, all of them TBE telling you less than a modern board does.
`apply_url` and `url` are identical, because TBE publishes no distinct apply link
in what we read and `apply_url` [falls back to `url`](/docs/objects/job) — not
true of [Lever](/ats/lever). `posted_at` is always midnight, because TBE dates
arrive day-precision as `2026-06-25 00:00:00.0`; the date is real, the time isn't,
on every Taleo row. And `work_arrangement` is usually `null`: the JSON-LD carries
`jobLocationType` only when a job is remote, so a TBE board can tell you a role
*is* remote but never that it isn't. We leave that null rather than reading the
silence as on-site.

## How TBE boards actually work

Server-rendered HTML on a sharded host, enumerable by URL pattern, with a list and
a detail:

```bash
# 1. List — page 0 is a fixed 10-job preview
curl "https://<shard>.tbe.taleo.net/<inst>/ats/careers/v2/searchResults?org=<ORG>&cws=1"

# 2. Pages 1..N — same session only
curl "https://<shard>.tbe.taleo.net/<inst>/ats/careers/v2/searchResults?next&rowFrom=10&org=<ORG>&cws=1"

# 3. Detail — schema.org JobPosting JSON-LD on the modern theme
curl "https://<shard>.tbe.taleo.net/<inst>/ats/careers/v2/viewRequisition?org=<ORG>&cws=1&rid=<rid>"
```

Neither of those first two lines does what it looks like it does, and both fail
quietly.

### Stale shards

TBE boards are sharded across instances, and organisations get migrated between
them: `phe01` becomes `phh02`. The URL on file still works, because it `302`s to
the new shard. So you follow that redirect **manually** and read two things off
it: the final base, since every later URL has to use the shard you landed on
rather than the one you started from, and the `JSESSIONID` the redirect target
sets, which the next section is entirely about. Hand this to a client that
auto-follows redirects and discards the hops, and you get page 0 and no way to
ask for page 1.

### Stateful pagination

Page 0 is a fixed ten-job preview. More jobs come from `?next&rowFrom=<n>`, but
only **inside the session that served page 0**. It's a jScroll infinite scroll,
and the session cookie is what makes it work. A cookieless `?next` returns
nothing. Not an error, not a `401`: an empty page that looks exactly like the end
of a board.

<Tip title="The ten-job board">
  A client that doesn't thread cookies through the pagination loop reads every TBE
  board as having exactly ten jobs, forever, at `200 OK`. Every board, plausibly
  populated, silently truncated. If your Taleo numbers look suspiciously round,
  count them.
</Tip>

## What else bites you

**There are two themes and only one has JSON-LD.** The modern theme emits
schema.org `JobPosting` on the detail page. The older one emits none at all,
renders the body in a `<div name="cwsJobDescription">` block instead, and packs
the location into the listing anchor as `Title | City`. Handle only the modern
theme and a chunk of boards come back with titles and no descriptions.

**A header block sits above the description.** On some tenants a job-description
*header* element precedes the real body, and it's a ~150-char teaser. Take the
first match and you store the teaser, which is populated, plausible, and wrong.

**`rid` is unique only within an org.** Two organisations can both have the same
requisition number, so the key has to be scoped by org or one silently overwrites
the other.

**The employer's domain is never in the JSON-LD.** TBE's `hiringOrganization` is
name-only: no `sameAs`, no `url`. The domain does exist on the detail *page*, in
its outbound links, mixed in with social and tracker hosts. Mining it at scrape
time and corroborating it against the employer name is the only cheap chance you
get at it.

**A `403` here is a burst, not a wall.** TBE `302`s across shards as normal
operation, and a burst can draw a `403` from the CDN. Both recover on the next
attempt, so treating that `403` as permanent disables live boards.

## Or use ours

The fetch is a one-liner. The session, the shard and the two themes aren't.

You get descriptions on 710 of 712 rows, taken from JSON-LD where the theme has it
and the `cwsJobDescription` block where it doesn't, with the teaser header kept
from winning. You get the real employer name off the JSON-LD rather than an org
code like `NORTHWIND2`, the domain mined from the detail page and corroborated
against that name, and the [company join](/docs/objects/company) hanging off it.
You get pagination that carries its cookie, so boards aren't truncated at ten. And
Taleo rows are [collapsed to one row](/docs/guides/deduplication) against the same
job on [LinkedIn](/ats/linkedin) rather than returned twice.

Our queue seeds 700+ TBE boards, assembled rather than fetched, like most of this
dataset. See [coverage](/docs/reference/coverage) for how often it's re-read and
[sources](/docs/reference/sources) for the rest of the vocabulary.

Query it with `?source=taleo`.
[Quickstart](/docs/quickstart), then [pricing](/pricing).
