Paylocity Jobs API
Query jobs from Paylocity boards through one API with ?source=paylocity — plus how Paylocity's recruiting board really behaves.
Paylocity is a payroll and HR platform with recruiting bolted on, sold to US small
and mid-sized businesses. Its boards live at
recruiting.paylocity.com/recruiting/jobs/All/<guid>, and they are the ones
churches, clinics, school districts and regional operators actually post to. There
are 4,782 Paylocity postings in this dataset — a number that moves hourly, so read
it from /v1/meta rather than trusting this sentence.
This is the platform in our small-business set with the most honest caveat attached, and the caveat is the interesting part.
Try it#
curl -G https://api.hyperjobs.io/v1/jobs \
-H "Authorization: Bearer $HYPERJOBS_KEY" \
--data-urlencode "source=paylocity" \
--data-urlencode "time_frame=7d" \
--data-urlencode "limit=2"{
"data": [
{
"id": "paylocity:1849327",
"title": "Parish Office Coordinator",
"company": {
"slug": "001ed382-3060-4f7b-a324-d44d173bb6c8",
"name": "St Catherine of Siena Church",
"domain": "stcatherinekc.org",
"website": "https://stcatherinekc.org",
"logo": "https://media.licdn.com/dms/image/…",
"industry": "Religious Institutions",
"employee_count": 31
},
"location": {
"remote": false,
"countries": ["United States"],
"cities": ["Kansas City"],
"regions": ["MO"],
"raw": ["Kansas City, MO, United States"]
},
"employment_type": ["FULL_TIME"],
"work_arrangement": null,
"seniority": null,
"salary": null,
"skills": ["administration", "scheduling"],
"taxonomies": ["Administrative"],
"apply_url": "https://recruiting.paylocity.com/Recruiting/Jobs/Details/1849327",
"url": "https://recruiting.paylocity.com/Recruiting/Jobs/Details/1849327",
"source": "paylocity",
"posted_at": "2026-07-08T15:41:00.000Z",
"expires_at": null,
"updated_at": "2026-07-14T10:22:18.000Z"
}
],
"total": 2,
"limit": 2,
"offset": 0
}How Paylocity boards actually work#
The board server-renders its whole list into a global, and the body needs a second call:
# 1. List — the board HTML carries `window.pageData` with every posting in it
curl -L "https://recruiting.paylocity.com/recruiting/jobs/All/<guid>"
# 2. Detail — schema.org JobPosting JSON-LD
curl -L "https://recruiting.paylocity.com/Recruiting/Jobs/Details/<JobId>"window.pageData is genuinely good: a Jobs array with JobTitle, a full
structured JobLocation, IsRemote, HiringDepartment and a PublishedDate
that carries a real offset. What it does not carry is the description —
Description is always "" on the list — so the body is one detail call per
posting, and that is where this platform gets difficult.
Ignore the documented feed API#
`/recruiting/api/feed/jobs/{guid}` returns HTTP 500
Paylocity advertises a feed endpoint as its integration route. Called with a board GUID it returns 500 — verified on three tenants.
It isn't broken. The feed key is a separate per-tenant opt-in credential, not the GUID sitting in the board URL. So the official-looking endpoint is a dead end for public boards, and anyone who "fixes" a working scraper by switching to the documented API gets 500s for their trouble.
window.pageData is the real route.
The burst-429 is the whole story#
The detail endpoint throttles on a burst budget, not a rate. Roughly 12 or 13 requests get through and the rest 429 — measured identically at concurrency 4, 3 and 2. Turning the pool down does not fix it, because it isn't a concurrency problem. The budget refills within seconds.
Here's why that matters more than a normal rate limit: a 429 on the detail page means no JSON-LD, which means a description-less row that still counts as a successful scrape. Nothing errors. The board reports success. The jobs are hollow.
| Approach | Descriptions |
|---|---|
| Fetch details with no 429 handling | 14% |
| Backoff and retry, 1s → 16s | 93% |
The residual 7% on that measured board is real and not fixable: those postings have no JSON-LD and no description div, because the employer left the body blank.
What that means for the rows we serve
Across all 4,782 Paylocity rows in the dataset today, 76.5% carry a description — not the 93% a clean single-board run achieves.
The gap is the same throttle, met at rotation scale rather than on one board
with room to breathe. We'd rather print the number that describes the rows you
will actually get than the one from the best run. It's among the lowest
description fills of anything we serve, and it is the honest state of this
platform today. Check /v1/meta and
coverage for where it currently sits.
What bites you#
LocationName is a venue, not a place. It reads
"St Gabriel Catholic Church Kansas City" — a building's name, which no geocoder
will do anything sensible with. The structured City / State / Country on
JobLocation is the ground truth. Take the label instead and you geocode a
parish hall.
The board token is a bare GUID, and so is the company name. Every Paylocity
board is identified by a UUID, which means the fallback company name minted from
the token is a UUID too. The detail page's hiringOrganization.name is the only
human-readable employer this platform ever emits — so the description fetch that
the 429 eats is also the identity fetch. Lose one, lose both.
There's no tier-1 company-domain route. Paylocity publishes nothing that
resolves a board to a company domain, so identity has to be inferred from outside
the source and does not always land. A related trap: the JS-shell notice on some
board pages points at enable-javascript.com, which an unguarded domain-grabber
will cheerfully record as the employer's website.
A bare UUID looks like something else's id. A guard that treats bare UUIDs as junk silently dropped every ADP and Paylocity board at once. Here the UUID is the real board id.
Or use ours#
Two calls per posting against a burst throttle that no concurrency setting avoids, across 2,653 GUID-named boards, where the fetch you need for the body is also the fetch you need for the employer's name.
You get the backoff already paid for, the structured address preferred over the
venue label, PublishedDate carried through with its offset, the employer's name
lifted out of the detail JSON-LD instead of a UUID, Paylocity rows deduplicated
against the same job on LinkedIn or elsewhere, expiry by board
diffing, and whatever company identity can be resolved
for a platform that publishes none. And you get told, in the note above, exactly
how complete the descriptions are rather than finding out later.
Query it with ?source=paylocity.
Quickstart, then pricing.
Every source above normalises onto the same schema, with the hiring company already joined onto each posting.
Get an API key