Documentation

Everything you need to integrate with the Open Job Board API.

The Open Job Board API is a free, open REST API for job listings. You can query thousands of jobs, perform full-text searches, and submit new listings. No sign-up is required for reading data.

Want to explore the API interactively? Try the API Explorer with built-in request testing.

Base URL

All endpoints share a single base URL. When self-hosting, replace with your own domain:

https://your-server.example.com

Interactive API documentation is available at /swagger-ui/.

Quick Start

Get up and running in under a minute. All read endpoints are public — no authentication required.

1. Search for jobs

Use full-text search to find relevant jobs:

curl -X POST "https://your-server.example.com/api/jobs/search" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "senior backend engineer",
    "country": "Germany",
    "remote": true,
    "page_num": 1,
    "page_size": 20
  }'

2. List jobs

Paginated list of active jobs:

curl "https://your-server.example.com/api/jobs?page=1&page_size=20"

3. Submit a job

Post a new job listing (no auth required, but rate limited):

curl -X POST "https://your-server.example.com/api/jobs" \
  -H "Content-Type: application/json" \
  -d '{
    "origin": { "source": "my-scraper", "reference": "job-123" },
    "title": "Senior Backend Engineer",
    "description": "We are looking for a backend engineer to join our platform team.",
    "company": { "name": "Acme Corp", "sector": "Technology" },
    "location": { "city": "Berlin", "country": "Germany", "remote": { "full": true } },
    "employment_type": "full-time",
    "salary": { "currency": "EUR", "min": 80000, "max": 120000, "period": "yearly" }
  }'

Authentication

Read Endpoints (no auth)

All read endpoints (list, search, detail) are public and require no authentication.

API Key for Writing (optional)

Job submissions work without authentication but are rate limited to 10 requests per minute per IP address.

For higher rate limits (100 req/min by default), include an API key in the X-API-Key header:

curl -X POST "https://your-server.example.com/api/jobs" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: ojb_your_api_key_here" \
  -d '{ ... }'

You can create an API key instantly below.

Create API Key

Generate an API key for higher rate limits on job submissions. Your key is hashed server-side and never stored in plaintext. Save it immediately — it will only be shown once.

A label to identify this key
For key recovery and important updates

List Jobs

GET /api/jobs

Returns a paginated list of active job listings, ordered by posted date (newest first).

Example

curl "https://your-server.example.com/api/jobs?page=1&page_size=20"

Parameters

ParameterTypeDescription
pageintegerPage number (default 1)
page_sizeintegerResults per page (default 20, max 100)

Response

{
  "data": [
    {
      "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
      "title": "Senior Backend Engineer",
      "company_name": "Acme Corp",
      "location_country": "Germany",
      "location_city": "Berlin",
      "remote_full": true,
      "employment_type": "full-time",
      "salary_currency": "EUR",
      "salary_min": 80000,
      "salary_max": 120000,
      "salary_period": "yearly",
      "posted_at": "2026-02-20T10:00:00Z"
    }
  ],
  "page": 1,
  "page_size": 20,
  "total_count": 150
}

Search Jobs

POST /api/jobs/search

Full-text search with relevance ranking. Results are scored by how well they match the query. The search is weighted: job title has the highest weight, followed by company name, description, and location.

Request Body

FieldTypeDescription
querystringSearch query (natural language, quoted phrases)
countrystringFilter by country
citystringFilter by city
remotebooleanOnly fully remote jobs
employmentstringEmployment type (case-insensitive)
salary_min_valnumberMinimum salary filter
salary_max_valnumberMaximum salary filter
source_filterstringFilter by source name
page_numintegerPage number (default 1)
page_sizeintegerResults per page (default 20, max 100)

Example

curl -X POST "https://your-server.example.com/api/jobs/search" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "senior backend engineer",
    "country": "Germany",
    "remote": true,
    "salary_min_val": 80000,
    "page_num": 1,
    "page_size": 20
  }'

Response

Returns a paginated envelope with job objects and a rank relevance score:

{
  "data": [
    {
      "rank": 0.42,
      "id": "a1b2c3d4-...",
      "title": "Senior Backend Engineer",
      "company_name": "Acme Corp",
      ...
    }
  ],
  "page": 1,
  "page_size": 20,
  "total_count": 42
}

Get Job Detail

GET /api/jobs/{id}

Returns the complete job record including full description, responsibilities, requirements, benefits, and all nested fields.

Example

curl "https://your-server.example.com/api/jobs/a1b2c3d4-e5f6-7890-abcd-ef1234567890"

Response

Returns the full job object. Includes all fields from the Job schema plus:

FieldTypeDescription
descriptionstringFull job description
responsibilitiesstring[]List of responsibilities
benefitsstring[]List of benefits
requirementsobjectQualifications, hard/soft skills
contactobjectContact person (name, email, phone)
company_anecdotestringBrief company description
company_locationsstring[]Physical office locations

Submit a Job

POST /api/jobs

Submit a new job listing. Only origin.source, title, and description are required. All other fields are optional.

