Comeet Jobs API.
Pull full HTML descriptions, structured locations, and department data from any Comeet-powered careers board through its token-bootstrapped Careers API — no per-job requests needed.
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 Comeet.
- Full HTML job descriptions
- Structured location data
- Department & team labels
- Employment & experience level
- Workplace type (remote/hybrid)
- Position UID & update timestamps
- 01Tech Job Board Aggregation
- 02Startup Hiring Trackers
- 03Talent Market Monitoring
- 04Remote Job Feeds
How to scrape Comeet.
Step-by-step guide to extracting jobs from Comeet-powered career pages—endpoints, authentication, and working code.
import requests
import re
import json
from urllib.parse import quote
# Canonical listings URL: /jobs/{tenant}/{board_code}
listings_url = "https://www.comeet.com/jobs/monday/41.00B"
response = requests.get(listings_url, timeout=30)
response.raise_for_status()
html = response.text
print(f"Fetched board page: {len(html)} bytes")match = re.search(r"COMPANY_DATA\s*=\s*(\{.*?\});", html, re.DOTALL)
if not match:
raise ValueError("Comeet bootstrap (COMPANY_DATA) not found — check the board URL")
company = json.loads(match.group(1))
company_uid = company.get("company_uid")
token = company.get("token")
company_name = company.get("name")
if not company_uid or not token:
raise ValueError("COMPANY_DATA did not expose company_uid/token")
print(f"Bootstrapped {company_name}: uid={company_uid}")api_url = (
f"https://www.comeet.co/careers-api/2.0/company/{quote(company_uid)}/positions"
f"?token={quote(token)}&details=true"
)
api_response = requests.get(
api_url,
headers={"Accept": "application/json", "Referer": listings_url},
timeout=30,
)
api_response.raise_for_status()
positions = api_response.json() # JSON array of position objects
print(f"Fetched {len(positions)} positions")def build_description(position):
# v2 exposes details[] at the top level; older payloads nest it under custom_fields.
details = position.get("details") or (position.get("custom_fields") or {}).get("details") or []
parts = [
d["value"]
for d in sorted(details, key=lambda d: d.get("order") or 0)
if d.get("value") # some entries ship a null value upstream
]
return "\n\n".join(parts)
jobs = []
for p in positions:
uid = p.get("uid")
if not uid:
continue # scraper rejects positions with no uid
loc = p.get("location") or {}
jobs.append({
"id": uid,
"title": (p.get("name") or "").strip(),
"department": p.get("department"),
"location": loc.get("name"),
"city": loc.get("city"),
"state": loc.get("state"),
"country": loc.get("country"),
"is_remote": loc.get("is_remote"),
"employment_type": p.get("employment_type"),
"experience_level": p.get("experience_level"),
"workplace_type": p.get("workplace_type"),
"listing_url": p.get("url_comeet_hosted_page") or p.get("url_active_page") or listings_url,
"apply_url": p.get("url_active_page") or p.get("url_comeet_hosted_page") or listings_url,
"description": build_description(p),
"updated_at": p.get("time_updated"),
})
for job in jobs:
print(f" - {job['title']} ({job['location']})")import time
def scrape_comeet(listings_url: str, delay: float = 0.2):
resp = requests.get(listings_url, timeout=30)
if resp.status_code == 404:
raise LookupError("Company not found on Comeet (HTTP 404)")
resp.raise_for_status()
m = re.search(r"COMPANY_DATA\s*=\s*(\{.*?\});", resp.text, re.DOTALL)
if not m:
raise ValueError("Comeet bootstrap did not expose company UID/token")
company = json.loads(m.group(1))
uid, token = company.get("company_uid"), company.get("token")
if not uid or not token:
raise ValueError("Missing company_uid or token in COMPANY_DATA")
time.sleep(delay) # backend paces requests ~200ms apart
api_url = (
f"https://www.comeet.co/careers-api/2.0/company/{quote(uid)}/positions"
f"?token={quote(token)}&details=true"
)
api = requests.get(
api_url,
headers={"Accept": "application/json", "Referer": listings_url},
timeout=30,
)
if api.status_code in (403, 429):
raise RuntimeError(f"Comeet blocked/rate-limited (HTTP {api.status_code}) — back off")
api.raise_for_status()
positions = api.json()
if not positions:
return [] # authoritative empty array — tenant may have paused hiring
seen, out = set(), []
for p in positions:
uid = p.get("uid")
if not uid or uid in seen:
continue
seen.add(uid)
out.append(p)
return out
# Scrape several boards
for url in [
"https://www.comeet.com/jobs/plus500_bulgaria/89.006",
"https://www.comeet.com/jobs/seekingalpha/46.006",
]:
try:
results = scrape_comeet(url)
print(f"{url}: {len(results)} positions")
except Exception as e:
print(f"{url}: {e}")The Careers API call needs both fields from the page's COMPANY_DATA object. Always fetch the canonical /jobs/{tenant}/{board_code} listings URL (strip any trailing job slug first) and abort with a clear error instead of calling the API blind when either field is missing.
An empty [] is authoritative, not a failure — treat zero positions as 'no open roles right now'. Return an empty result and retry later rather than failing the run or falling back to HTML scraping (this provider has no HTML fallback).
With details=true the Careers API v2 returns details[] at the top level, but older or embedded payloads nest the same array under custom_fields.details. Read the top-level array first and fall back to custom_fields.details so both shapes work.
Comeet returns 403 when a request is blocked and 429 when rate-limited. Keep at least ~200ms between requests, send Accept: application/json plus a Referer matching the board page, and back off exponentially on either status.
A minority of postings ship custom_fields.details entries whose value is null, yielding an empty description. Skip null/empty values when concatenating and don't assume every listing carries a description.
Some tenants leave the location object null on individual postings. Build location fields defensively with .get() and omit them when absent rather than raising a KeyError.
- 1Fetch the COMPANY_DATA token fresh each run — it is short-lived and tied to the board
- 2Request the positions endpoint with details=true so full descriptions arrive inline, avoiding per-job fetches
- 3Send Accept: application/json and a Referer matching the board page, as the backend does
- 4Order description fields by their `order` value before concatenating, and skip null values
- 5Deduplicate positions on uid and drop entries that have no uid
- 6Space requests ~200ms apart and back off on HTTP 403/429
One endpoint. All Comeet jobs. No scraping, no sessions, no maintenance.
Get API accesscurl "https://connect.jobo.world/api/jobs?sources=comeet" \
-H "X-Api-Key: YOUR_KEY" Access Comeet
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.