Rippling Jobs API: Get Rippling Job Listings as JSON

CleanJobData Engineering

TL;DR

Rippling career pages call a public, keyless JSON API at api.rippling.com. The v2 list endpoint pages 100 jobs at a time with a 0-based page number and returns structured locations, but no description, date or pay. A v1 detail call per job returns the description (split into company and role blocks), the createdOn timestamp and payRangeDetails. Multi-location jobs repeat in the list once per location, so dedupe by id.

On this page

To get a company's jobs from Rippling, call https://api.rippling.com/platform/api/ats/v2/board/{slug}/jobs?page=0&pageSize=100 for the list, then https://api.rippling.com/platform/api/ats/v1/board/{slug}/jobs/{id} for each job's description, date and pay. Neither call needs an API key: they're the same endpoints the hosted ats.rippling.com career pages use.

That's the DIY route, and it works for a handful of companies you already know. If you'd rather not build and maintain a Rippling 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

EndpointGET https://api.rippling.com/platform/api/ats/v2/board/{slug}/jobs?page=0&pageSize=100
Detail endpointGET https://api.rippling.com/platform/api/ats/v1/board/{slug}/jobs/{id}
API keyNone
Paginationpage (0-based) and pageSize=100; stop at totalPages
Full descriptionDetail call per job, in two HTML blocks
Salary fieldYes, payRangeDetails on the detail
RemoteworkplaceType on each list location (ON_SITE, REMOTE, HYBRID)

Finding the Board Slug

Hosted Rippling career pages live at https://ats.rippling.com/{slug}/jobs, and that path segment is the slug you pass to the API. Job pages add the job's UUID: https://ats.rippling.com/{slug}/jobs/{id}. Some links carry a locale prefix, such as https://ats.rippling.com/de-DE/{slug}/jobs/{id}; the slug is the segment after the locale.

The slug is chosen by the employer and often isn't the company name. You'll see slugs like acme-careers, jobs-at-acme or even a generic open-roles, so copy it from a real careers URL rather than guessing. If the company embeds the board on its own site, open the browser's Network tab and look for requests to api.rippling.com.

The List Request

curl "https://api.rippling.com/platform/api/ats/v2/board/acme-careers/jobs?page=0&pageSize=100"

The response is a page of summaries:

{
  "items": [
    {
      "id": "84d388b6-7656-434c-8862-0312eb6b97ac",
      "name": "Business Operations Manager",
      "url": "https://ats.rippling.com/acme-careers/jobs/84d388b6-7656-434c-8862-0312eb6b97ac",
      "department": { "name": "Bizops" },
      "locations": [
        {
          "name": "San Francisco, CA",
          "country": "United States",
          "countryCode": "US",
          "state": "California",
          "stateCode": "CA",
          "city": "San Francisco",
          "workplaceType": "ON_SITE"
        }
      ],
      "language": "en-US"
    }
  ],
  "page": 0,
  "pageSize": 100,
  "totalItems": 1,
  "totalPages": 1
}

The list has structured locations but no description, posting date, pay or employment type. Extra flags like includeDescription or expand are ignored, so you can't ask the list for more.

List Fields

FieldWhat it holds
idJob UUID. Use it for the detail call and as your dedup key
nameJob title. Watch for trailing spaces
urlPublic job page on ats.rippling.com
department.nameDepartment label, if the employer set one
locations[]One object per location, with city, state, stateCode, country, countryCode and workplaceType
languageA locale such as en-US
totalItems, totalPagesCounts for the whole board, for pagination

Pagination

page starts at 0. Request pages until page + 1 >= totalPages, or until a page comes back empty. A pageSize of 100 works; we haven't relied on anything larger.

One thing to know before you count jobs: a job posted to several locations appears once per location, with the same id and a different locations entry. On a large board, the row count can be nearly double the number of real jobs. Dedupe by id and merge the locations arrays, or you'll store the same job several times.

The Detail Request

curl "https://api.rippling.com/platform/api/ats/v1/board/acme-careers/jobs/84d388b6-7656-434c-8862-0312eb6b97ac"

Trimmed response:

{
  "uuid": "84d388b6-7656-434c-8862-0312eb6b97ac",
  "name": "Business Operations Manager",
  "description": {
    "company": "<div><h1>About Acme</h1><p>...</p></div>",
    "role": "<div><h2>Role Overview</h2><p>...</p></div>"
  },
  "workLocations": ["Remote (Washington, DC, US)"],
  "department": { "name": "General & Administrative" },
  "employmentType": { "label": "SALARIED_FT", "id": "Salaried, full-time" },
  "createdOn": "2026-07-21T16:15:42.717000-07:00",
  "url": "https://ats.rippling.com/acme-careers/jobs/84d388b6-7656-434c-8862-0312eb6b97ac",
  "board": { "slug": "acme-careers", "title": "Careers at Acme", "boardURL": "https://ats.rippling.com/acme-careers/jobs" },
  "payRangeDetails": [
    { "location": "Hybrid (Washington, DC, US)", "currency": "USD", "frequency": "YEAR", "rangeStart": 140000, "rangeEnd": 165000, "isRemote": false }
  ],
  "companyName": "Acme"
}

What the detail adds:

  • description is an object with two HTML blocks, company and role. Keep both. On some boards the whole posting is in company and role is an empty stub; on others company is a short "About us" blurb and the posting is in role.
  • createdOn is an ISO timestamp with a numeric offset. It's the only date Rippling gives you, so it's your "posted" date.
  • workLocations is an array of display strings covering every location of the job.
  • payRangeDetails is an array with one entry per pay location: currency, frequency, rangeStart, rangeEnd. It's an empty array when the employer doesn't publish pay.
  • employmentType.label is a code such as SALARIED_FT, HOURLY_FT or CONTRACTOR; employmentType.id is the readable version.
  • companyName is the employer's real name, which is handy because the slug often isn't.

