- highFeed links point at easyapply.co, not the tenant subdomain
- Items in a tenant's own RSS feed link to the apex host, so a naive host check treats every job as belonging to no board. Take the employer from the feed you requested, and only fall back to page-level attribution for links that arrived from outside.
- mediumJob IDs come in two different shapes
- GetHired publishes both /a/{uuid} and /job/{slug} routes for the same platform, and both appear in feeds and inbound links. Accept either as the external identifier and normalise by stripping the query string, or the same job is stored twice under different keys.
- mediumA tenantless link cannot be attributed
- Apex-host job URLs carry no employer. Fetch the page and read the board links in the company logo and default link anchors, requiring exactly one distinct tenant. If two boards appear, leave the job unattributed rather than guessing.
- mediumDescriptions are truncated
- The RSS description is a teaser, not the posting body. Fetch the job page and read the JobPosting JSON-LD description; storing the feed text leaves every record with a few sentences and no requirements or benefits.
GetHired Jobs API.
GetHired powers hourly and small-business hiring on easyapply.co. Each employer board publishes a complete RSS feed, and every job page carries canonical JobPosting JSON-LD — no key required.
What's in every response.
Data fields, real-world applications, and the companies already running on GetHired.
Data fields
- Full Job Descriptions
- JobPosting JSON-LD
- Complete Per-Tenant Feed
- Location Categories
- Published Dates
- Direct Apply URLs
Use cases
- 01Hourly Job Aggregation
- 02Small Business Hiring Trackers
- 03Local Job Board Syndication
- 04Careers Page Monitoring
Trusted by
- Rosedale Green
- Langenstein Uptown
- Langenstein River Ridge
How to scrape GetHired.
Step-by-step guide to extracting jobs from GetHired-powered career pages—endpoints, authentication, and working code.
from urllib.parse import urlparse
RESERVED = {"api", "app", "easyapply", "support", "www"}
def parse_board(url: str) -> str | None:
parsed = urlparse(url)
if parsed.scheme != "https":
return None
labels = parsed.netloc.lower().split(".")
if len(labels) != 3 or labels[1] != "easyapply" or labels[2] != "co":
return None
tenant = labels[0]
if tenant in RESERVED or not 0 < len(tenant) <= 63:
return None
return tenant
def parse_job_id(url: str) -> str | None:
"""Jobs appear as /a/{uuid} or /job/{slug} on either host."""
segments = [s for s in urlparse(url).path.split("/") if s]
if len(segments) != 2 or segments[0] not in ("a", "job"):
return None
return segments[1]
print(parse_board("https://0758kentonhousinginc.easyapply.co"))import requests
from xml.etree import ElementTree
def fetch_feed(session, tenant: str) -> list[dict]:
feed_url = f"https://{tenant}.easyapply.co/rss"
resp = session.get(feed_url,
headers={"Accept": "application/rss+xml"}, timeout=30)
resp.raise_for_status()
root = ElementTree.fromstring(resp.content)
if root.tag.lower() != "rss":
raise RuntimeError("GetHired response was not an RSS document")
channel = root.find("channel")
if channel is None:
raise RuntimeError("GetHired feed contained no channel")
company = (channel.findtext("title") or "").strip()
rows, seen = [], set()
for item in channel.findall("item"):
link = (item.findtext("link") or item.findtext("guid") or "").strip()
job_id = parse_job_id(link)
if not job_id or job_id in seen:
continue
seen.add(job_id)
rows.append({
"id": job_id,
"title": (item.findtext("title") or "").strip(),
"summary": (item.findtext("description") or "").strip(),
"location": (item.findtext("category") or "").strip() or None,
"posted_at": item.findtext("pubDate"),
"company": company,
"url": link.split("?")[0],
})
return rows
session = requests.Session()
listings = fetch_feed(session, "0758kentonhousinginc")
print(f"{len(listings)} open jobs")import json
import time
from bs4 import BeautifulSoup
def find_job_posting(html: str) -> dict | None:
soup = BeautifulSoup(html, "html.parser")
for tag in soup.find_all("script", type="application/ld+json"):
try:
data = json.loads(tag.string or "")
except (json.JSONDecodeError, TypeError):
continue
for node in (data if isinstance(data, list) else [data]):
if isinstance(node, dict) and node.get("@type") == "JobPosting":
return node
return None
def fetch_detail(session, listing: dict, tenant: str) -> dict | None:
resp = session.get(
listing["url"],
headers={"Accept": "text/html",
"Referer": f"https://{tenant}.easyapply.co"},
timeout=30)
if resp.status_code in (404, 410):
return None # job removed
resp.raise_for_status()
posting = find_job_posting(resp.text)
if not posting:
return None
address = ((posting.get("jobLocation") or {}).get("address")) or {}
return {
"id": listing["id"],
"title": posting.get("title") or listing["title"],
"description_html": posting.get("description"),
"employment_type": posting.get("employmentType"),
"posted_at": posting.get("datePosted") or listing.get("posted_at"),
"closes_at": posting.get("validThrough"),
"company": (posting.get("hiringOrganization") or {}).get("name")
or listing.get("company"),
"city": address.get("addressLocality"),
"state": address.get("addressRegion"),
"url": listing["url"],
}
for listing in listings[:3]:
print(fetch_detail(session, listing, "0758kentonhousinginc"))
time.sleep(0.25)def resolve_tenant(session, job_url: str) -> str | None:
resp = session.get(job_url, headers={"Accept": "text/html"}, timeout=30)
if not resp.ok or not find_job_posting(resp.text):
return None
soup = BeautifulSoup(resp.text, "html.parser")
tenants = set()
for anchor in soup.select("a.vega-default-link[href], a.jobpage_company_logo[href]"):
tenant = parse_board(anchor["href"])
if tenant:
tenants.add(tenant)
# Ambiguous attribution is worse than none — require exactly one board.
return tenants.pop() if len(tenants) == 1 else None
print(resolve_tenant(
session, "https://easyapply.co/a/414e3fef-dd31-4ec6-b192-04175b7f1fa7"))- 1Use the tenant RSS feed as the inventory and the job page as the record
- 2Exclude www, app, api, support and easyapply when deriving a tenant
- 3Normalise both /a/{uuid} and /job/{slug} shapes to one external identifier
- 4Send the tenant board as the Referer on every job-page request
- 5Require exactly one board link before attributing an apex-host job
- 6Throttle to ~250ms between requests with at most three concurrent detail fetches
One endpoint. All GetHired jobs. No scraping, no sessions, no maintenance.
Get API accesscurl "https://connect.jobo.world/api/jobs?sources=gethired" \
-H "X-Api-Key: YOUR_KEY"Try the API.
Test Jobs and Feed endpoints against https://connect.jobo.world with live request/response examples, then copy ready-to-use curl commands.
Access GetHired
job data today.
One API call. Structured data. No scraping infrastructure to build or maintain — start with the $5 free starting balance and scale as you grow.