# Eightfold Jobs API: Get Eightfold Job Listings as JSON

> Eightfold career sites serve jobs from a public /api/apply/v2/jobs endpoint with no key. Fixed 10-job pages, detail calls, dates and PCSX tenants.

Source: https://cleanjobdata.com/articles/how-to-scrape-eightfold-job-listings
Published: 2026-11-01

TL;DR: Eightfold career sites, on {company}.eightfold.ai or a vanity host, load jobs from a public JSON endpoint at /api/apply/v2/jobs that needs no key, cookie or token. The list pages with start at a fixed 10 jobs per page whatever num says, and its job_description is often missing, so you fetch /api/apply/v2/jobs/{id} for the full HTML description. Use t_create (epoch seconds) as the post date rather than t_update, and expect some tenants to refuse the v2 list with a 403 and serve jobs through /api/pcsx/search instead.

To get a company's jobs from Eightfold, page through `https://{host}/api/apply/v2/jobs?start=0&num=10`, then call `https://{host}/api/apply/v2/jobs/{id}` for each job's full description. Both are public JSON endpoints with no API key, cookie or CSRF token, and they're the same calls the Eightfold career site makes in your browser. The catch is volume: the list is fixed at 10 jobs per page, and large enterprise tenants have thousands of jobs.

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 Eightfold scraper, CleanJobData already ingests ATS platforms and company career sites and serves the jobs normalized through one REST API — see the [Jobs API docs](https://cleanjobdata.com/docs/api/jobs).

## At a Glance

| | |
|---|---|
| List endpoint | `GET https://{host}/api/apply/v2/jobs?start={n}&num=10` |
| Detail endpoint | `GET https://{host}/api/apply/v2/jobs/{id}` |
| API key | None |
| Pagination | Offset (`start`), fixed 10 per page |
| Full description | Unreliable in the list; always in the detail |
| Post date | `t_create`, epoch seconds |
| Salary field | No |
| Remote | `work_location_option` on the list |

## What Eightfold Is

Eightfold AI sells a talent intelligence platform to large enterprises: banks, insurers, mining and industrial companies, defense contractors and retailers. Many customers use Eightfold's career site on top of a separate applicant tracking system, so Eightfold is often what candidates see even though the requisition lives elsewhere.

Eightfold also has APIs for its customers, which need credentials issued for the employer's account. The career-site endpoints below are different: they serve published jobs to anyone, and the robots.txt on the tenants we checked explicitly allows `/api/apply`.

## Finding the Host

Eightfold career sites live at `https://{company}.eightfold.ai/careers`. Some employers put a vanity host in front, such as `careers.acme.com`, and the same API answers there. The host is all you need.

The list endpoint accepts an optional `domain` query parameter, and the career site sends one. Leave it out. The API works without it and returns the same jobs, while a **wrong** `domain` on a real tenant returns an HTML 404 that looks exactly like a missing board.

## Listing Jobs

```bash
curl "https://acme.eightfold.ai/api/apply/v2/jobs?start=0&num=10"
```

The response is an envelope with a total `count` and a `positions` array, plus many keys that only matter to the career site's UI. A trimmed row (values illustrative):

```json
{
  "count": 1590,
  "positions": [
    {
      "id": 43831466,
      "name": "Maintenance Technician",
      "location": "Belo Horizonte, Minas Gerais, Brazil",
      "locations": ["Belo Horizonte, Minas Gerais, Brazil"],
      "department": "Operations",
      "business_unit": "Base Metals",
      "t_create": 1786766641,
      "t_update": 1790165158,
      "ats_job_id": "5369",
      "display_job_id": "5369",
      "job_description": "Join our maintenance team...",
      "work_location_option": "onsite",
      "canonicalPositionUrl": "https://acme.eightfold.ai/careers/job/43831466"
    }
  ]
}
```

### Pagination

The page size is a hard 10. We tried `num` values from 11 to 1,000, and other names such as `limit`, `page_size`, `per_page` and `size`. Every request still returned 10 jobs. Advance `start` by 10 until you've covered `count`.

You can't ask for newest jobs only, either. `sort_by` is silently ignored with any value, and the default order is only loosely newest-first. So every sync has to page the whole list: a 1,590-job tenant is 159 requests before you fetch a single description. Jobs open and close while you page, so the list can shift by a few rows. Dedupe by `id`, and accept a small gap against `count` rather than retrying forever.

## Fetching the Detail

```bash
curl "https://acme.eightfold.ai/api/apply/v2/jobs/43831466"
```

The detail has the same core fields as the list row, and its `job_description` is the full HTML description. You need it because the list's `job_description` is unreliable: in our samples it was present on 37 of 40 rows on one tenant, 6 of 40 on another and 0 of 21 on a third, and when present it can be cut short.

`work_location_option` is the exception: it's on the list row, and the detail returns `null` for it. Keep the list row and merge it with the detail.

## Fetching Eightfold Jobs (Python)

```python
import time
from datetime import datetime, timezone

import requests

HEADERS = {"accept": "application/json"}


def get_json(url, params=None):
    res = requests.get(url, params=params, headers=HEADERS, timeout=30)
    res.raise_for_status()
    return res.json()


def list_eightfold_jobs(host):
    rows, start, total = {}, 0, None
    while total is None or start < total:
        data = get_json(f"https://{host}/api/apply/v2/jobs", {"start": start, "num": 10})
        total = data["count"]
        page = data.get("positions") or []
        if not page:
            break
        for row in page:
            rows.setdefault(row["id"], row)  # the list can shift while you page
        start += 10  # fixed page size, whatever num says
        time.sleep(0.3)
    return list(rows.values())


def fetch_job(host, row):
    detail = get_json(f"https://{host}/api/apply/v2/jobs/{row['id']}")
    return {
        "id": str(row["id"]),
        "title": row["name"].strip(),
        "posted_at": datetime.fromtimestamp(row["t_create"], tz=timezone.utc),
        "locations": detail.get("locations") or row.get("locations") or [],
        "workplace": row.get("work_location_option"),  # list only
        "description_html": detail.get("job_description") or row.get("job_description") or "",
        "url": row.get("canonicalPositionUrl") or f"https://{host}/careers/job/{row['id']}",
    }


rows = list_eightfold_jobs("acme.eightfold.ai")
jobs = [fetch_job("acme.eightfold.ai", r) for r in rows[:20]]
```

`t_create` is in the list, so you can drop jobs older than your cutoff before making any detail calls.

## Fields Worth Knowing

- **`id`** is numeric and stable. Use it as the dedup key.
- **`name`** is the job title.
- **`t_create`** is the creation time in epoch **seconds**. It looked like a real creation date on every tenant we checked: spread over weeks to months, at working-hours times, never in the future.
- **`t_update`** is not a post date. On one tenant, 966 of 1,590 jobs had a `t_update` in the same hour, which points to a bulk refresh. The career page's JSON-LD `datePosted` also moves between the two values and carries no time zone, so `t_create` is the field to trust.
- **`locations`** is an array of free-text strings, and `location` is the first one. There are no separate city, state or country fields, and the format varies by tenant ("Brazil", "Melbourne, FL, US", "United States-Florida-Melbourne"). The [location normalization guide](https://cleanjobdata.com/guides/job-location-normalization) covers resolving them.
- **`work_location_option`** is `onsite`, `hybrid`, `remote` or `remote_local`. `remote_local` reads as remote limited to a region, but we haven't confirmed what the career site shows for it.
- **`canonicalPositionUrl`** is the candidate-facing job page, on the vanity host when the tenant has one.
- **`department`**, **`business_unit`**, **`ats_job_id`** and **`display_job_id`** are present on many tenants. The last two carry the employer's own requisition number.
- **No salary, no employment type** and no reliable company name or website. Pay, when published, is in the description text.

## PCSX Tenants

Some tenants answer the v2 list with a 403 and a JSON body of `{"message": "Not authorized for PCSX"}`. That doesn't mean the board is gone. These tenants serve their list through a second search API:

```bash
curl "https://acme.eightfold.ai/api/pcsx/search?domain=acme.com&start=0&num=10"
```

This one does need `domain`, set to the employer's own domain (`acme.com`, not the Eightfold host). The response is wrapped as `{"status": 200, "error": ..., "data": {"count": ..., "positions": [...]}}`, and the rows use camelCase names: `creationTs` instead of `t_create`, `workLocationOption` instead of `work_location_option`, `positionUrl` as a relative path, and an extra `standardizedLocations` array. Page it the same way, 10 at a time. The v2 **detail** endpoint still works on these tenants, so everything after the list is the same.

## Gotchas

**There's no clean "board removed" signal.** A tenant that no longer exists fails at DNS with no HTTP status, and a mistyped host gives an HTML 404. Neither is a reliable reason to delete a company's jobs; investigate before you expire anything.

**401, 403 and 404 on the list are usually not about the board.** They mean a PCSX tenant, a wrong `domain`, or a change on Eightfold's side.

**Volume adds up.** A big tenant is hundreds of list pages plus one detail call per new job. Keep concurrency low, pause between batches, and back off on 429 and 5xx.

**Reposts keep their ID.** If an employer reposts a job, the `id` and `t_create` can stay the same, so the job won't look new.

**No webhooks.** Poll the list, diff the IDs against your last complete run, and only expire jobs when the full list came back. See [syncing job data in depth](https://cleanjobdata.com/guides/syncing-job-data-in-depth).

## When This Is Enough

For one employer, these endpoints are clean JSON with no authentication, and the code above is most of what you need.

## When It Isn't

Across many Eightfold tenants, the fixed page size makes every sync expensive, locations stay free text, and PCSX tenants need their own code path. Eightfold is also one platform among many; the [ATS public job postings API list](https://cleanjobdata.com/articles/ats-public-job-postings-api-list) compares 33, and [Workday](https://cleanjobdata.com/articles/how-to-scrape-workday-job-listings) is the other big enterprise platform with an offset-paged JSON list and a detail call per job.

## Skip the scraper: get this data from the CleanJobData API

If you'd rather not maintain an Eightfold 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`.

```bash
curl "https://api.cleanjobdata.com/jobs?title=engineer&company_website_url=acme.com&limit=20" \
  -H "Authorization: Bearer $CLEANJOBDATA_API_KEY"
```

Eightfold jobs come back in the same schema as every other source, with their free-text locations resolved to cities, states and countries. Use `company_website_url` or `employer_id` to scope to one employer. 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](https://cleanjobdata.com/docs/api/jobs) for every filter and response field
- [Playground](https://cleanjobdata.com/playground) to run queries without writing code
- [Pricing](https://cleanjobdata.com/pricing) — the Trial is free with no card required

## FAQ

### Does Eightfold have a public jobs API?

Yes, though it's undocumented. Every Eightfold career site calls `GET https://{host}/api/apply/v2/jobs` for the list and `/api/apply/v2/jobs/{id}` for each job, with no key, cookie or token.

### Why does Eightfold only return 10 jobs per page?

The page size is fixed at 10 on the server. Larger `num` values and other page-size parameters are ignored, so you advance `start` by 10 until you reach `count`.

### Which Eightfold date field is the posting date?

Use `t_create`, which is epoch seconds. `t_update` changes when Eightfold refreshes jobs in bulk, so many jobs share the same value.

### What does "Not authorized for PCSX" mean?

The tenant serves its job list through Eightfold's PCSX search API instead of the v2 list. Call `/api/pcsx/search` with the employer's own domain in the `domain` parameter; the v2 detail endpoint still works for each job.