Deduplication: If a job with the same source + reference already exists, the API returns HTTP 409. Scrapers can safely re-submit without creating duplicates.

Required Fields

FieldTypeDescription
origin.sourcestringUnique name for the submitter (1-200 chars)
titlestringJob title (2-500 chars)
descriptionstringJob description (10-100,000 chars)

Optional Fields

FieldTypeDescription
origin.referencestringOriginal job ID from source (for deduplication)
origin.contactobjectContact person: name, email, phone
companyobjectname, website, sector, anecdote, location[]
employment_typestringe.g. "full-time", "part-time", "contract"
locationobjectcity, country, remote: {full, days}
requirementsobjectqualifications[], hard_skills[], soft_skills[], others[]
salaryobjectcurrency, min, max, period (hourly/daily/weekly/monthly/yearly)
benefitsstring[]List of benefits
responsibilitiesstring[]List of responsibilities
posted_atdatetimeWhen originally posted (ISO 8601)
parsed_atdatetimeWhen parsed by scraper (ISO 8601)

Full Example

curl -X POST "https://your-server.example.com/api/jobs" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: ojb_your_key_here" \
  -d '{
    "origin": {
      "source": "my-scraper",
      "reference": "job-99999",
      "contact": { "name": "Jane", "email": "jobs@acme.com" }
    },
    "title": "Senior Backend Engineer",
    "description": "We are looking for a senior backend engineer to join our platform team and help us scale to millions of users.",
    "responsibilities": [
      "Design and build scalable REST APIs",
      "Own and improve database performance"
    ],
    "company": {
      "name": "Acme Corp",
      "website": "https://acme.com",
      "sector": "Technology"
    },
    "employment_type": "full-time",
    "location": {
      "city": "Berlin",
      "country": "Germany",
      "remote": { "full": true }
    },
    "requirements": {
      "hard_skills": ["Go", "PostgreSQL", "Kubernetes"],
      "qualifications": ["5+ years backend experience"]
    },
    "salary": {
      "currency": "EUR",
      "min": 90000,
      "max": 130000,
      "period": "yearly"
    },
    "benefits": ["30 days vacation", "Health insurance"]
  }'

Success Response (201)

{
  "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "created_at": "2026-02-27T12:00:00Z"
}

Filtering & Sorting

Use the POST /api/jobs/search endpoint for filtered and sorted queries. All filters are passed as JSON in the request body:

FieldTypeDescription
querystringFull-text search query
countrystringFilter by country (case-insensitive)
citystringFilter by city (case-insensitive)
remotebooleanOnly fully remote jobs
employmentstringEmployment type (case-insensitive)
salary_min_valnumberMinimum salary filter
salary_max_valnumberMaximum salary filter
locationstringLocation to geocode for proximity search
latitudenumberExplicit latitude for proximity search
longitudenumberExplicit longitude for proximity search
max_distance_kmnumberRadius in km for proximity search (default 50)

Sorting

Results are sorted by relevance (when a query is provided), then by proximity (when using geo search), then by posted date (newest first).

Proximity Search

Provide coordinates or a location string to find jobs near a specific area:

{
  "query": "engineer",
  "location": "Berlin, Germany",
  "max_distance_km": 100
}

Rate Limits

TierLimitScope
Anonymous (no API key)10 requests/minutePer IP address
With API key100 requests/minutePer API key

Rate limits apply to the submit-job endpoint only. Read endpoints (list, search, detail) are not rate limited.

When the limit is exceeded, the API returns 429 Too Many Requests with a Retry-After: 60 header.

If you provide an invalid API key, the request falls back to IP-based rate limiting silently.

Error Codes

CodeMeaningDetails
200SuccessRequest completed successfully
201CreatedJob submitted successfully
400Bad RequestInvalid JSON body
405Method Not AllowedWrong HTTP method
409ConflictDuplicate job (same source + reference)
422Validation ErrorRequest body failed schema validation
429Rate LimitedToo many requests, retry after 60s
500Server ErrorInternal server error

Error Response Format

{
  "error": "Validation failed",
  "details": {
    "fieldErrors": {
      "title": ["String must contain at least 2 character(s)"]
    },
    "formErrors": []
  }
}

Job Schema

Fields returned by the list and search endpoints:

FieldTypeDescription
iduuidUnique job identifier
titlestringJob title
company_namestring?Company name
company_sectorstring?Company industry
company_websitestring?Company website URL
location_citystring?Job city
location_countrystring?Job country
remote_fullboolean?True if fully remote
remote_daysinteger?Remote days per week (hybrid)
employment_typestring?e.g. full-time, part-time, contract
salary_currencystring?e.g. EUR, USD, GBP
salary_minnumber?Minimum salary
salary_maxnumber?Maximum salary
salary_periodstring?hourly, daily, weekly, monthly, yearly
sourcestringName of the submitter
referencestring?Original job ID from source
posted_atdatetime?When originally posted
created_atdatetimeWhen added to the database
is_activebooleanWhether the job is active