Skip to content

magnusfroste/openjobs-api

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

124 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

OpenJobs - Job Aggregation Platform

Microservices-based job aggregation platform with intelligent incremental sync

OpenJobs aggregates job listings from multiple sources into a unified API, featuring:

  • πŸ”„ Incremental Sync - Only fetch new jobs, avoid duplicates
  • 🐳 Microservices Architecture - Each connector runs independently
  • ⏰ Cron Scheduling - Precise daily sync at 6:00 AM
  • πŸ“Š Database-backed State - No file-based persistence needed
  • πŸš€ High Limits - Fetch up to 500 jobs per connector

🎯 Current Status

Production Deployment: https://api.openjobs.ink
Legacy URL: https://app-openjobs.katsu6.easypanel.host (deprecated)

Connector Jobs Sync Method Limit
ArbetsfΓΆrmedlingen 50+ API date filter 500
EURES (Adzuna) 1+ API date filter 100
Remotive 100+ Client filter 100
RemoteOK 168+ Client filter All
Total 333+ Daily at 6 AM -

πŸ—οΈ Architecture

Microservices Design

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    Main API (Port 8080)                 β”‚
β”‚  - REST API                                             β”‚
β”‚  - Dashboard                                            β”‚
β”‚  - Scheduler (Cron)                                     β”‚
β”‚  - HTTP Plugin Orchestrator                             β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
             β”‚
             β”‚ HTTP POST /sync
             β”‚
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚                 β”‚          β”‚          β”‚
    β–Ό                 β–Ό          β–Ό          β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  AF    β”‚      β”‚ EURES  β”‚  β”‚Remotiveβ”‚  β”‚RemoteOKβ”‚
β”‚ :8081  β”‚      β”‚ :8082  β”‚  β”‚ :8083  β”‚  β”‚ :8084  β”‚
β””β”€β”€β”€β”€β”¬β”€β”€β”€β”˜      β””β”€β”€β”€β”€β”¬β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”¬β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”¬β”€β”€β”€β”˜
     β”‚               β”‚           β”‚           β”‚
     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                     β”‚
                     β–Ό
            β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
            β”‚  Supabase DB    β”‚
            β”‚  - job_posts    β”‚
            β”‚  - sync_logs    β”‚
            β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

✨ Key Features

Intelligent Incremental Sync

  • Database-backed state: Tracks last sync via posted_date in database
  • API date filtering: ArbetsfΓΆrmedlingen & EURES use API parameters
  • Client-side filtering: Remotive & RemoteOK filter locally
  • Zero duplicates: All syncs show 0 new when no updates

Cron-Based Scheduling

CRON_SCHEDULE=0 6 * * *  # Daily at 6:00 AM
  • Precise timing (not interval-based)
  • Configurable per environment
  • Fallback to interval mode if not set

High Performance

  • ArbetsfΓΆrmedlingen: 500 jobs/sync (up from 20)
  • EURES: 100 jobs/sync (up from 10)
  • Remotive: 100 jobs/sync (up from 10)
  • RemoteOK: All jobs (client-filtered)

Production Ready

  • Docker containerized
  • Easypanel deployment
  • Health checks on all services
  • Comprehensive logging

πŸ“‘ API Endpoints

Main API (Port 8080)

# Health & Status
GET  /health                 # System health check
GET  /                       # Dashboard UI

# Jobs (Public)
GET  /jobs                   # List all jobs
GET  /jobs/:id               # Get specific job

# Jobs (Protected - Requires API Key)
POST /jobs                   # Post a new job (requires X-API-Key header)

# Sync
POST /sync/manual            # Trigger manual sync
GET  /sync/history           # View sync logs

# Plugins
GET  /plugins                # List registered plugins

πŸ“ Note: To post jobs via API, you need an API key. Register at OpenJobs_Web to get your free API key instantly.

πŸš€ How to Start Posting Jobs

Quick Start (3 Steps)

1. Get Your API Key (30 seconds)

# Visit the registration page
https://openjobs-web.vercel.app/register

# Fill in:
- Company name
- Email
- Website (optional)

# βœ… Your API key is generated instantly!

2. Post Your First Job

Option A: Use the Web Form (Easiest)

# Login at OpenJobs_Web
https://openjobs-web.vercel.app/login

# Click "Post a Job"
# Fill the form and submit

Option B: Use the API (For Integration)

