# SAP SuccessFactors Jobs API

> Query jobs from SAP SuccessFactors boards through one API with ?source=successfactors — plus how SAP's sitemal.xml feed really behaves.

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

SAP SuccessFactors is the HR suite most of the SAP-shaped enterprise world runs
on, and its careers sites are served by a product called **Recruiting
Marketing** (formerly Jobs2Web, which is why so many of its hosts still carry
that name). It's one of the largest sources in this dataset at 82,140 postings —
a number that moves hourly, so read it from
[`/v1/meta`](/docs/api-reference/meta) rather than trusting this sentence.

It is also, by a distance, the cheapest enterprise platform in this pool to
collect: one unauthenticated GET returns a tenant's entire board **with full
descriptions**.

## Try it

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

```json title="Illustrative — the shape, not a live response"
{
  "data": [
    {
      "id": "successfactors:R-0041882",
      "title": "SAP Basis Administrator",
      "company": {
        "slug": "meridian-industries",
        "name": "Meridian Industries",
        "domain": "meridianindustries.com",
        "website": "https://meridianindustries.com",
        "logo": "https://media.licdn.com/dms/image/…",
        "industry": "Chemicals",
        "employee_count": 18500
      },
      "location": {
        "remote": false,
        "countries": ["Germany"],
        "cities": ["Walldorf"],
        "regions": null,
        "raw": ["Walldorf, Germany"]
      },
      "employment_type": null,
      "work_arrangement": null,
      "seniority": null,
      "salary": null,
      "skills": ["sap basis", "hana"],
      "taxonomies": ["IT"],
      "apply_url": "https://jobs.meridianindustries.com/job/Walldorf-SAP-Basis-Administrator/R-0041882",
      "url": "https://jobs.meridianindustries.com/job/Walldorf-SAP-Basis-Administrator/R-0041882",
      "source": "successfactors",
      "posted_at": "2026-07-11T06:22:10.000Z",
      "expires_at": null,
      "updated_at": "2026-07-14T06:22:10.000Z"
    }
  ],
  "total": 2,
  "limit": 2,
  "offset": 0
}
```

`posted_at` and `expires_at` are the interesting fields on this source, and both
are explained below. Neither is what it looks like.

## How SuccessFactors boards actually work

One request per tenant. That's the whole thing:

```bash
curl "https://<recruiting-marketing-host>/sitemal.xml"
```

<Note title="Yes, `sitemal.xml`">
  Not `sitemap.xml`. `sitemal.xml` is SAP's real path, and it is not a typo on
  this page — it's a typo in the product, shipped and now load-bearing. It's an
  RSS 2.0 feed rather than a sitemap: full `<description>` bodies plus
  Google-Merchant `g:` fields, unauthenticated, no pagination, no detail call.
</Note>

The host is the employer's own careers domain (`jobs.<company>.com`,
`<company>.jobs2web.com`, or a vanity name), which means there is no shared API
host and no global index. Every tenant is found individually.

What each `<item>` carries:

| Field | What it holds |
| --- | --- |
| `description` | The **full** posting body, not a teaser |
| `g:id` | The requisition id |
| `g:employer` | The real employer name |
| `g:location` | Positional CSV: `City[, State], ISO-CC[, postal]` |
| `g:salary` | Comp as posted, where posted |
| `g:job_function` | A clean category, ~93% fill |

The `<title>` is `Engineer II (Middlesex, GB)` — title and location fused into one
string, so the location has to be split back out of the parentheses. Nested parens
happen (`Title (City (0200), GB)`), so a lazy match splits it wrong.

## What bites you

<Warning title="The feed has no date tag at all">
  There is no `pubDate` on a SuccessFactors item. Not "sometimes missing" —
  absent, on every job and every tenant. So a posting's real publish date is
  simply not in the feed, and the only honest thing to do is record when we first
  saw it.

  That means `posted_at` on this source is a **first-seen** time, not SAP's
  `datePosted`. Recovering the true date would cost a detail fetch per posting,
  which is a steep price for a field that's already close for anything inside the
  freshness window. It's a source limitation, and it's worth knowing before you
  build a "posted in the last 24h" chart on it.
</Warning>

**`g:expiration_date` is a lie, and we deliberately don't read it.** It's present,
it parses, and it's a synthetic constant: **now + 30 days**, on every job, on
every tenant. It's a rolling default, not a real posting expiry. Store it and you
ship an `expires_at` that is always exactly a month out and means nothing. Ours is
null here unless the platform actually declared something.

**`g:location` needs cleaning to resolve.** It's positional, and it arrives as
`Clearwater, FL, US, 33762` or `Walldorf, DE`. The trailing token is sometimes a
postal code and the one before it is an ISO-2 country. Feed that string to a
geocoder as-is and the postal code and the bare country code both fuzz the match.
We drop the postal token and expand `DE` to `Germany` so the country is
deterministic rather than guessed.

**The board token isn't the employer.** `g:employer` is, and it's on every item.
The host is often a vanity domain that says nothing about the legal entity behind
it.

## Or use ours

This is the one enterprise platform where "just fetch it" is nearly true. One GET,
no auth, no pagination, no N+1, full bodies. Descriptions land on **100%** of the
SuccessFactors rows we serve.

So the pitch isn't the fetch. It's that the feed doesn't publish a date and
publishes a fake expiry, that its locations need decomposing before they resolve,
that the useful employer name is a `g:` extension rather than anything standard,
and that **there is no list of tenants anywhere** — every one of the boards behind
these 82,140 postings was found individually, because SAP publishes no index of
who its customers are.

You also get these rows deduplicated against the same job on
[LinkedIn](/ats/linkedin) or an employer's other boards, and the [company
join](/docs/objects/company).

SuccessFactors sits in the heavy refresh tier, attempted every third cycle — see
[coverage](/docs/reference/coverage).

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