Paylocity Jobs API: Get Paylocity Job Postings as JSON (No Key)

CleanJobData Engineering

TL;DR

Paylocity has no standalone public jobs API, but its career page at recruiting.paylocity.com/recruiting/jobs/All/ plus a company GUID embeds every open job as JSON in a window.pageData script, so one keyless request returns the whole list. Each job has a structured address, a publish date and an IsRemote flag, but only a short text snippet. The full description, and salary when the employer publishes it, come from the schema.org JSON-LD on each job's detail page.

On this page

Paylocity has no standalone jobs API, but its career page at https://recruiting.paylocity.com/recruiting/jobs/All/{guid} embeds every open job as JSON in a window.pageData script, so one keyless request gets the whole list. The full description, and salary when the employer publishes it, come from the schema.org JSON-LD on each https://recruiting.paylocity.com/Recruiting/Jobs/Details/{JobId} page.

That's the DIY route, and it works for a handful of companies you already know. If you'd rather not build and maintain a Paylocity scraper, CleanJobData already ingests ATS platforms and company career sites and serves the jobs normalized through one REST API — see the Jobs API docs.

At a Glance

EndpointGET https://recruiting.paylocity.com/recruiting/jobs/All/{guid} (JSON inside HTML)
Job pageGET https://recruiting.paylocity.com/Recruiting/Jobs/Details/{JobId} (JSON-LD)
API keyNone
PaginationNone; one page holds every job
Full descriptionJSON-LD on each job page; the list has a short snippet
Salary fieldJSON-LD baseSalary, on some jobs
RemoteIsRemote boolean in pageData

Finding the Company GUID

Paylocity career pages have URLs like:

https://recruiting.paylocity.com/recruiting/jobs/All/0f1e2d3c-0000-0000-0000-000000000000/Acme-Inc

The GUID after /All/ identifies the company. The trailing Acme-Inc is cosmetic, and the page loads without it. Every Paylocity company shares the recruiting.paylocity.com host, so the GUID is the only value you need. Companies often link to it from their own careers page, so follow the "View openings" or "Apply" link.

The List Request

curl "https://recruiting.paylocity.com/recruiting/jobs/All/0f1e2d3c-0000-0000-0000-000000000000"

The response is HTML. Somewhere in an inline script is a line that starts window.pageData = {...};, and that object holds every job in Jobs. One job, trimmed:

{
  "JobId": 4600001,
  "JobTitle": "Field Service Technician",
  "LocationName": "Prairieville Shop",
  "PublishedDate": "2026-10-29T10:30:00-05:00",
  "Description": "POWER UP YOUR CAREER WITH ACME. Acme has been proud to serve",
  "JobLocation": {
    "Name": "Prairieville Shop",
    "Address": "100 Main Street",
    "City": "Prairieville",
    "State": "LA",
    "Zip": "70700",
    "Country": "USA"
  },
  "IsRemote": false
}
FieldWhat it is
JobIdNumeric job ID, used in the detail URL
JobTitleJob title
PublishedDatePublish time with a UTC offset
DescriptionA truncated plain-text snippet, not the description
LocationNameThe employer's name for the location, not a place name
JobLocationAddress, City, State, Zip, Country
IsRemoteBoolean remote flag

pageData also has Departments, Locations and ModuleTitle. ModuleTitle is the page heading, which on one company was "ALL JOB POSTINGS", so don't use it as the company name.

Parsing pageData

Don't try to match the whole object with a regex. Find the assignment and let a JSON decoder read one object from that position:

import json
import requests
 
def get_paylocity_jobs(guid: str) -> list[dict]:
    html = requests.get(f"https://recruiting.paylocity.com/recruiting/jobs/All/{guid}", timeout=20).text
    marker = html.find("window.pageData")
    if marker == -1:
        raise ValueError("pageData not found; check the GUID")
    start = html.index("{", marker)
    page_data, _ = json.JSONDecoder().raw_decode(html, start)
    return page_data.get("Jobs") or []

