All platforms

Rival (SilkRoad) Jobs API.

Pull live openings from SilkRoad-hosted career boards on jobs.silkroad.com, with titles, locations, categories, and posting dates parsed from every rendered job page.

Get API access
Rival (SilkRoad)
Live
20K+jobs indexed monthly
<3haverage discovery time
1hrefresh interval
Companies using Rival (SilkRoad)
NYU Langone HealthNumotionJMTCOLSA
Developer tools

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 Rival (SilkRoad).

Data fields
  • Full Job Descriptions
  • Structured Job Locations
  • Job Categories
  • Employment Type
  • Posting Dates
  • Direct Apply Links
Use cases
  1. 01Job Board Aggregation
  2. 02Career Site Monitoring
  3. 03Healthcare & Enterprise Hiring Data
  4. 04New-Posting Alerts
Trusted by
NYU Langone HealthNumotionJMTCOLSA
DIY GUIDE

How to scrape Rival (SilkRoad).

Step-by-step guide to extracting jobs from Rival (SilkRoad)-powered career pages—endpoints, authentication, and working code.

HTMLintermediateNo published limit; pace ~750ms between requests and keep detail fetches to 2 concurrentNo auth

Resolve the board URL for a tenant

A SilkRoad board lives at /{tenant}/{portal} on jobs.silkroad.com — both path segments matter. The portal is not always 'Careers', and some tenants use short two-letter codes, so always keep the full two-segment root.

Step 1: Resolve the board URL for a tenant
import requests

# Board root = /{tenant}/{portal}, e.g. Numotion/Careers or NYULangone/NYULHCareers.
BASE = "https://jobs.silkroad.com"
tenant, portal = "Numotion", "Careers"
board_url = f"{BASE}/{tenant}/{portal}"

resp = requests.get(board_url, timeout=15)
resp.raise_for_status()
print(board_url, resp.status_code)

Fetch a listings page and parse the job cards

Request the board with ?page=N and the embedded suffix, which returns the same job DOM with nav/footer chrome suppressed. Each card is an anchor with id Jobs_PagedJobList_Job-{id}, a .sr-panel__title, and an optional .sr-panel__location.

Step 2: Fetch a listings page and parse the job cards
from bs4 import BeautifulSoup

# "embedded" mode trims page chrome and keeps the same sr-panel selectors.
EMBEDDED = "embedded=true&modal="
BASE = "https://jobs.silkroad.com"

def fetch_page(board_url: str, page: int = 1) -> str:
    url = f"{board_url}?page={page}&{EMBEDDED}"
    r = requests.get(url, timeout=15)
    r.raise_for_status()
    return r.text

def parse_cards(html: str) -> list[dict]:
    soup = BeautifulSoup(html, "html.parser")
    jobs = []
    for a in soup.select('a[id^="Jobs_PagedJobList_Job-"]'):
        job_id = a["id"].removeprefix("Jobs_PagedJobList_Job-")
        title_el = a.select_one(".sr-panel__title")
        loc_el = a.select_one(".sr-panel__location")
        href = a.get("href", "")
        jobs.append({
            "id": job_id,
            "url": BASE + href if href.startswith("/") else href,
            "title": title_el.get_text(strip=True) if title_el else None,
            "location": loc_el.get_text(strip=True) if loc_el else None,
        })
    return jobs

listings = parse_cards(fetch_page(board_url, 1))
print(f"Found {len(listings)} jobs on page 1")

Paginate until the next-link disappears

SilkRoad exposes a Jobs_PagedJobList_NextLink control while more pages exist. Loop ?page=N until that marker is gone, then de-duplicate by numeric job id since a card can repeat across page boundaries.

Step 3: Paginate until the next-link disappears
def scrape_all(board_url: str) -> list[dict]:
    page, all_jobs = 1, []
    while True:
        html = fetch_page(board_url, page)
        all_jobs.extend(parse_cards(html))
        # Advance only while the "next" control is present.
        if 'id="Jobs_PagedJobList_NextLink"' not in html:
            break
        page += 1
    # Collapse duplicates by numeric job id.
    return list({j["id"]: j for j in all_jobs}.values())

jobs = scrape_all(board_url)
print(f"Total unique jobs: {len(jobs)}")

Fetch a job detail page and parse the fields

Append the same embedded suffix to the job URL. Title comes from #Jobs_JobDetail_TitleText, the body from section.sr-job-detail__description, the apply link from #Jobs_JobDetail_Multiform_ApplyLink, and template-driven fields (location, category, employment type, posting date) from ConfigurablePageDetail__* blocks.

