Skip to main content
Jobo

Jobo Enterprise — Python Client

Access millions of job listings, enriched company profiles, and geocoding — all from a single API.

PyPI Python License: MIT


Features

Sub-client Property Description
Jobs Feed client.feed Bulk and managed job feeds with cursor-based pagination (106 ATS)
Jobs Search client.search Full-text search, filters, facets, and single-job lookup
Companies client.companies Enriched company profiles and per-company job listings
Locations client.locations Geocode location strings into structured coordinates

Both sync (JoboClient) and async (AsyncJoboClient) are included.

Get your API key → enterprise.jobo.world/api-keys


Installation

pip install jobo-enterprise

Quick Start

from jobo_enterprise import JoboClient

with JoboClient(api_key="your-api-key") as client:
    # Search for jobs
    results = client.search.search(q="software engineer", location="San Francisco")
    for job in results.jobs:
        print(f"{job.title} at {job.company.name}")

    # Geocode a location
    geo = client.locations.geocode("London, UK")
    print(f"{geo.locations[0].display_name}: {geo.locations[0].latitude}, {geo.locations[0].longitude}")

Authentication

client = JoboClient(api_key="your-api-key")

Jobs Feed — client.feed

Bulk-sync millions of active jobs using cursor-based pagination.

Fetch a batch

from jobo_enterprise import LocationFilter

response = client.feed.get_jobs(
    locations=[
        LocationFilter(country="US", region="California"),
        LocationFilter(country="US", city="New York"),
    ],
    sources=["greenhouse", "workday"],
    work_models=["remote", "hybrid"],
    batch_size=1000,
)

print(f"Got {len(response.jobs)} jobs, has_more={response.has_more}")

Auto-paginate all jobs

for job in client.feed.iter_jobs(batch_size=1000, sources=["greenhouse"]):
    save_to_database(job)

Incremental sync

After the initial backfill, pass updated_after to pick up only what changed. Scans page by immutable creation time by default (stable_scan), so records cannot shift across page boundaries while you are reading.

from datetime import datetime, timedelta, timezone

since = datetime.now(timezone.utc) - timedelta(hours=1)

for job in client.feed.iter_jobs(updated_after=since, batch_size=1000):
    upsert(job)

Managed feed

Jobs from the companies you configured through Managed Job Scraping in the Jobo portal. Same batch and cursor semantics, minus the locations filter.

for job in client.feed.iter_managed_jobs(batch_size=1000):
    save_to_database(job)

Expired job IDs

expired_since is optional and defaults to 24 hours ago. Maximum lookback is 7 days.

for job_id in client.feed.iter_expired_job_ids():
    mark_as_expired(job_id)

Jobs Search — client.search

Full-text search with filters and page-based pagination.

Simple search

from jobo_enterprise import WorkModel

results = client.search.search(
    q="data scientist",
    location="New York",
    sources="greenhouse,lever",
    work_model=WorkModel.REMOTE,  # or just "remote"
    min_salary_usd=120000,
    page_size=50,
)

print(f"Found {results.total} jobs across {results.total_pages} pages")

Closed value sets. Parameters with a fixed set of accepted values ship as enums for discoverability — WorkModel, EmploymentType, ExperienceLevel, CompensationPeriod, and SkillType. Each member subclasses str, so passing the equivalent literal (e.g. "remote") is always valid too. Values are lowercase and hyphenated ("full-time", "per-diem"); the API matches them exactly, so a misspelt value simply matches nothing.

Fetch one job

job = client.search.get_job("a1b2c3d4-e5f6-7890-abcd-ef1234567890")

Unmetered — this endpoint deducts no credits, which makes it a cheap way to wire up an integration.

Trim the payload

Omit include_fields for the whole job, pass a subset to keep only those heavy fields, or pass an empty value for core fields only.

results = client.search.search(q="data scientist", include_fields="summary", page_size=50)

Advanced search (typed filters & facets)

from jobo_enterprise import InclusionExclusionFilter, RangeFilter

results = client.search.search_advanced(
    queries=["machine learning engineer", "ML engineer", "AI engineer"],
    locations=["San Francisco", "New York"],
    sources=["greenhouse", "lever", "ashby"],
    work_models=["remote", "hybrid"],
    skills=InclusionExclusionFilter(include=["python"], exclude=["php"]),
    salary_usd=RangeFilter(min=150000),
    include_facets=["work_model", "experience_level"],
    page_size=100,
)

for facet, buckets in results.facets.items():
    print(facet, [(b.key, b.count) for b in buckets])

Auto-paginate all results

for job in client.search.iter_jobs(
    queries=["backend engineer"],
    locations=["London"],
    page_size=100,
):
    print(f"{job.title} — {job.company.name}")

Companies — client.companies

Fetch fully enriched company profiles and list jobs scoped to a company.

company = client.companies.get(job.company.id)
print(company.name, company.website, company.industries)

# Jobs for a single company (paginated)
jobs = client.companies.get_jobs(job.company.id, page_size=50)
print(f"{jobs.total} jobs at {company.name}")

