- highThe job ID is rejected as malformed
- AppOne job IDs are 24-character lowercase hex strings, not integers or slugs. Validate against that shape before calling the posting endpoint; anything else is a marketing or apply-flow URL and will return an error page rather than JSON.
- mediumThere is no total count or pagination to verify completeness
- The portal endpoint returns the entire inventory in one response and publishes no authoritative total. Use the length of jobPosts as the count, and treat a sudden drop between runs as a signal to re-fetch rather than as immediate evidence that jobs closed.
- mediumA bare apply.appone.com link cannot be attributed to an employer
- Job URLs carry no portal slug. Call /api/apply/v2/jobposting/{id} and read jobPortalUrl, which names the owning jobs.appone.com portal, before writing the record. Without that step every syndicated link lands in an unattributed bucket.
- lowSalary fields are missing on most rows
- The salary object is optional and frequently absent or partially filled, with only salaryOption set. Read minimum, maximum, salaryOption and periodType defensively and store nulls rather than coercing a missing band to zero.
AppOne RSS Jobs API.
AppOne serves each employer a portal at jobs.appone.com/{slug}. One anonymous JSON call returns the whole portal inventory, and a second returns the full description and salary band for any job. Portals that publish RSS instead are served separately.
What's in every response.
Data fields, real-world applications, and the companies already running on AppOne RSS.
Data fields
- Full Job Descriptions
- Salary Minimum & Maximum
- Employment & Workplace Type
- Location Strings
- Posted Dates
- Portal-to-Job Backlinks
Use cases
- 01SMB Job Aggregation
- 02Staffing Agency Feeds
- 03Compensation Benchmarking
- 04ATS Data Pipelines
How to scrape AppOne RSS.
Step-by-step guide to extracting jobs from AppOne RSS-powered career pages—endpoints, authentication, and working code.
import re
from urllib.parse import urlparse
PORTAL_HOST = "jobs.appone.com"
APPLY_HOST = "apply.appone.com"
MONGO_ID = re.compile("^[a-f0-9]{24}$", re.IGNORECASE)
def parse_portal(url: str) -> str | None:
parsed = urlparse(url)
if parsed.scheme != "https" or parsed.netloc.lower() != PORTAL_HOST:
return None
segments = [s for s in parsed.path.split("/") if s]
return segments[0].lower() if len(segments) == 1 else None
def parse_job_id(url: str) -> str | None:
parsed = urlparse(url)
if parsed.scheme != "https" or parsed.netloc.lower() != APPLY_HOST:
return None
segments = [s for s in parsed.path.split("/") if s]
if len(segments) != 2 or segments[0] != "job" or not MONGO_ID.match(segments[1]):
return None
return segments[1].lower()
print(parse_portal("https://jobs.appone.com/aacnnursing")) # 'aacnnursing'import requests
from urllib.parse import quote
def fetch_portal(session, slug: str) -> dict:
url = f"https://{PORTAL_HOST}/api/portal/v1/companyjobposts/{quote(slug)}"
resp = session.get(url, headers={"Accept": "application/json"}, timeout=30)
resp.raise_for_status()
return resp.json()
session = requests.Session()
portal = fetch_portal(session, "aacnnursing")
print(portal.get("companyName"), len(portal.get("jobPosts") or []))def map_rows(portal: dict) -> list[dict]:
company_name = (portal.get("companyName") or "").strip()
rows = []
for job in portal.get("jobPosts") or []:
url = (job.get("jobPostUrl") or "").strip()
job_id = parse_job_id(url)
if not job_id or job_id != (job.get("jobPostId") or "").lower():
continue # URL and ID disagree — skip the row
salary = job.get("salary") or {}
rows.append({
"id": job_id,
"title": (job.get("jobTitle") or "").strip(),
"company": company_name,
"url": url,
"location": job.get("location"),
"employment_type": job.get("jobType"),
"workplace_type": job.get("workplaceType"),
"posted_at": job.get("datePosted"),
"salary_min": salary.get("minimum"),
"salary_max": salary.get("maximum"),
"salary_option": salary.get("salaryOption"),
"salary_period": salary.get("periodType"),
})
return rows
rows = map_rows(portal)import time
def fetch_job(session, job_id: str) -> dict | None:
url = f"https://{APPLY_HOST}/api/apply/v2/jobposting/{job_id}"
resp = session.get(url, headers={"Accept": "application/json"}, timeout=30)
if resp.status_code in (404, 410):
return None # posting removed
resp.raise_for_status()
job = resp.json() or {}
if (job.get("jobPostId") or "").lower() != job_id:
raise RuntimeError("AppOne returned a different job than requested")
salary = job.get("salary") or {}
return {
"id": job_id,
"title": (job.get("jobTitle") or "").strip(),
"description_html": (job.get("description") or "").strip(),
"company": job.get("companyName"),
"client_id": job.get("clientId"),
"location": job.get("location"),
"employment_type": job.get("jobType"),
"workplace_type": job.get("workplaceType"),
"portal_url": job.get("jobPortalUrl"),
"salary_min": salary.get("minimum"),
"salary_max": salary.get("maximum"),
"url": f"https://{APPLY_HOST}/job/{job_id}",
}
for row in rows[:3]:
print(fetch_job(session, row["id"]))
time.sleep(0.5)def resolve_portal_from_job(session, job_url: str) -> str | None:
job_id = parse_job_id(job_url)
if not job_id:
return None
job = fetch_job(session, job_id)
if not job or not job.get("portal_url"):
return None
return parse_portal(job["portal_url"])
print(resolve_portal_from_job(
session, "https://apply.appone.com/job/000000000000000000000000"))- 1Validate job IDs against the 24-character hex shape before calling the posting API
- 2Cross-check each row's jobPostId against the ID inside its jobPostUrl
- 3Use jobPortalUrl to attribute an orphan apply.appone.com link to its employer
- 4Confirm the response URL was not redirected off the canonical API path
- 5Throttle to ~500ms between requests and cap concurrent detail fetches at two
- 6Store the salary object's four fields separately instead of flattening to one string
One endpoint. All AppOne RSS jobs. No scraping, no sessions, no maintenance.
Get API accesscurl "https://connect.jobo.world/api/jobs?sources=appone rss" \
-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 AppOne RSS
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.