HiringThing Jobs API.
Pull an entire company board in one request: every HiringThing subdomain publishes an RSS feed carrying full HTML descriptions, locations, and posted dates.
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.
What's in every response.
Data fields, real-world applications, and the companies already running on HiringThing.
- Full HTML Descriptions
- Structured Pay Ranges
- City & State Locations
- Posted Dates
- Remote vs On-Site Flags
- Job Category Tags
- 01SMB Job Aggregation
- 02Career Page Monitoring
- 03Location-Based Filtering
- 04Salary Benchmarking
How to scrape HiringThing.
Step-by-step guide to extracting jobs from HiringThing-powered career pages—endpoints, authentication, and working code.
import requests
import xml.etree.ElementTree as ET
company = "demco"
rss_url = f"https://{company}.hiringthing.com/api/rss.xml"
response = requests.get(rss_url)
response.raise_for_status()
root = ET.fromstring(response.content)
channel = root.find("channel")
items = channel.findall("item")
print(f"Found {len(items)} jobs in RSS feed")import xml.etree.ElementTree as ET
def parse_rss_jobs(rss_content):
root = ET.fromstring(rss_content)
channel = root.find("channel")
jobs = []
for item in channel.findall("item"):
title_elem = item.find("title")
link_elem = item.find("link")
location_elem = item.find("location")
description_elem = item.find(".//{http://search.yahoo.com/mrss/}description")
jobs.append({
"title": title_elem.text if title_elem is not None else None,
"url": link_elem.text if link_elem is not None else None,
"location": location_elem.text if location_elem is not None else None,
"description_html": description_elem.text if description_elem is not None else None,
})
return jobs
jobs = parse_rss_jobs(response.content)
for job in jobs[:3]:
print(f"{job['title']} - {job['location']}")import requests
import json
import re
from bs4 import BeautifulSoup
def get_job_details(company: str, job_url: str) -> dict:
full_url = f"https://{company}.hiringthing.com{job_url}" if job_url.startswith("/") else job_url
response = requests.get(full_url)
soup = BeautifulSoup(response.text, "html.parser")
# Find the ApplyButtonGroup component with embedded JSON
apply_div = soup.find("div", {"data-react-class": "HiringThing.Components.ApplyButtonGroup"})
if not apply_div:
return {}
props_json = apply_div.get("data-react-props", "{}")
data = json.loads(props_json)
job_data = data.get("jobObj", {}).get("table", {})
return {
"id": job_data.get("id"),
"title": job_data.get("title"),
"location": job_data.get("location"),
"location_info": job_data.get("location_info"),
"description_html": job_data.get("html_description"),
"posted_at": job_data.get("posted_at"),
"company_id": job_data.get("company_id"),
}
details = get_job_details("demco", "/job/984299/learning-environment-field-consultant")
print(details)import requests
import json
from bs4 import BeautifulSoup
def extract_salary_data(html_content: str) -> list:
soup = BeautifulSoup(html_content, "html.parser")
salaries = []
salary_divs = soup.find_all("div", {"data-react-class": "HiringThing.Components.JobSalary"})
for div in salary_divs:
props = div.get("data-react-props", "{}")
data = json.loads(props)
salaries.append({
"min": data.get("minSalary", {}).get("amount"),
"max": data.get("maxSalary", {}).get("amount"),
"currency": data.get("minSalary", {}).get("currency"),
"frequency": data.get("payFrequency"),
})
return salaries
# Fetch listings page
response = requests.get("https://demco.hiringthing.com/")
salaries = extract_salary_data(response.text)
for s in salaries[:3]:
print(f"${s['min']} - ${s['max']} {s['frequency']}")import requests
import time
import xml.etree.ElementTree as ET
def scrape_hiringthing_company(company: str, delay: float = 0.5) -> dict:
rss_url = f"https://{company}.hiringthing.com/api/rss.xml"
try:
response = requests.get(rss_url, timeout=30)
if response.status_code == 404:
return {"error": f"Company '{company}' not found", "jobs": []}
response.raise_for_status()
root = ET.fromstring(response.content)
channel = root.find("channel")
items = channel.findall("item")
jobs = []
for item in items:
jobs.append({
"title": item.find("title").text,
"url": item.find("link").text,
"location": getattr(item.find("location"), "text", None),
})
return {"company": company, "jobs": jobs, "count": len(jobs)}
except requests.RequestException as e:
return {"error": str(e), "jobs": []}
# Scrape multiple companies with rate limiting
companies = ["demco", "example1", "example2"]
for company in companies:
result = scrape_hiringthing_company(company)
print(f"{company}: {result.get('count', 0)} jobs")
time.sleep(delay)A 404 means the board or subdomain does not exist. Confirm the company slug from the careers link, and fall back to HTML scraping of the listings page (div.job-container) if the feed is disabled.
The data-react-props attribute escapes markup (e.g. \u003c for <). Python's json.loads() unescapes this automatically, so parse before inspecting the raw string.
That endpoint is an authenticated admin API gated by a signed _ats_session cookie, not a public source. Use the RSS feed or embedded JSON instead — do not try to forge the session.
The feed omits compensation. Fetch the HTML listings page and parse the data-react-props on JobSalary components for min, max, currency, and pay frequency.
- 1Use the RSS feed for the fastest pull — one request returns all jobs with full descriptions
- 2Read data-react-props attributes for structured salary, location, and posted-date metadata
- 3Add a 0.5-1 second delay between company boards when scraping in bulk
- 4Cache results by company ID (present in the embedded JSON and sitemaps) to dedupe across refreshes
- 5Fall back to HTML scraping of div.job-container when a feed is unavailable
- 6Discover all boards through the global S3 sitemap index (gzipped per-company sitemaps)
One endpoint. All HiringThing jobs. No scraping, no sessions, no maintenance.
Get API accesscurl "https://connect.jobo.world/api/jobs?sources=hiringthing" \
-H "X-Api-Key: YOUR_KEY" Access HiringThing
job data today.
One API call. Structured data. No scraping infrastructure to build or maintain — start with the free tier and scale as you grow.