All platforms

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.

Get API access
Polymer
Live
50K+jobs indexed monthly
<3haverage discovery time
1hrefresh interval
Companies using Polymer
Violet LabsGetaround
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 Polymer.

Data fields
  • Structured Salary Ranges
  • Remote & Workplace Type
  • Job Category & Department
  • Full HTML Descriptions
  • Application Questions
  • Employment Type & Location
Use cases
  1. 01Startup Job Aggregation
  2. 02Tech Talent Sourcing
  3. 03Remote Job Monitoring
  4. 04Salary Benchmarking
Trusted by
Violet LabsGetaround
DIY GUIDE

How to scrape Polymer.

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

RESTintermediateNo published limits; ~200ms between requests, ~5 concurrent detail callsNo auth

Locate the jobs API endpoint

Polymer serves the same JSON API on two host types: the main jobs.polymer.co (Cloudflare-fronted) and per-company custom domains like jobs.company.com (direct access). Prefer a custom domain when available to skip Cloudflare handling entirely.

Step 1: Locate the jobs API endpoint
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}")

Fetch the job listings

Call the listings API to retrieve all job metadata. This endpoint returns title, location, salary, and category fields, but does NOT include the job description - that requires a separate call per job.

Step 2: Fetch the job listings
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)}")

Parse the listing metadata

Extract the fields available on each listing item. Useful ones include salary_pretty, remoteness_pretty, kind_pretty, and job_category_name.

Step 3: Parse the listing metadata
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"),
    })

Fetch full job descriptions

Because the listings API omits descriptions, call the per-job details endpoint for each ID to get the full HTML description and application questions.

Step 4: Fetch full job descriptions
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 respectful

Paginate through every page

Terminate on meta.is_last rather than guessing a page count (meta.total is the page count, not the job count). Increment page until the API reports the final page.

Step 5: Paginate through every page
import 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)}")
Common issues
criticalCloudflare challenge on jobs.polymer.co

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.

highListings response omits job descriptions

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.

mediummeta.total is the page count, not the job count

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.

mediumIntermittent 403s and connection resets on detail calls

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.

mediumEmpty or out-of-order pages

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.

lowCustom domain vs main-host URL construction

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.

Best practices
  1. 1Prefer custom domains over jobs.polymer.co to skip Cloudflare handling entirely
  2. 2Fetch descriptions only for new or changed job IDs; the listings feed is cheap, detail calls are not
  3. 3Terminate pagination on meta.is_last, and confirm meta.next_page advances before requesting it
  4. 4Space detail requests ~200ms apart and cap concurrency around 5, matching the reference scraper
  5. 5Retry a single 403 or connection reset after a short pause before failing the request
  6. 6Handle missing salary_pretty and null city/state fields gracefully - not every job includes them
Or skip the complexity

One endpoint. All Polymer jobs. No scraping, no sessions, no maintenance.

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

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.

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