# GoHire Jobs API

> Query jobs from GoHire boards through one API with ?source=gohire — plus how GoHire's widget endpoints really behave.

Source: https://hyperjobs.io/ats/gohire

GoHire is hiring software aimed at small businesses and startups — the kind of
employer with one person doing recruitment alongside three other jobs. Boards
live at `jobs.gohire.io/<slug>-<clientHash>`, and the careers widget those boards
embed is backed by two undocumented public endpoints.

## What the adapter reads

Both are unauthenticated, and both are mandatory:

```bash
# List — the whole board
curl "https://api2.gohire.io/widget-jobs/<clientHash>"

# Detail — one job
curl "https://api.gohire.io/widget-job?clientHash=<hash>&jobId=<id>"
```

Note `api.` for the detail and `api2.` for the list. That is not a typo, and it
is the first thing you'll get wrong.

Neither call is skippable, because each one holds exactly what the other lacks:

| | List | Detail |
| --- | --- | --- |
| Posting date | **The only source** | No date field at all |
| Description | `""` on 56 of 56 rows | **The only source** |
| Location | Free text | Structured `city` / `county` / `country` |
| Employer name | — | `client.name` |

So it's a hard N+1 with no way out. No pagination exists — the list returns the
whole board (16, 16 and 24 jobs on the boards sampled), though the largest board
we've seen is 24, so a cap can't be ruled out.

The `clientHash` is the 8-character token from the board URL, and it's
case-insensitive at the API (`DE5JLHJO` and `de5jlhjo` return byte-identical
responses) — which matters only because it means you must normalize it before
using it in a dedup key, or the same board arrives twice.

## What bites you

### An unknown board returns `{}` at 200, never 404

Pass a hash that doesn't exist, or pass the full slug by mistake instead of just
the hash, and the API returns **HTTP 200 with an empty envelope**. No 404, no
error, no message.

An empty envelope is the only failure signal this API gives. That means "wrong
token" and "board with nothing open" are byte-identical, and a typo in a board
list reads as a company that isn't hiring — quietly, forever, until someone
checks by hand.

### `sitemap.txt` is a honeypot

<Warning title="Do not build discovery on this file">
  `jobs.gohire.io/sitemap.txt` exists, returns 200, and lists **20,173 URLs**.
  It looks exactly like the global board index that this platform otherwise
  lacks, and it is worthless.

  Every URL is on a dead legacy `{slug}-{numeric-clientId}` scheme. **0 of 20**
  board roots sampled resolve, and the numeric id in them is *not* the
  `clientHash` the API needs — so even the ones you can map are unusable
  against the endpoints above.

  There is no usable global index for GoHire. Boards have to be found the hard
  way, like nearly everywhere else. [WeRecruit](/ats/werecruit)'s sitemap is real
  and enumerates its whole platform; [HireHive](/ats/hirehive) just hands you
  every tenant. GoHire looks like it does and doesn't.
</Warning>

### Structured location fields repeat themselves

Tenants routinely fill `city`, `county` and `country` with the same value,
producing `"North America, North America, US"` when joined naively. Dedupe
case-insensitively before you join, or the geocoder gets a string no place
matches.

### Dates are zone-less human strings

`"10 June, 2026"`. Not ISO, not a timestamp, and no zone anywhere. `Date.parse`
reads that at your server's local midnight, which shifts the date a day west for
anyone running in Europe. It needs a bare-calendar-date parser, and the
day-month-year order needs reordering before most of them will take it.

### There is no JSON-LD

Job pages carry none, so the usual schema.org fallback — the thing that rescues
[JazzHR](/ats/jazzhr) and half this pool — isn't available. The widget endpoints
are the entire surface.

Where GoHire is kind: `client.name` on the detail is the real employer name,
which matters more than it sounds. The board token is an opaque hash, so without
that field a GoHire posting cannot be attributed to a company at all. Salary
arrives as free text (`"Competitive"`, `"£35,031.40 per annum"`) and is kept
verbatim rather than guessed at. Descriptions came back at 100% on the boards
tested (16/16 and 24/24).

## Where this stands

`?source=gohire` is served today, and the queries on this page return rows.

What bounds this platform is the honeypot section, not the fetching: the widget
endpoints are cheap to read, and there is no cheap way to learn which boards
exist. Breadth on GoHire grows a board at a time, which is the bottleneck here.

The keys of [`/v1/meta`](/docs/api-reference/meta)`.sources` are the live
vocabulary — the values `?source=` actually accepts, with counts, and the only
thing worth coding against. [Sources & coverage](/docs/reference/sources) says the
same in prose.

See [all platforms](/ats) · [quickstart](/docs/quickstart).
