Eightfold Jobs API: Get Eightfold Job Listings as JSON

CleanJobData Engineering

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.

On this page

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.

At a Glance

List endpointGET https://{host}/api/apply/v2/jobs?start={n}&num=10
Detail endpointGET https://{host}/api/apply/v2/jobs/{id}
API keyNone
PaginationOffset (start), fixed 10 per page
Full descriptionUnreliable in the list; always in the detail
Post datet_create, epoch seconds
Salary fieldNo
Remotework_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

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):

{
  "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

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)

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 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:

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.

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 compares 33, and Workday 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.

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.

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.