In JavaScript, the same idea works with a small brace-counting loop from the first { after window.pageData, then JSON.parse on the slice.

The Job Page and Its JSON-LD

curl "https://recruiting.paylocity.com/Recruiting/Jobs/Details/4600001"

Each job page has a script type="application/ld+json" block with a JobPosting object:

  • description. The full description, as HTML.
  • hiringOrganization.name. The company name. This is the reliable source for it.
  • datePosted. A timestamp with an offset.
  • jobLocation.address. addressLocality, addressRegion, postalCode, addressCountry.
  • baseSalary. Present on some jobs only: currency plus value.minValue, value.maxValue and value.unitText. On one company, one job in five had it.

See the JobPosting schema guide for the full field list.

import re
 
LD_JSON = re.compile(r'<script[^>]+type="application/ld\+json"[^>]*>(.*?)</script>', re.S | re.I)
 
def get_paylocity_detail(job_id: int) -> dict | None:
    html = requests.get(f"https://recruiting.paylocity.com/Recruiting/Jobs/Details/{job_id}", timeout=20).text
    for block in LD_JSON.findall(html):
        try:
            ld = json.loads(block)
        except json.JSONDecodeError:
            continue
        if ld.get("@type") == "JobPosting":
            return ld
    return None
 
def fetch_paylocity_jobs(guid: str) -> list[dict]:
    jobs = []
    for raw in get_paylocity_jobs(guid):
        ld = get_paylocity_detail(raw["JobId"]) or {}
        loc = raw.get("JobLocation") or {}
        salary = ld.get("baseSalary") or {}
        value = salary.get("value") or {}
        jobs.append({
            "id": str(raw["JobId"]),
            "title": raw.get("JobTitle"),
            "company": (ld.get("hiringOrganization") or {}).get("name"),
            "published": raw.get("PublishedDate"),
            "city": loc.get("City"),
            "state": loc.get("State"),
            "country_alpha3": loc.get("Country"),
            "remote": bool(raw.get("IsRemote")),
            "description_html": ld.get("description") or raw.get("Description"),
            "salary_min": value.get("minValue"),
            "salary_max": value.get("maxValue"),
            "salary_currency": salary.get("currency"),
            "salary_unit": value.get("unitText"),
            "url": f"https://recruiting.paylocity.com/Recruiting/Jobs/Details/{raw['JobId']}",
        })
    return jobs

If a detail page fails, the list still gives you the title, location, date and remote flag, so keep the job and fill in the description later.

Gotchas

Country is an alpha-3 code. JobLocation.Country is "USA", not "US". Convert it with an ISO 3166 table if you store alpha-2.

LocationName isn't a location. It's the employer's label for a site, such as a branch or shop name. Build the location from City and State, and fall back to LocationName only when both are empty.

The list description is a snippet. Description in pageData is cut off mid-sentence and has no formatting. Use the JSON-LD description for the real text.

Salary needs a unit. When baseSalary is present, read unitText before comparing figures, because hourly and annual values share the same fields. The salary extraction guide covers annualizing.

Two dates. PublishedDate in the list and datePosted in the JSON-LD didn't match exactly on the job we checked. Pick one and use it consistently.

Remote has a flag, hybrid doesn't. IsRemote is a real boolean, but there's no hybrid equivalent, so also check the title and location for "remote" or "hybrid".

Edits are easy to miss. If you only fetch job pages for new IDs, you won't see a changed description on a job you already stored.

Career Page Data vs Paylocity's Official API

Paylocity offers APIs to its clients and integration partners, with credentials tied to the employer's Paylocity account. They cover payroll and HR data and aren't meant for reading other companies' job listings. The approach here uses only what the public career page already sends to every visitor, which also means it's undocumented and can change.

When This Is Enough

For one company, a single list request plus one job page per new job is about as simple as career-site scraping gets, and you get a remote flag and structured addresses for free.

When It Isn't

Across many companies you need each one's GUID, a detail request per job for descriptions, and country conversion, before you add other platforms like BambooHR and iCIMS. See which ATS have a public job postings API for how they compare.

CleanJobData reads Paylocity career pages alongside other platforms and returns one schema, with parsed salary when it's published, locations resolved to cities, states and countries, and a remote type. Try it in the Playground or see the API Documentation.

Skip the scraper: get this data from the CleanJobData API

If you'd rather not maintain a Paylocity collector, you can query normalized jobs from ATS platforms and company career sites with a single request. The response has structured city/state/country locations, parsed salary when the employer publishes one, a remote type, and company metadata, and you page through results with the cursor in pagination.next_page.

curl "https://api.cleanjobdata.com/jobs?title=technician&domain=recruiting.paylocity.com&limit=20" \
  -H "Authorization: Bearer $CLEANJOBDATA_API_KEY"

The domain filter is an exact match on the host of each job's application_url, so it only returns jobs whose apply link resolves to recruiting.paylocity.com; use company_website_url or employer_id to scope to one employer instead. Coverage depends on which employers are in the dataset, so check that the companies you care about appear before you rely on it.

FAQ

Does Paylocity have a public jobs API?

Not a standalone one. The career page at recruiting.paylocity.com/recruiting/jobs/All/{guid} embeds every open job as JSON in window.pageData, which you can parse without a key. Paylocity's official APIs need client or partner credentials.

How do I get the full job description from Paylocity?

Fetch https://recruiting.paylocity.com/Recruiting/Jobs/Details/{JobId} and read the description field of the JobPosting JSON-LD block. The Description in the list is only a short snippet.

Does Paylocity include salary?

Sometimes. The job page's JSON-LD has a baseSalary with a range, currency and unit when the employer publishes pay, and it's missing on many jobs.

How do I find a company's Paylocity GUID?

It's the value after /recruiting/jobs/All/ in the career page URL. Anything after the GUID, such as the company name, is optional.