Metadata-Version: 2.4
Name: jobo-enterprise
Version: 3.0.0
Summary: Official Python client for the Jobo Enterprise API. Access millions of job listings from 45+ ATS platforms, geocode locations, and automate job applications. Organized by feature area: Feed, Search, Locations, and Auto Apply.
Author-email: Jobo <support@jobo.world>
License: MIT
Project-URL: Homepage, https://jobo.world/enterprise/
Project-URL: Documentation, https://github.com/Prakkie91/jobo-python#readme
Project-URL: Repository, https://github.com/Prakkie91/jobo-python
Project-URL: Issues, https://github.com/Prakkie91/jobo-python/issues
Keywords: jobo,jobs,api,enterprise,job-board,recruitment,ats,greenhouse,lever,workday,smartrecruiters,ashby,bamboohr,icims,job-scraper,job-listings,job-data,job-aggregator,job-feed,applicant-tracking-system,hiring,careers-page,job-search-api,bulk-jobs
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Typing :: Typed
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: httpx<1.0.0,>=0.25.0
Requires-Dist: pydantic<3.0.0,>=2.0.0
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.21; extra == "dev"
Requires-Dist: pytest-httpx>=0.30; extra == "dev"
Requires-Dist: ruff>=0.4; extra == "dev"
Requires-Dist: mypy>=1.10; extra == "dev"
Dynamic: license-file

<img src="https://raw.githubusercontent.com/Prakkie91/jobo-python/main/jobo-logo.png" alt="Jobo" width="120" />

# Jobo Enterprise — Python Client

**Access millions of job listings, geocode locations, and automate job applications — all from a single API.**

[![PyPI](https://img.shields.io/pypi/v/jobo-enterprise)](https://pypi.org/project/jobo-enterprise/)
[![Python](https://img.shields.io/pypi/pyversions/jobo-enterprise)](https://pypi.org/project/jobo-enterprise/)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)

---

## Features

| Sub-client          | Property             | Description                                              |
| ------------------- | -------------------- | -------------------------------------------------------- |
| **Jobs Feed**       | `client.feed`        | Bulk job feed with cursor-based pagination (45+ ATS)     |
| **Jobs Search**     | `client.search`      | Full-text search with location, remote, and source filters |
| **Locations**       | `client.locations`   | Geocode location strings into structured coordinates     |
| **Auto Apply**      | `client.auto_apply`  | Automate job applications with form field discovery      |

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

> **Get your API key** → [enterprise.jobo.world/api-keys](https://enterprise.jobo.world/api-keys)

---

## Installation

```bash
pip install jobo-enterprise
```

## Quick Start

```python
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

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

---

## Jobs Feed — `client.feed`

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

### Fetch a batch

```python
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

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

### Expired job IDs

```python
from datetime import datetime, timedelta

expired_since = datetime.utcnow() - timedelta(days=1)

for job_id in client.feed.iter_expired_job_ids(expired_since=expired_since):
    mark_as_expired(job_id)
```

---

## Jobs Search — `client.search`

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

### Simple search

```python
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.

### Advanced search (typed filters & facets)

```python
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

```python
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.

```python
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.

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

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

---

## Auto Apply — `client.auto_apply`

Automate job applications with form field discovery and filling.

```python
from jobo_enterprise import FieldAnswer

# Start a session
session = client.auto_apply.start_session(job.apply_url)

print(f"Provider: {session.provider_display_name}")
print(f"Fields: {len(session.fields)}")

# Fill in fields — `type` mirrors the FormFieldInfo.type of each field
answers = [
    FieldAnswer(field_id="first_name", type="text", value="John"),
    FieldAnswer(field_id="last_name", type="text", value="Doe"),
    FieldAnswer(field_id="email", type="text", value="john@example.com"),
]

result = client.auto_apply.set_answers(session.session_id, answers)

if result.is_terminal:
    print("Application submitted!")

# Clean up
client.auto_apply.end_session(session.session_id)
```

### Profiles & one-shot run

```python
from jobo_enterprise import AutoApplyProfileRequest

profile = client.auto_apply.create_profile(
    AutoApplyProfileRequest(
        name="Default",
        first_name="John",
        last_name="Doe",
        email="john@example.com",
        phone="+1-555-0100",
    )
)

# Run the full flow end-to-end against the stored profile
run = client.auto_apply.run(profile.id, job.apply_url)
print(run.status, run.steps_completed, run.fields_filled)
```

---

## Async Support

Every sub-client has an async equivalent via `AsyncJoboClient`:

```python
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

```python
from jobo_enterprise import (
    JoboAuthenticationError,
    JoboRateLimitError,
    JoboValidationError,
    JoboServerError,
    JoboError,
)

try:
    results = client.search.search(q="engineer")
except JoboAuthenticationError:
    print("Invalid API key")
except JoboRateLimitError as e:
    print(f"Rate limited. Retry after {e.retry_after}s")
except JoboValidationError as e:
    print(f"Bad request: {e.detail}")
except JoboServerError:
    print("Server error — try again later")
```

## Supported ATS Sources (45+)

| 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`                                                                                |

## 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)    |
| `httpx_client` | `None`                        | Custom httpx client          |

## Use Cases

- **Build a job board** — Search and display jobs from 45+ 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
- **Auto-apply automation** — Automate job applications at scale
- **Location intelligence** — Geocode and normalize job locations

## Links

- **Website** — [jobo.world/enterprise](https://jobo.world/enterprise/)
- **Get API Key** — [enterprise.jobo.world/api-keys](https://enterprise.jobo.world/api-keys)
- **GitHub** — [github.com/Prakkie91/jobo-python](https://github.com/Prakkie91/jobo-python)
- **PyPI** — [pypi.org/project/jobo-enterprise](https://pypi.org/project/jobo-enterprise/)

## License

MIT — see [LICENSE](LICENSE).