Fetching Jobs (Python)

import time
import requests
 
BASE_V1 = "https://api.rippling.com/platform/api/ats/v1/board"
BASE_V2 = "https://api.rippling.com/platform/api/ats/v2/board"
 
def list_rippling_jobs(slug: str) -> dict:
    jobs = {}  # id -> summary, merging repeated rows
    page = 0
    while True:
        r = requests.get(f"{BASE_V2}/{slug}/jobs", params={"page": page, "pageSize": 100}, timeout=30)
        r.raise_for_status()
        data = r.json()
        for item in data["items"]:
            job = jobs.setdefault(item["id"], {**item, "locations": []})
            for loc in item.get("locations") or []:
                if loc not in job["locations"]:
                    job["locations"].append(loc)
        page += 1
        if not data["items"] or page >= data["totalPages"]:
            break
        time.sleep(0.3)
    return jobs
 
def fetch_rippling_jobs(slug: str) -> list:
    out = []
    for job_id, summary in list_rippling_jobs(slug).items():
        r = requests.get(f"{BASE_V1}/{slug}/jobs/{job_id}", timeout=20)
        if r.status_code == 404:
            continue  # unknown id; a closed job drops out of the list on the next run
        r.raise_for_status()
        d = r.json()
        desc = d.get("description") or {}
        html = "".join(f"<div>{part}</div>" for part in (desc.get("company"), desc.get("role")) if part and part.strip())
        out.append({
            "id": d["uuid"],
            "title": d["name"].strip(),
            "created_on": d.get("createdOn"),
            "locations": d.get("workLocations") or [],
            "structured_locations": summary["locations"],
            "employment_type": (d.get("employmentType") or {}).get("label"),
            "pay": d.get("payRangeDetails") or [],
            "url": d.get("url"),
            "html": html,
        })
        time.sleep(0.2)
    return out

Fetch details one at a time or in small batches. A first run on a big board costs one request per job, and Rippling doesn't publish a rate limit.

Gotchas

Location strings are mixed. workLocations holds strings like "Austin, TX", "Remote (Dallas, Texas, US)", "Remote (United States)", a bare country like "Taiwan", or a bare state code like "LA" or "OK". Don't feed two-letter tokens to a country lookup: "LA" is Louisiana and "CA" is California here, not Laos or Canada. Prefer the structured fields from the list's locations when you have them, and fall back to parsing strings only for locations the list didn't cover. See the location normalization guide.

Pay-range locations can disagree with the job's locations. Each payRangeDetails entry has its own location string, and it doesn't always match workLocations. We've seen a job listed as "Remote (Washington, DC, US)" with a pay range labelled "Hybrid (Washington, DC, US)". Use workLocations and the list's workplaceType for location and remote status, and use pay ranges only for pay.

Pay comes in different units. frequency tells you the period, such as YEAR. When a job has several pay ranges, they can differ by location. Group by currency and frequency before taking a min and max, and convert hourly or monthly figures before you compare them with annual ones. The salary extraction guide covers the conversions.

The list has no date. You can't filter old postings until you've fetched the detail. Some boards keep postings open for years, so cache the IDs you've already judged too old instead of re-fetching them every run.

A missing board is a JSON 404. An unknown slug returns HTTP 404 with {"error_code":"RESOURCE_NOT_FOUND"}. A 404 with an HTML body means something else, such as a changed endpoint, so don't treat every 404 as "this company stopped hiring."

No webhooks. Poll the list, compare job IDs with your last run, and treat a job as closed when it drops out of a complete list. See syncing job data in depth.

Public Board API vs Rippling's Official API

Rippling also has an authenticated platform API for its customers. It needs credentials from the employer's own Rippling account and covers that company's internal data. You don't need it to read published job ads: the board endpoints above are public and return what's on the careers page, for any company that hosts its board on Rippling.

When This Is Enough

For one company's jobs, the list-plus-detail pattern is simple, and Rippling gives you more structure than most: separate city, state and country fields, a workplace type and real pay ranges.

When It Isn't

Across many companies and platforms you end up maintaining a different collector for each, with different pagination, date fields and location formats. The ATS public job postings API list shows how much they differ.

CleanJobData reads Rippling boards alongside other platforms and returns one schema, with locations resolved to cities, states and countries, a remote type, an employment type and salary converted to a consistent range. 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 a Rippling 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=ats.rippling.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, so it only returns jobs whose apply link resolves to ats.rippling.com; use company_website_url or employer_id to scope to one employer instead. Coverage depends on which employers are in the dataset, so check that the companies you care about appear before you rely on it.

FAQ

Does Rippling have a public jobs API?

Yes, though it isn't documented. Rippling career pages load jobs from api.rippling.com/platform/api/ats/v2/board/{slug}/jobs, which needs no API key and pages 100 jobs at a time. A second, per-job call to the v1 detail endpoint returns the description, date and pay.

How do I get the job description from Rippling?

Call https://api.rippling.com/platform/api/ats/v1/board/{slug}/jobs/{id} for each job. The description is an object with two HTML blocks, company and role, and you should keep both because some boards put the whole posting in one and leave the other nearly empty.

Does the Rippling jobs API include salary?

Yes, on the detail call. payRangeDetails is an array of pay ranges with currency, frequency, rangeStart and rangeEnd, and it's empty when the employer doesn't publish pay. The list endpoint has no pay data.

Why does the Rippling list return the same job more than once?

A job posted to several locations appears once per location, with the same id and a different locations entry. Dedupe by id and merge the locations before you count or store jobs.