Step 4: Fetch a job detail page and parse the fields
def scrape_detail(job_url: str) -> dict:
    sep = "&" if "?" in job_url else "?"
    r = requests.get(f"{job_url}{sep}{EMBEDDED}", timeout=15)
    if r.status_code == 404:
        return {"url": job_url, "removed": True}
    r.raise_for_status()
    soup = BeautifulSoup(r.text, "html.parser")

    def field(element_id: str):
        # <div id="ConfigurablePageDetail__X"><h2>Label</h2><div>Value</div></div>
        box = soup.find(id=element_id)
        val = box.find("div") if box else None
        return val.get_text(strip=True) if val else None

    title_el = soup.select_one("#Jobs_JobDetail_TitleText")
    desc_el = soup.select_one("section.sr-job-detail__description")
    apply_el = soup.select_one("#Jobs_JobDetail_Multiform_ApplyLink")
    return {
        "url": job_url,
        "title": title_el.get_text(strip=True) if title_el else None,
        "description": desc_el.get_text(" ", strip=True) if desc_el else None,
        "apply_url": apply_el.get("href") if apply_el else job_url,
        "location": field("ConfigurablePageDetail__DisplayLocation"),
        "category": field("ConfigurablePageDetail__DisplayCategory"),
        "employment_type": field("ConfigurablePageDetail__DisplayPosition"),
        "posted_date": field("ConfigurablePageDetail__PostingDate"),
    }

print(scrape_detail(jobs[0]["url"]))

Detect tenants that migrated off SilkRoad

A live SilkRoad board always emits Jobs_PagedJobList_ or sr-panel markers. If page 1 has neither and no next-link, the tenant has likely moved to another ATS — skip it rather than treating an empty parse as an error.

Step 5: Detect tenants that migrated off SilkRoad
def is_silkroad_board(html: str) -> bool:
    return "Jobs_PagedJobList_" in html or "sr-panel" in html

first = fetch_page(board_url, 1)
if not is_silkroad_board(first):
    print("Tenant no longer served by SilkRoad — skip")
else:
    print("Live SilkRoad board")
Common issues
highTenant migrated off SilkRoad but the URL still resolves

The page loads yet contains no Jobs_PagedJobList_ or sr-panel markers, so parsing yields zero jobs. Check for those markers on page 1; if they are absent and there is no next-link, treat the tenant as migrated and skip it.

mediumBoard root needs both tenant and portal segments

The listings root is /{tenant}/{portal} — a single segment is invalid, and the portal is not always 'Careers' (e.g. NYULangone/NYULHCareers, or two-letter tenants like RI/CBS). Always resolve the full two-segment path before requesting.

medium403 / 429 responses when crawling aggressively

SilkRoad blocks or throttles fast crawlers. Pace requests around 750ms, keep concurrent detail fetches at 2 or fewer, and back off on 403/429 before retrying.

lowMissing category, employment type, or posting date

ConfigurablePageDetail__* fields are template-driven and optional — some tenants omit PostingDate, DisplayCategory, or DisplayPosition. Default every configurable field to None instead of assuming it exists.

mediumEmpty or missing job description on the detail page

When section.sr-job-detail__description is absent the body cannot be parsed. Fall back to the listing title, and flag or skip jobs whose detail body comes back empty.

Best practices
  1. 1Always resolve the full two-segment board root (/{tenant}/{portal}) — the portal is not always 'Careers'.
  2. 2Append embedded=true&modal= to listing and detail requests for smaller, chrome-free HTML.
  3. 3Paginate with ?page=N and stop when the Jobs_PagedJobList_NextLink control disappears.
  4. 4Pace requests around 750ms and keep detail concurrency at 2 or fewer to avoid 403/429 blocks.
  5. 5Treat every ConfigurablePageDetail__* field (location, category, employment type, posting date) as optional.
  6. 6De-duplicate listings by numeric job id before fetching detail pages.
Or skip the complexity

One endpoint. All Rival (SilkRoad) jobs. No scraping, no sessions, no maintenance.

Get API access
cURL
curl "https://connect.jobo.world/api/jobs?sources=rival (silkroad)" \
  -H "X-Api-Key: YOUR_KEY"
Ready to integrate

Access Rival (SilkRoad)
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.

99.9%API uptime
<200msAvg response
50M+Jobs processed