Metadata-Version: 2.4
Name: remote-jobs-api
Version: 1.2.6
Summary: Normalized remote-job data across 5 boards — one endpoint, one schema, skill fit-score
Author-email: Nova <earnnova@tten.no>
License: MIT
Project-URL: Homepage, https://github.com/earnnova-dev/remote-jobs-api
Project-URL: Documentation, https://earnnova-dev.github.io/remote-jobs-api/
Keywords: jobs,remote,api,scraping,feed,aggregator,data
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
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: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Software Development :: Libraries
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Dynamic: license-file

# Remote Jobs API

**Live, normalized remote-job data across 5 boards — one clean endpoint, one schema.**

Stop scraping Remotive / RemoteOK / We Work Remotely / Jobicy / Hacker News
yourself. This API pulls them, normalizes every listing into a single
schema, dedupes, and hands you structured JSON (or CSV) — with an optional
**0-100 skill fit-score** for ranking.

Built for:
- Job boards & aggregator sites
- Recruiter and ATS tooling
- Lead-gen and market research
- **AI agents** that need structured job data in one call

## Why it's different
- **One schema, every board.** No per-board adapters on your side.
- **No session scraping.** Reads public job-board endpoints — your data never
  leaves their servers, no browser, no account.
- **Self-hostable.** Stdlib-only Python (zero dependencies). Run it on a
  laptop, a $5 VPS, or containerize it with the included Dockerfile.
- **Deterministic fit scoring.** No hidden LLM, no black box — reproducible
  ranking you can reason about and unit-test.
- **Structured salary, machine-filterable.** Every listing carries parsed
  `salary_min` / `salary_max` / `salary_currency` / `salary_period`, and
  `?min_salary=N` filters by the top of the range — no regex on your side.

## Endpoints

| Method | Path | Description |
|--------|------|-------------|
| GET | `/health` | Status + live job count |
| GET | `/v1/jobs/sources` | Available boards |
| GET | `/v1/jobs` | Normalized job listings |

### GET /v1/jobs

Query params:

| Param | Type | Notes |
|-------|------|-------|
| `skills` | string | Comma-separated keywords → adds `fit_score`, ranks best-first |
| `source` | string | `remotive` / `remoteok` / `jobicy` / `wwr` / `hn` |
| `limit` | int | 1-500 (default 50) |
| `min_score` | int | 0-100, filter by fit (needs `skills`) |
| `min_salary` | number | min salary, filters on the parsed top-of-range (`salary_max`); drops jobs with no parsed salary |
| `format` | string | `json` (default) or `csv` |

### Example

```bash
curl "https://<your-host>/v1/jobs?skills=python,backend,api&limit=3"
```

```json
{
  "count": 3,
  "generated_at": 1790433192,
  "jobs": [
    {
      "id": "94516b10164d3c70",
      "title": "Senior Backend Developer (Python)",
      "company": "Proxify AB",
      "url": "https://weworkremotely.com/remote-jobs/proxify-ab-senior-backend-developer-python-10",
      "location": "Anywhere in the World",
      "salary": "USD 170,000-200,000 / yearly",
      "salary_min": 170000,
      "salary_max": 200000,
      "salary_currency": "USD",
      "salary_period": "year",
      "category": "Back-End Programming",
      "source": "wwr",
      "published": "2026-09-15T09:01:36+00:00",
      "fit_score": 80
    }
  ]
}
```

## Run it yourself

Zero runtime dependencies — it's pure Python stdlib.

**Option A — CLI (query the feed directly, no server needed):**

```bash
pip install remote-jobs-api
remote-jobs-api --skills python,api --limit 5
remote-jobs-api --source wwr --format csv
remote-jobs-api --help
```

**Option B — HTTP API server (self-host):**

```bash
pip install remote-jobs-api
remote-jobs-api --serve --port 8321        # or: python -m remote_jobs_api.server
```

Or from source / Docker:

```bash
git clone https://github.com/earnnova-dev/remote-jobs-api
cd remote-jobs-api
python3 -m remote_jobs_api.server          # listens on :8321
```

```bash
docker build -t remote-jobs-api .
docker run -p 8321:8321 remote-jobs-api
```

Or the fastest way — one command with the included `docker-compose.yml`
(keys + accounts persist in a named volume):

```bash
git clone https://github.com/earnnova-dev/remote-jobs-api && cd remote-jobs-api
export RJA_ADMIN_TOKEN=***            # required: mint keys via /admin/keys
docker compose up -d                     # live on http://localhost:8321
```

**Option C — Kubernetes (Helm chart, self-host):**

A production-ready chart ships in the repo (`charts/remote-jobs-api/`) and is
published as a static Helm repository on GitHub Pages. It provisions the API
server, a persistent key store (PVC), and an optional ingress, PDB and
NetworkPolicy.

```bash
helm repo add earnnova https://earnnova-dev.github.io/remote-jobs-api/
helm repo update
helm install rja earnnova/remote-jobs-api --namespace rja --create-namespace --set image.repository=ghcr.io/earnnova-dev/remote-jobs-api --set image.tag=1.1.0
```

The chart generates an admin token on install; retrieve it:

```bash
kubectl -n rja get secret rja-rja -o jsonpath='{.data.token}' | base64 -d
```

See `charts/remote-jobs-api/README.md` for the full values reference
(ingress, TLS, persistence, NetworkPolicy egress allowlist, resources).


Env vars:
- `PORT` (default `8321`)
- `RJA_CACHE_TTL` (default `300` s) — how long to cache upstream fetches

## Testing

Offline unit tests (no network): `python -m pytest`.

## Pricing (indicative, for a hosted tier)

| Plan | Price | Includes |
|------|-------|----------|
| **Free** | $0 | 100 calls/mo, all boards, 50 results/req |
| **Pro** | $19/mo | 10k calls/mo, CSV, min_score, higher limits |
| **Team** | $49/mo | 100k calls/mo, SLA, webhooks (soon) |

> Self-hosted = free, MIT. The paid tier is the hosted, always-on,
> high-rate-limit version — the same engine, managed for you.

## Related product

Want alerts instead of a data endpoint? **GigWatch** is a self-hosted gig watcher
that monitors the same live feeds, filters by your skills, and pings you
(console, email, or Slack) only when a new matching gig appears.

- Repo: https://github.com/earnnova-dev/gigwatch
- Install: `pip install gigwatch-nova`

## License

MIT. See [LICENSE](LICENSE).

## Data & compliance

This API only reads public, no-auth job-board endpoints and returns data
exactly as those boards publish it. It does not store your personal data,
does not scrape authenticated sessions, and does not resell any board's
proprietary content beyond what is publicly viewable.
