> ## Documentation Index
> Fetch the complete documentation index at: https://jobo.world/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Client libraries

> Official Python, Node.js, and .NET clients for the jobs API, plus the dedicated TypeScript client for Auto Apply.

Three official clients are published for the jobs API. As of **4.0.0** they sit on the same version and cover the same surface, so pick whichever matches your stack. [Auto Apply has its own TypeScript client](#auto-apply-typescript), described below.

|                  | Python                                                  | Node.js / TypeScript                                 | .NET                                                           |
| ---------------- | ------------------------------------------------------- | ---------------------------------------------------- | -------------------------------------------------------------- |
| Package          | `jobo-enterprise`                                       | `jobo-enterprise`                                    | `Jobo.Enterprise.Client`                                       |
| Version          | **4.0.0**                                               | **4.0.0**                                            | **4.0.0**                                                      |
| Registry         | [PyPI](https://pypi.org/project/jobo-enterprise/)       | [npm](https://www.npmjs.com/package/jobo-enterprise) | [NuGet](https://www.nuget.org/packages/Jobo.Enterprise.Client) |
| Source           | [jobo-python](https://github.com/Prakkie91/jobo-python) | [jobo-node](https://github.com/Prakkie91/jobo-node)  | [jobo-dotnet](https://github.com/Prakkie91/jobo-dotnet)        |
| Requires         | Python 3.9+                                             | Node 18+                                             | .NET 6+                                                        |
| Jobs search      | ✅                                                       | ✅                                                    | ✅                                                              |
| Job by id        | ✅                                                       | ✅                                                    | ✅                                                              |
| Jobs feed        | ✅                                                       | ✅                                                    | ✅                                                              |
| Managed feed     | ✅                                                       | ✅                                                    | ✅                                                              |
| Expired ids      | ✅                                                       | ✅                                                    | ✅                                                              |
| Companies        | ✅                                                       | ✅                                                    | ✅                                                              |
| Geocoding        | ✅                                                       | ✅                                                    | ✅                                                              |
| Auto-pagination  | ✅                                                       | ✅                                                    | ✅                                                              |
| Retry on 429/503 | ✅                                                       | ✅                                                    | ✅                                                              |

<Note>
  **4.0.0 is a breaking release.** The `EmploymentType` and `CompensationPeriod` value sets changed to the values the API actually accepts, and the retired Auto Apply sub-client was removed. Pin an exact version and read the notes below before upgrading from 3.x — or, for Node, from 1.x.
</Note>

<Note>
  **Auto Apply is not part of `jobo-enterprise`.** The live contract is profileless and callback-driven, and it has a dedicated TypeScript client: [`@jobo-ai/autoapply`](#auto-apply-typescript). Earlier `jobo-enterprise` versions exposed a `create_profile` / `start_session` / `set_answers` model against routes that no longer exist; 4.0.0 removes it.
</Note>

You do not need a client library. Every endpoint is plain HTTPS with a single header, and the curl examples in these docs work with any language.

***

## Upgrading to 4.0.0

Three things can change behaviour in code that already works.

**Filter values.** `EmploymentType` members now serialize the canonical `full-time` / `part-time` instead of `full_time` / `part_time`. The index matches both spellings, so this changes what goes over the wire rather than what you get back. `freelance` and `intern` are new members — those values exist in the data and the old enums could not express them. `CompensationPeriod` members were renamed to the values the API actually emits (`hourly`, `yearly`, `per-diem`, …); the old ones (`hour`, `day`, `year`) matched nothing.

**Auto Apply.** `client.auto_apply` / `client.autoApply` / `client.AutoApply` no longer exists. Nothing on it worked — every method targeted a removed route. Its replacement is the dedicated [`@jobo-ai/autoapply`](#auto-apply-typescript) package.

**Node only.** npm was serving 1.0.1, which exposed flat methods (`searchJobs`, `getJobsFeed`). 4.0.0 is the sub-client shape the other two clients have always had: `client.search.search(...)`, `client.feed.getJobs(...)`.

***

## Python

```bash theme={null}
pip install jobo-enterprise
```

The client is organised into sub-clients by resource. **Every method takes keyword arguments only.**

```python theme={null}
from jobo_enterprise import JoboClient

with JoboClient(api_key="YOUR_API_KEY") as client:
    results = client.search.search(q="data scientist", location="New York", page_size=10)
    for job in results.jobs:
        print(f"{job.title} at {job.company.name} — {job.source}")
```

`JoboClient(api_key, *, base_url="https://connect.jobo.world", timeout=30.0, feed_timeout=120.0, httpx_client=None)`. Use `AsyncJoboClient` for the async equivalent.

| Sub-client         | Methods                                                                                                                                       |
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `client.search`    | `search(...)`, `search_advanced(...)`, `iter_jobs(...)`, `get_job(job_id)`                                                                    |
| `client.feed`      | `get_jobs(...)`, `iter_jobs(...)`, `get_managed_jobs(...)`, `iter_managed_jobs(...)`, `get_expired_job_ids(...)`, `iter_expired_job_ids(...)` |
| `client.companies` | `get(company_id)`, `get_jobs(company_id, ...)`                                                                                                |
| `client.locations` | `geocode(location)`                                                                                                                           |

### Auto-pagination

`iter_jobs` and `iter_expired_job_ids` handle pages and cursors for you.

```python theme={null}
# Page-based, over search results
for job in client.search.iter_jobs(queries=["backend engineer"], locations=["London"]):
    print(job.title)

# Cursor-based, over the whole feed
for job in client.feed.iter_jobs(sources=["greenhouse"], batch_size=1000):
    save_to_database(job)
```

### Incremental sync

`updated_after` is the watermark for picking up changes after a backfill. Scans page by immutable creation time by default (`stable_scan`), so records cannot shift across page boundaries mid-read.

```python theme={null}
import datetime as dt

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

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

### Errors

`429` and `503` are retried with bounded backoff honouring `Retry-After`. Everything else raises, deriving from `JoboError`:

```python theme={null}
from jobo_enterprise import (
    JoboError,
    JoboAuthenticationError,
    JoboPermissionError,
    JoboNotFoundError,
    JoboRateLimitError,
    JoboValidationError,
    JoboCursorRestartRequiredError,
    JoboServerError,
)

try:
    results = client.search.search(q="engineer")
except JoboAuthenticationError:
    print("Invalid API key")
except JoboRateLimitError:
    print("Rate limited")
except JoboError as exc:
    print(f"API error: {exc} ({exc.code})")
```

Every exception carries the [problem `code`](/docs/errors) when the API supplies one, alongside `status_code`, `detail`, and the raw `response_body`.

***

## Node.js / TypeScript

```bash theme={null}
npm install jobo-enterprise
```

Zero runtime dependencies — it uses the built-in `fetch`, so it needs Node 18+ and also runs in Bun, Deno, and browsers. Options are camelCase; wire fields on the response objects stay snake\_case.

```typescript theme={null}
import { JoboClient } from "jobo-enterprise";

const client = new JoboClient({ apiKey: "YOUR_API_KEY" });

const results = await client.search.search({
  q: "software engineer",
  location: "San Francisco",
  sources: "greenhouse,lever",
});
```

`new JoboClient({ apiKey, baseUrl?, timeout?, feedTimeout?, fetch? })`.

| Sub-client         | Methods                                                                                                                           |
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------- |
| `client.search`    | `search(...)`, `searchAdvanced(...)`, `iter(...)`, `getJob(jobId)`                                                                |
| `client.feed`      | `getJobs(...)`, `iterJobs(...)`, `getManagedJobs(...)`, `iterManagedJobs(...)`, `getExpiredJobIds(...)`, `iterExpiredJobIds(...)` |
| `client.companies` | `get(companyId)`, `getJobs(companyId, ...)`                                                                                       |
| `client.locations` | `geocode(location)`                                                                                                               |

The iterators are async generators:

```typescript theme={null}
for await (const job of client.feed.iterJobs({ sources: ["greenhouse"], batchSize: 1000 })) {
  await saveToDatabase(job);
}
```

***

## .NET

```bash theme={null}
dotnet add package Jobo.Enterprise.Client
```

```csharp theme={null}
using Jobo.Enterprise.Client;

var client = new JoboClient(new JoboClientOptions { ApiKey = "YOUR_API_KEY" });

var results = await client.Search.SearchAsync(q: "software engineer", pageSize: 10);
```

Targets net6.0 and net8.0. Register it with DI via `services.AddJoboClient(...)`.

| Sub-client         | Methods                                                                                                                                                                        |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `client.Search`    | `SearchAsync(...)`, `SearchAdvancedAsync(...)`, `EnumerateAsync(...)`, `GetJobAsync(jobId)`                                                                                    |
| `client.Feed`      | `GetJobsAsync(...)`, `EnumerateJobsAsync(...)`, `GetManagedJobsAsync(...)`, `EnumerateManagedJobsAsync(...)`, `GetExpiredJobIdsAsync(...)`, `EnumerateExpiredJobIdsAsync(...)` |
| `client.Companies` | `GetAsync(companyId)`, `GetJobsAsync(companyId, ...)`                                                                                                                          |
| `client.Locations` | `GeocodeAsync(location)`                                                                                                                                                       |

Feed enumeration returns `IAsyncEnumerable<Job>`:

```csharp theme={null}
await foreach (var job in client.Feed.EnumerateJobsAsync(new JobFeedRequest { BatchSize = 1000 }))
{
    await SaveToDatabaseAsync(job);
}
```

If you pass your own `HttpClient` instead of `JoboClientOptions`, its `Timeout` governs every request — set it to at least 120 seconds when you use the feed endpoints.

***

## Auto Apply (TypeScript)

```bash theme={null}
npm install @jobo-ai/autoapply
```

[`@jobo-ai/autoapply`](https://www.npmjs.com/package/@jobo-ai/autoapply) covers the whole [Auto Apply](/docs/api-reference/auto-apply/auto-apply) surface: the four application operations plus a signed callback handler. Zero runtime dependencies; Node 20+, Bun, Deno, and edge runtimes. Properties are snake\_case, matching the wire format and these docs exactly.

```typescript theme={null}
import { createClient } from "@jobo-ai/autoapply";

const jobo = createClient({ apiKey: process.env.JOBO_API_KEY });

const application = await jobo.applications.create({ job_id: "..." }); // or apply_url
const result = await jobo.applications.waitForTerminal(application.id);
```

`create` sends the required `Idempotency-Key` for you, `list` auto-paginates with `for await`, and errors throw a typed `JoboAPIError` carrying the [problem `code`](/docs/errors) and `retryAfterSeconds`.

The callback side — signature verification, event parsing, and the `proceed` / `cancel` answer contract — is a Web-standard handler with framework adapters at `@jobo-ai/autoapply/next`, `/nuxt`, and `/node` (Express, Next.js Pages Router). See [Callbacks](/docs/api-reference/auto-apply/callbacks) for examples.

<Note>
  The AutoApply *provider* SDK, `@jobo-ai/autoapply-sdk`, is a different package for building browser agents — not for calling the API.
</Note>

***

## No SDK

```bash theme={null}
curl -G "https://connect.jobo.world/api/jobs" \
  -H "X-Api-Key: $JOBO_API_KEY" \
  --data-urlencode "q=designer" \
  --data-urlencode "page_size=10"
```

Pin whichever client you use to an exact version — see [Versioning](/docs/versioning) for the compatibility contract, and [Errors](/docs/errors) for the response shapes you will need to handle yourself.
