Polymer Jobs API.
Pull structured job listings — salary ranges, remote rules, and job categories — from Polymer's clean public JSON API, popular with YC-backed startups and tech scaleups.
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 Polymer.
- Structured Salary Ranges
- Remote & Workplace Type
- Job Category & Department
- Full HTML Descriptions
- Application Questions
- Employment Type & Location
- 01Startup Job Aggregation
- 02Tech Talent Sourcing
- 03Remote Job Monitoring
- 04Salary Benchmarking
How to scrape Polymer.
Step-by-step guide to extracting jobs from Polymer-powered career pages—endpoints, authentication, and working code.
import requests
# Custom domain (recommended - no Cloudflare)
org_slug = "violet-labs"
base_url = f"https://jobs.violetlabs.com"
# Main domain (Cloudflare-fronted): needs a TLS-fingerprinting client
# such as curl_cffi (browser impersonation) to clear the challenge
# base_url = "https://jobs.polymer.co"
listings_url = f"{base_url}/api/v1/public/organizations/{org_slug}/jobs"
print(f"Listings endpoint: {listings_url}")import requests
org_slug = "violet-labs"
base_url = "https://jobs.violetlabs.com"
url = f"{base_url}/api/v1/public/organizations/{org_slug}/jobs"
params = {"page": 1}
headers = {"Accept": "application/json"}
response = requests.get(url, params=params, headers=headers)
data = response.json()
jobs = data.get("items", [])
meta = data.get("meta", {})
# Note: meta.total is the PAGE count, meta.count is jobs on this page
print(f"Page {meta.get('page')} of {meta.get('total')} - {meta.get('count', 0)} jobs")
print(f"Is last page: {meta.get('is_last', True)}")for job in jobs:
print({
"id": job.get("id"),
"title": job.get("title"),
"location": job.get("display_location"),
"remote": job.get("remoteness_pretty"),
"employment_type": job.get("kind_pretty"),
"salary": job.get("salary_pretty"),
"category": job.get("job_category_name"),
"url": job.get("job_post_url"),
"published_at": job.get("published_at"),
})import requests
import time
base_url = "https://jobs.violetlabs.com"
org_slug = "violet-labs"
def get_job_details(job_id: int) -> dict:
url = f"{base_url}/api/v1/public/organizations/{org_slug}/jobs/{job_id}"
response = requests.get(url)
return response.json()
# Fetch details for each job
for job in jobs[:5]: # Limit for example
details = get_job_details(job["id"])
print(f"Title: {details.get('title')}")
print(f"Description length: {len(details.get('description', ''))}")
print(f"Questions: {len(details.get('questions', []))}")
time.sleep(0.5) # Be respectfulimport requests
def fetch_all_jobs(base_url: str, org_slug: str) -> list:
all_jobs = []
page = 1
while True:
url = f"{base_url}/api/v1/public/organizations/{org_slug}/jobs"
params = {"page": page}
response = requests.get(url, params=params)
data = response.json()
jobs = data.get("items", [])
meta = data.get("meta", {})
all_jobs.extend(jobs)
# Stop on the final page; also stop if next_page fails to advance
if meta.get("is_last", True) or not meta.get("next_page"):
break
page = meta["next_page"]
return all_jobs
jobs = fetch_all_jobs("https://jobs.violetlabs.com", "violet-labs")
print(f"Total jobs fetched: {len(jobs)}")The main host is fronted by Cloudflare, and its API returns the challenge HTML too. Use a TLS-fingerprinting client such as curl_cffi (browser impersonation) rather than plain requests, or scrape the company's custom domain (e.g. jobs.company.com), which is not protected.
The listings API returns metadata only. Call the per-job details endpoint (/jobs/{job_id}) for each ID to get the full HTML description and application questions.
Do not size loops off meta.total - it is the number of pages. meta.count is the number of jobs on the current page. Drive termination off meta.is_last and verify meta.next_page advances before requesting it.
Otherwise sub-second detail requests can transiently fail with a Cloudflare 403 or a dropped connection. Retry once after a short pause (~250ms) before treating it as a hard error; the same URL usually returns 200 on the retry.
Treat an empty page as the true end only when meta.is_last is true and meta.count is 0; an empty non-final page, or a response whose meta.page differs from the page you requested, signals an error rather than completion.
Extract the org-slug from the URL path and build the identical /api/v1/public/organizations/{slug}/jobs path regardless of host, so the same code handles jobs.polymer.co and custom domains.
- 1Prefer custom domains over jobs.polymer.co to skip Cloudflare handling entirely
- 2Fetch descriptions only for new or changed job IDs; the listings feed is cheap, detail calls are not
- 3Terminate pagination on meta.is_last, and confirm meta.next_page advances before requesting it
- 4Space detail requests ~200ms apart and cap concurrency around 5, matching the reference scraper
- 5Retry a single 403 or connection reset after a short pause before failing the request
- 6Handle missing salary_pretty and null city/state fields gracefully - not every job includes them
One endpoint. All Polymer jobs. No scraping, no sessions, no maintenance.
Get API accesscurl "https://connect.jobo.world/api/jobs?sources=polymer" \
-H "X-Api-Key: YOUR_KEY" Access Polymer
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.