Locations — client.locations

Geocode location strings into structured data with coordinates.

result = client.locations.geocode("San Francisco, CA")

for location in result.locations:
    print(f"{location.display_name}: {location.latitude}, {location.longitude}")

Auto Apply

Not covered by this client. The Auto Apply contract is profileless and callback-driven, and application creation is not yet open to traffic. Call it over plain HTTPS — see the Auto Apply reference.


Async Support

Every sub-client has an async equivalent via AsyncJoboClient:

import asyncio
from jobo_enterprise import AsyncJoboClient

async def main():
    async with AsyncJoboClient(api_key="your-api-key") as client:
        # Search
        results = await client.search.search(q="frontend developer")

        # Auto-paginated feed
        async for job in client.feed.iter_jobs(batch_size=500):
            await process_job(job)

        # Geocode
        geo = await client.locations.geocode("Berlin, DE")

asyncio.run(main())

Error Handling

429 and 503 are retried for you with bounded backoff, honouring Retry-After. Everything else raises immediately, as a subclass of JoboError:

from jobo_enterprise import (
    JoboAuthenticationError,
    JoboPermissionError,
    JoboNotFoundError,
    JoboRateLimitError,
    JoboValidationError,
    JoboCursorRestartRequiredError,
    JoboServerError,
    JoboError,
)

try:
    results = client.search.search(q="engineer")
except JoboAuthenticationError:
    print("Invalid API key")
except JoboPermissionError:
    print("Key is not entitled to this resource")
except JoboNotFoundError:
    print("No such job or company")
except JoboRateLimitError as e:
    print(f"Rate limited. Retry after {e.retry_after}s")
except JoboValidationError as e:
    print(f"Bad request: {e.detail} ({e.code})")
except JoboCursorRestartRequiredError:
    print("Feed cursor is spent — discard it and start a new scan")
except JoboServerError:
    print("Server error — try again later")

Every exception carries the API's machine-readable problem code when one is supplied, alongside status_code, detail, and the raw response_body.

Supported ATS Sources (106)

Category Sources
Enterprise ATS workday, smartrecruiters, icims, successfactors, oraclecloud, taleo, dayforce, csod, adp, ultipro, paycom
Tech & Startup greenhouse, lever_co, ashby, workable, workable_jobs, rippling, polymer, gem, pinpoint, homerun
Mid-Market bamboohr, breezy, jazzhr, recruitee, personio, jobvite, teamtailor, comeet, trakstar, zoho
SMB & Niche gohire, recooty, applicantpro, hiringthing, careerplug, hirehive, kula, careerpuck, talnet, jobscore
Specialized freshteam, isolved, joincom, eightfold, phenompeople

The full catalogue of 106 providers is listed in the API documentation. Treat it as an open set — new provider_id values appear as platforms are added.

Configuration

Parameter Default Description
api_key required Your API key
base_url https://connect.jobo.world API base URL
timeout 30.0 Request timeout (seconds)
feed_timeout 120.0 Response timeout for the feed routes
httpx_client None Custom httpx client

Use Cases

  • Build a job board — Search and display jobs from 106 ATS platforms
  • Job aggregator — Bulk-sync millions of listings with the feed endpoint
  • ATS data pipeline — Pull jobs from Greenhouse, Lever, Workday, etc. into your data warehouse
  • Recruitment tools — Power candidate-facing job search experiences
  • Company intelligence — Enrich listings with funding, headcount, and tech-stack data
  • Location intelligence — Geocode and normalize job locations

Links

License

MIT — see LICENSE.

Metadata

Release files for jobo-enterprise 4.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for jobo-enterprise 4.0.0
File Size Uploaded
jobo_enterprise-4.0.0.tar.gz 28.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for jobo-enterprise 4.0.0
File Interpreter ABI Platform
jobo_enterprise-4.0.0-py3-none-any.whl Python 3 none any Details

Total release size: 52.8 kB

Release files / jobo_enterprise-4.0.0.tar.gz

Download URL jobo_enterprise-4.0.0.tar.gz
Size 28.0 kB
Tags Source
SHA-256 checksum
How to use checksums
e04939e7ba19544ad2f24e8abdca9ebaa5248579edcc3e1df3dcdc7253126666
BLAKE2b-256 checksum
How to use checksums
5ce4af58e0d6500cc1ac78b7389c1cb9e461f604c7cd0b6169b6442241d7e6f7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.13.14

Release files / jobo_enterprise-4.0.0-py3-none-any.whl

Download URL jobo_enterprise-4.0.0-py3-none-any.whl
Size 24.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
6ec69050f689c21cbbdec13c6a87a84c31d5670fb3855651e1864551811d4a7b
BLAKE2b-256 checksum
How to use checksums
69e39e497efced8eb7a809dc4a8a91d4e3ea82319c31939555fe474ed9c77428
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.13.14

Release history Release notifications | RSS feed

This release

4.0.0 This release

2 release files

3.0.0

2 release files

1.1.1

2 release files

1.1

2 release files

1.0.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page