All platforms

Phenom Jobs API

Query jobs from Phenom career sites through one API — and how the Phenom search widget, its CSRF handshake and tenant-dependent descriptions work.

?source=phenomsource reference

Phenom People powers the career sites of large enterprises — the kind of employer that runs its own branded jobs domain rather than a subdomain of its ATS. There's no phenom.com/<company> pattern to look for: a Phenom board looks like jobs.bell.ca or an enterprise's own careers host, and the only reliable tell is the shape of the URLs underneath it. Our queue tracks 85 Phenom boards, the smallest of the enterprise providers here, and every one of them was identified by its endpoints rather than its domain.

Try it#

BASH
curl -G https://api.hyperjobs.io/v1/jobs \
  -H "Authorization: Bearer $HYPERJOBS_KEY" \
  --data-urlencode "source=phenom" \
  --data-urlencode "time_frame=7d" \
  --data-urlencode "limit=2"
Illustrative — the shape, not a live response
{
  "data": [
    {
      "id": "phenom:R-441209",
      "title": "Network Operations Specialist",
      "company": {
        "slug": "atlas-telecom",
        "name": "Atlas Telecom",
        "domain": "atlastelecom.ca",
        "website": "https://atlastelecom.ca",
        "logo": "https://media.licdn.com/dms/image/…",
        "industry": "Telecommunications",
        "employee_count": 41000
      },
      "location": {
        "remote": false,
        "countries": ["Canada"],
        "cities": ["Montreal"],
        "regions": ["Quebec"],
        "raw": ["Montreal, Quebec, Canada"]
      },
      "employment_type": ["FULL_TIME"],
      "work_arrangement": "Hybrid",
      "seniority": "Mid-Senior level",
      "salary": null,
      "skills": ["bgp", "mpls", "network troubleshooting"],
      "taxonomies": ["IT & Infrastructure"],
      "apply_url": "https://jobs.atlastelecom.ca/us/en/job/R-441209",
      "url": "https://jobs.atlastelecom.ca/us/en/job/R-441209",
      "source": "phenom",
      "posted_at": "2026-07-11T00:00:00.000Z",
      "expires_at": null,
      "updated_at": "2026-07-14T03:52:18.000Z"
    }
  ],
  "total": 2,
  "limit": 2,
  "offset": 0
}

How Phenom boards actually work#

Phenom's search is a widget, and reaching it takes a handshake:

BASH
# 1. Seed the CSRF cookie and token
curl -c jar "https://<site>/us/en/search-results"
 
# 2. POST the widget endpoint with the token
curl -b jar -X POST "https://<site>/widgets" \
  -H "content-type: application/json" \
  -H "x-csrf-token: <token>" \
  -d '{"ddoKey":"refineSearch","from":0,"size":100,"pageName":"search-results", …}'

The token appears in two places — a cookie whose name contains csrf, and a "csrfToken": "…" embedded in the search page's HTML. Either works; you need one. Skip the seed request and the POST fails.

The response nests further down than you'd guess: refineSearch.data.jobs[] with refineSearch.data.totalHits next to it. Page with from, 100 at a time.

Identifying a Phenom board is the hard part#

Phenom has two URL signatures, and both only mean Phenom on the employer's own careers host: the /{country}/{lang}/search-results page, and the /widgets endpoint itself.

Match on the path, not the URL string

Testing whether the whole URL contains /widgets is the obvious rule and it is wrong. It claims JazzHR's own app.jazz.co/widgets/bootstrap.js. It claims any careers page with a CDN chat widget on it. We hit exactly that: a detector that matched substrings against the full URL happily returned the raw URL as a board token for boards that were not Phenom at all.

Match /widgets as the entire path, match search-results as a path pattern, and normalise to the origin — which is what every real Phenom board token looks like anyway.

Structured fields Phenom gives you#

latitude and longitude — exact coordinates, no geocoding. ml_skills, a structured skills array present on close to every posting on Phenom tenants. multi_location[] for multi-site roles. department, category, and unit (the business unit, finer than department).

Seniority arrives through whichever of three fields a tenant uses: jobLevel (Professional, Entry Level, Management, Student), or — on tenants that omit it — an experienceLevel career ladder (Early Career, Mid-Career, Senior Level, Co-op/Intern), or type. Non-level values like Non-Salaried and Leadership Programs map to nothing and should stay null rather than become a seniority.

What bites you#

Whether descriptions ship inline is tenant-dependent

This is the defining property of Phenom and there's no flag for it. Some tenants return a full description in the search payload — one live board returns roughly 14,000 characters per job. Others omit the field entirely, and the full text exists only on the job-detail page.

There's a descriptionTeaser as a fallback, which is a teaser: enough to look populated in a spot check, not enough to be the job ad. Location, coordinates and dates are always complete, so those rows are worth keeping — but a Phenom posting's description quality depends on which employer's site it came from, and nothing in the response tells you which case you're in.

The location field isn't the one you'd guess. Phenom's real field is locationMontreal, Quebec. There is no cityState, no city. Reaching for those returns undefined and the city is silently dropped. The country is separate, so a city-only string needs it appended.

type means two different things on two different tenants. On most it's the employment type — Full-Time. On career-ladder tenants it holds the level instead — Senior Level. Same key, different meaning, no discriminator.

Salary is detail-only and carries no currency. Pay-transparency tenants embed minimumSalary / maximumSalary / salaryType in the job-detail page's phApp.ddo blob. There's no currency key at all — pay transparency is US driven, so USD is a reasonable inference only when the job is confirmed US by its country field. Otherwise the numbers are real and the currency is unknown, and we leave it null rather than assume. That's why salary is null on most Phenom postings here: it's an opt-in N+1 against a field the platform barely publishes.

Or use ours#

Phenom is a CSRF handshake, a POST with a fifteen-key body, a response nested three levels down, a detector that's easy to get dangerously wrong, and a description that may or may not be there depending on which enterprise you're looking at.

You get it flattened: the handshake done, the pagination walked, coordinates and ml_skills carried through, seniority resolved from whichever of three fields the tenant uses, type disambiguated, and the description taken inline where the tenant ships it. Rows are kept rather than dropped when only a teaser is available — with the honest consequence that Phenom's description fill is uneven in a way we can't fix from outside.

Phenom is in the fast refresh tier, attempted every cycle — see coverage — and it's one of the 17 sources you can actually filter on, which several larger enterprise adapters are not.

Quickstart · Pricing

Every source above normalises onto the same schema, with the hiring company already joined onto each posting.

Get an API key