All platforms

JazzHR Jobs API

Query jobs from JazzHR boards through one API — and why JazzHR has no JSON API, only an HTML table and per-job JSON-LD.

?source=jazzhrsource reference

JazzHR is recruiting software for small teams, with boards at <slug>.applytojob.com. It's the one platform in this pool with no JSON API at all — no feed, no widget endpoint, no public REST. There's an HTML listing page and there are per-job detail pages carrying JobPosting JSON-LD, and that's the whole surface. You are parsing markup, and the markup is what you get.

Try it#

BASH
curl -G https://api.hyperjobs.io/v1/jobs \
  -H "Authorization: Bearer $HYPERJOBS_KEY" \
  --data-urlencode "source=jazzhr" \
  --data-urlencode "time_frame=7d" \
  --data-urlencode "limit=2"
Illustrative — the shape, not a live response
{
  "data": [
    {
      "id": "jazzhr:aBcD3fGh1J",
      "title": "Field Service Technician",
      "company": {
        "slug": "ridgeline-hvac",
        "name": "Ridgeline HVAC",
        "domain": "ridgelinehvac.com",
        "website": "https://ridgelinehvac.com",
        "logo": "https://media.licdn.com/dms/image/…",
        "industry": "Construction",
        "employee_count": 38
      },
      "location": {
        "remote": false,
        "countries": ["United States"],
        "cities": ["Reno"],
        "regions": ["NV"],
        "raw": ["Reno, NV, United States"]
      },
      "employment_type": ["FULL_TIME"],
      "work_arrangement": null,
      "seniority": "Mid-Senior level",
      "salary": {
        "min": 28,
        "max": 34,
        "currency": "USD",
        "unit": "HOUR",
        "annual_min": 58240,
        "annual_max": 70720,
        "summary": null
      },
      "skills": ["hvac", "refrigeration"],
      "taxonomies": ["Skilled Trades"],
      "apply_url": "https://ridgelinehvac.applytojob.com/apply/jobs/details/aBcD3fGh1J",
      "url": "https://ridgelinehvac.applytojob.com/apply/jobs/details/aBcD3fGh1J",
      "source": "jazzhr",
      "posted_at": "2026-07-10T00:00:00.000Z",
      "expires_at": null,
      "updated_at": "2026-07-13T20:16:44.000Z"
    }
  ],
  "total": 2,
  "limit": 2,
  "offset": 0
}

How JazzHR boards actually work#

An HTML table, then a page per job:

BASH
# 1. Listing — an HTML table, one <tr id="row_job_…"> per posting
curl -L "https://<slug>.applytojob.com/apply/jobs"
 
# 2. Detail — JobPosting JSON-LD in the page head
curl -L "https://<slug>.applytojob.com/apply/jobs/details/<id>"

<slug> is the subdomain. Both need redirects followed.

The listing rows are the id source. Each <tr id="row_job_…"> contains an anchor with class job_title_link whose href is /apply/jobs/details/<id> — that's the posting id and the title in one element. A resumator_department span carries the department, and the row's last cell carries the location.

Everything else comes from the detail page's JSON-LD, which is where JazzHR quietly redeems itself.

The JSON-LD is better than you'd expect#

Because it's schema.org rather than a bespoke JSON shape, you get standard fields from a standard parser:

JSON-LD fieldGives you
descriptionThe full body
datePostedThe posting date — the listing has none
validThroughA real declared expiry. Rare, but structured
baseSalaryStructured pay when the employer published it
jobLocation.addressLocality, region, country — better than the table cell
jobLocationTypeTELECOMMUTE when remote. Absent otherwise
experienceRequirementsA plain level label — near 100% populated
hiringOrganization.sameAsThe employer's homepage — 100%
hiringOrganization.logoThe employer's logo — around 81%

experienceRequirements is worth calling out: schema.org allows an object there, but JazzHR only ever emits the string form — Mid Level, Experienced, Manager/Supervisor — and it's on essentially every posting. That's free seniority from a field most platforms don't populate at all.

jobLocationType is only present on remote postings. Its absence is not a signal that the job is on-site; it's just absence. That's why work_arrangement is null rather than On-site on most JazzHR rows.

What bites you#

Cloudflare-gated tenants come back hollow, not failed

Some JazzHR tenants sit behind Cloudflare and return a plain 403 to an ordinary client. The listing page often still parses — so you get rows, with ids and titles — while every JSON-LD detail fetch fails.

Because the detail carries description, date, location and salary together, a blocked tenant yields a full board of hollow postings. Titles and nothing else. It looks like a company that publishes terrible job ads, not like a fetch problem.

A browser-fingerprint tier clears many of them. Residential IPs are the real fix, and neither is a code change — this is the failure mode to know about rather than one we can parse our way out of.

The listing is a table, and tables change. Every field on the listing side is a regex against markup: row ids, an anchor class, a span class, the last cell. JazzHR owes you nothing here — there's no contract, no version, no deprecation notice. It works until a template ships.

The last cell isn't reliably the location. It's the location because that's where JazzHR puts it, not because anything says so. The JSON-LD's structured address is better whenever the detail fetch succeeds, which is exactly when the listing value matters least.

uniqueJobCode is the requisition id, and it's in the JSON-LD under a non-obvious name.

Or use ours#

JazzHR is markup parsing plus an N+1, against 2,689 tracked boards, where a meaningful slice of tenants will hand you title-only rows unless you're coming from the right kind of client.

You get the rows joined to their JSON-LD, the structured address preferred over the table cell, experienceRequirements mapped to a real seniority, salary parsed from baseSalary, the employer's homepage and logo carried through, and the hollow-tenant problem absorbed on our side rather than yours. Blocked tenants stay in the rotation and yield sparse rows rather than poisoning a run — which is a tradeoff we're making on your behalf and would rather you knew about.

Plus dedup, expiry by board diffing, and the company join.

JazzHR is in the fast refresh tier, attempted every cycle — see coverage.

Quickstart · Pricing

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

Get an API key