curl -X POST https://api.openjobs.ink/jobs \
  -H "Content-Type: application/json" \
  -H "X-API-Key: YOUR-API-KEY-HERE" \
  -d '{
    "title": "Senior React Developer",
    "company": "Your Company",
    "description": "We are looking for an experienced developer...",
    "location": "Stockholm, Sweden",
    "employment_type": "full-time",
    "salary_min": 50000,
    "salary_max": 70000,
    "salary_currency": "SEK",
    "is_remote": false,
    "url": "https://yourcompany.com/apply"
  }'

3. Your Job is Live!

  • βœ… Immediately available via /jobs API
  • βœ… Visible to all job aggregators
  • βœ… Included in OpenJobs ecosystem

Required Fields

  • title - Job title
  • company - Company name
  • description - Job description

Optional Fields

  • location, employment_type, salary_min, salary_max, salary_currency
  • is_remote, url, expires_date
  • requirements[], benefits[]

Need Help?


Jobs API - Query Parameters

GET /jobs

Returns a list of job postings with optional filtering.

Query Parameters:

Parameter Type Description Example
limit integer Number of jobs to return (default: 20, max: 500) ?limit=100
offset integer Number of jobs to skip for pagination (default: 0) ?offset=20
is_active boolean Filter by active status (default: all) ?is_active=true
created_after ISO 8601 Return jobs created after this timestamp ?created_after=2025-11-17T06:00:00Z

Examples:

# Get first 100 active jobs
GET /jobs?is_active=true&limit=100

# Get jobs created in the last 24 hours
GET /jobs?created_after=2025-11-16T15:00:00Z&is_active=true

# Incremental sync - get only new jobs since last check
GET /jobs?created_after=2025-11-17T06:00:00Z&is_active=true&limit=500

# Pagination - get next page
GET /jobs?is_active=true&limit=100&offset=100

Response Format:

{
  "success": true,
  "data": [
    {
      "id": "af-12345",
      "title": "Senior Developer",
      "company": "Tech AB",
      "description": "Job description...",
      "location": "Stockholm",
      "salary_min": 50000,
      "salary_max": 70000,
      "salary_currency": "SEK",
      "is_remote": false,
      "is_active": true,
      "url": "https://...",
      "posted_date": "2025-11-15T10:00:00Z",
      "created_at": "2025-11-17T06:15:23Z",
      "updated_at": "2025-11-17T06:15:23Z",
      "requirements": ["Python", "React"],
      "benefits": ["Remote work", "Health insurance"]
    }
  ]
}

Key Fields:

  • posted_date: When the employer originally posted the job
  • created_at: When OpenJobs added the job to the database (use for incremental sync)
  • updated_at: When the job was last modified in OpenJobs
  • is_active: Whether the job is currently active (not expired)

Plugin Endpoints (Ports 8081-8084)

GET  /health                 # Plugin health check
POST /sync                   # Trigger plugin sync

πŸš€ Deployment

Quick Start (Easypanel)

1. Deploy Main API

Image: ghcr.io/magnusfroste/openjobs:latest
Port: 8080

Environment Variables:

SUPABASE_URL=https://supabase.froste.eu
SUPABASE_ANON_KEY=your-key-here
USE_HTTP_PLUGINS=true
CRON_SCHEDULE=0 6 * * *
PLUGIN_ARBETSFORMEDLINGEN_URL=http://plugin-arbetsformedlingen:8081
PLUGIN_EURES_URL=http://plugin-eures:8082
PLUGIN_REMOTIVE_URL=http://plugin-remotive:8083
PLUGIN_REMOTEOK_URL=http://plugin-remoteok:8084

2. Deploy Each Plugin

Create 4 services with these images:

  • ghcr.io/magnusfroste/openjobs-arbetsformedlingen:latest (Port 8081)
  • ghcr.io/magnusfroste/openjobs-eures:latest (Port 8082)
  • ghcr.io/magnusfroste/openjobs-remotive:latest (Port 8083)
  • ghcr.io/magnusfroste/openjobs-remoteok:latest (Port 8084)

Each needs:

SUPABASE_URL=https://supabase.froste.eu
SUPABASE_ANON_KEY=your-key-here
PORT=808X  # Respective port

EURES also needs:

ADZUNA_APP_ID=your-adzuna-id
ADZUNA_APP_KEY=your-adzuna-key

See EASYPANEL_ENV_SETUP.md for detailed instructions.

πŸ”Œ Connectors

Active Connectors

Connector Source Type Jobs
ArbetsfΓΆrmedlingen Swedish Employment Service Government 50+
EURES Adzuna API (European jobs) Commercial 1+
Remotive Remotive.com Platform 100+
RemoteOK RemoteOK.com Platform 168+

