All platforms

Breezy HR Jobs API

Query jobs from Breezy HR boards through one API — and why Breezy's posting pages 403 your client no matter which IP you come from.

?source=breezysource reference

Breezy HR is a visual, pipeline-first hiring tool aimed at small companies. Boards live at <tenant>.breezy.hr, and there's a public per-tenant JSON feed that returns the whole board in one call. It also returns no descriptions, and the place they live is behind a WAF that doesn't care what IP you use. Breezy is the only platform in this pool besides one other where a browser-fingerprint fetch tier is mandatory rather than a fallback.

Try it#

BASH
curl -G https://api.hyperjobs.io/v1/jobs \
  -H "Authorization: Bearer $HYPERJOBS_KEY" \
  --data-urlencode "source=breezy" \
  --data-urlencode "time_frame=7d" \
  --data-urlencode "limit=2"
Illustrative — the shape, not a live response
{
  "data": [
    {
      "id": "breezy:brightpath_customer-success-manager",
      "title": "Customer Success Manager",
      "company": {
        "slug": "brightpath",
        "name": "BrightPath",
        "domain": "brightpath.co.za",
        "website": "https://brightpath.co.za",
        "logo": "https://media.licdn.com/dms/image/…",
        "industry": "Financial Services",
        "employee_count": 45
      },
      "location": {
        "remote": false,
        "countries": ["South Africa"],
        "cities": ["Cape Town"],
        "regions": ["Western Cape"],
        "raw": ["Cape Town, Western Cape, South Africa"]
      },
      "employment_type": ["FULL_TIME"],
      "work_arrangement": "Hybrid",
      "salary": {
        "min": 8000,
        "max": 10000,
        "currency": "ZAR",
        "unit": "MONTH",
        "annual_min": 96000,
        "annual_max": 120000,
        "summary": "R8,000 – R10,000 / month"
      },
      "skills": ["account management", "onboarding"],
      "taxonomies": ["Customer Service"],
      "apply_url": "https://brightpath.breezy.hr/p/a1b2c3d4e5f6-customer-success-manager",
      "url": "https://brightpath.breezy.hr/p/a1b2c3d4e5f6-customer-success-manager",
      "source": "breezy",
      "posted_at": "2026-07-08T00:00:00.000Z",
      "expires_at": null,
      "updated_at": "2026-07-13T14:20:07.000Z"
    }
  ],
  "total": 2,
  "limit": 2,
  "offset": 0
}

How Breezy boards actually work#

One GET for the list, no auth, no pagination:

BASH
curl "https://<tenant>.breezy.hr/json"

<tenant> is the subdomain. The response is a bare JSON array. Per job: friendly_id, name, url, published_date, type, location, locations[], department, salary, and company.logo_url.

And no description. The body exists only on the HTML posting page — one fetch per posting, which is the entire cost of this platform.

The posting page is where it gets interesting#

BASH
curl "https://<tenant>.breezy.hr/p/<friendly_id>"

Most posting pages carry a JobPosting JSON-LD block, and that's the clean path. Some tenants ship none — there the body exists only as a <div class="description"> in the page HTML, and you have to walk the div nesting to find the matching close tag, because the body is rich HTML full of nested divs and a lazy .*?</div> truncates it at the first one. Two templates, two parsers, no way to know which you'll get until you fetch.

Breezy's WAF fingerprints your client, not your IP

The posting pages 403 a plain HTTP client on both direct and residential proxy egress. Rotating IPs does nothing, because the IP was never the problem — it's the TLS and HTTP/2 fingerprint. This is the one wall in this pool that a proxy budget cannot buy its way past.

The fix is a fetch tier that reproduces a real browser's TLS and h2 fingerprint (we use impit). Without it, every posting page fails, the catch swallows it, and you get a full board of rows with description: null — a 200-shaped success containing nothing worth having.

That last sentence is the whole reason Breezy is worth writing about. The failure isn't loud. It's a complete-looking board of description-less jobs, which under a no-description-no-job rule means a board of nothing.

What bites you#

is_remote is true for hybrid roles. Same trap as Ashby, same consequence. Verified live on a tenant whose hybrid jobs all carried is_remote: true. The bool alone mislabels every hybrid job as fully remote.

remote_details.value is the authoritative field — remote or hybrid — and it can express the difference. Hybrid has to win first; only then does an explicit remote mean remote, and only an explicit is_remote: false means on-site.

friendly_id repeats across tenants. They're human-readable — accountant, sales-manager — so a key of breezy:<friendly_id> collides across boards and one company's accountant overwrites another's. Ours is scoped: breezy:<tenant>_<friendly_id>.

The display location drops the country. location.name gives Cape Town, ZA; the structured city / state.name / country.name gives Cape Town, Western Cape, South Africa. Prefer the structured fields.

salary is free text. R8,000 – R10,000 / month — a currency symbol you have to recognise, an en dash, and a period suffix. It parses, but it's a string, not a number, and it's often absent.

There's no expiry field. published_date is the only date. A pulled job is only detectable by re-fetching the board.

Or use ours#

Breezy is a request per posting, through a browser-fingerprint tier, against two different page templates, keyed on ids that collide across tenants.

We pay that N+1 on every new posting — a description-less row is worthless, so there's no cheap mode worth having — and skip it for postings already in the pool, so a re-scrape of an unchanged board costs one call. When a posting page 403s, we climb to the impersonation tier rather than record an empty row. When the JSON-LD is missing, we walk the div nesting. We track 1,385 Breezy boards.

Then the usual: remote_details read instead of the hybrid-inclusive bool, tenant-scoped ids, structured locations preferred over the display string, the free-text salary parsed and normalised, dedup, and the company join.

Breezy is in the fast refresh tier, attempted every cycle, despite the N+1 — 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