Connector Interface

All connectors implement:

type PluginConnector interface {
    GetID() string
    GetName() string
    FetchJobs() ([]JobPost, error)
    SyncJobs() error
}

Adding New Connectors

  1. Create connector in connectors/yourname/
  2. Implement PluginConnector interface
  3. Add Dockerfile
  4. Register in main scheduler
  5. Deploy as new microservice

See existing connectors for examples.

οΏ½ Getting an API Key

To post jobs via the API, you need an API key from OpenJobs_Web:

  1. Register: Visit OpenJobs_Web
  2. Create Account: Fill in your company details
  3. Get API Key: Your unique API key is generated instantly
  4. Use It: Include in X-API-Key header when posting jobs
# Example: Post a job with your API key
curl -X POST https://api.openjobs.ink/jobs \
  -H "Content-Type: application/json" \
  -H "X-API-Key: your-api-key-here" \
  -d '{"title":"Developer","company":"Acme","description":"..."}'

Features:

  • βœ… Free forever
  • βœ… Instant generation
  • βœ… No credit card required
  • βœ… Post unlimited jobs

οΏ½οΏ½ Data Sync

Automatic Sync

  • Schedule: Daily at 6:00 AM (configurable via CRON_SCHEDULE)
  • Method: HTTP POST to each plugin container
  • Logging: All syncs logged to sync_logs table

Manual Sync

curl -X POST https://api.openjobs.ink/sync/manual

Incremental Sync Logic

API Date Filtering (ArbetsfΓΆrmedlingen, EURES):

  1. Query database for most recent job's posted_date
  2. Add ?published-after=YYYY-MM-DD to API request
  3. API returns only new jobs

Client-Side Filtering (Remotive, RemoteOK):

  1. Fetch all jobs from API
  2. Query database for most recent job's posted_date
  3. Filter locally to only process new jobs
  4. Skip transformation/insertion of duplicates

πŸ› οΈ Local Development

Prerequisites

  • Go 1.21+
  • Docker & Docker Compose
  • Supabase account (or self-hosted)

Quick Start

1. Clone & Setup

git clone https://github.com/magnusfroste/openjobs.git
cd openjobs
cp .env.example .env
# Edit .env with your Supabase credentials

2. Run Database Migrations

-- In Supabase SQL Editor, run:
migrations/001_create_job_posts.sql
migrations/002_add_job_fields.sql

3. Start All Services

docker-compose -f docker-compose.plugins.yml up

This starts:

4. Trigger Sync

curl -X POST http://localhost:8080/sync/manual

πŸ“ Project Structure

openjobs/
β”œβ”€β”€ cmd/
β”‚   β”œβ”€β”€ openjobs/                 # Main API
β”‚   β”œβ”€β”€ plugin-arbetsformedlingen/ # AF plugin
β”‚   β”œβ”€β”€ plugin-eures/             # EURES plugin
β”‚   β”œβ”€β”€ plugin-remotive/          # Remotive plugin
β”‚   └── plugin-remoteok/          # RemoteOK plugin
β”œβ”€β”€ connectors/
β”‚   β”œβ”€β”€ arbetsformedlingen/       # AF connector logic
β”‚   β”œβ”€β”€ eures/                    # EURES connector logic
β”‚   β”œβ”€β”€ remotive/                 # Remotive connector logic
β”‚   └── remoteok/                 # RemoteOK connector logic
β”œβ”€β”€ pkg/
β”‚   β”œβ”€β”€ models/                   # Data models
β”‚   └── storage/                  # Database operations
β”œβ”€β”€ internal/
β”‚   β”œβ”€β”€ api/                      # HTTP handlers
β”‚   └── scheduler/                # Cron scheduler
β”œβ”€β”€ migrations/                   # Database migrations
β”œβ”€β”€ docs/                         # Documentation
β”œβ”€β”€ Dockerfile                    # Main API container
β”œβ”€β”€ docker-compose.plugins.yml    # All services
└── EASYPANEL_ENV_SETUP.md        # Deployment guide

πŸ“š Documentation

🀝 Contributing

Contributions welcome! Please:

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Submit a pull request

πŸ“ License

MIT License - see LICENSE file for details.


Built with ❀️ by @magnusfroste

About

OpenJobs is a dynamic job aggregation platform designed for seamless integration by aggregating listings from multiple sources into a unified API. Utilizing a robust microservices architecture, OpenJobs ensures efficient and intelligent job synchronization, making it ideal for developers who need a comprehensive and reliable job data source.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors