
Jobbank Search
- 51 installs
- 29.7k repo stars
- Updated August 4, 2026
- madslorentzen/ai-job-search
Searches live Danish job listings on Akademikernes Jobbank (jobbank.dk) via CLI, with detail lookup by job ID.
About
Queries jobbank.dk's public RSS feed and JSON-LD pages for academic and highly-educated positions in Denmark, filtering by keyword, type, location, industry, and education. A developer uses it to find and inspect Danish academic jobs from the terminal.
- Repeatable filters for type, location, work-area, industry, education
- search then detail workflow; RSS capped at 100 results, no auth
Jobbank Search by the numbers
- 51 all-time installs (skills.sh)
- +15 installs in the week ending Aug 2, 2026 (Skillselion tracking)
- Ranked #309 of 550 CLI & Terminal skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/madslorentzen/ai-job-search --skill jobbank-searchAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 51 |
|---|---|
| repo stars | ★ 29.7k |
| Last updated | August 4, 2026 |
| Repository | madslorentzen/ai-job-search ↗ |
What it does
Searches live Danish job listings on Akademikernes Jobbank (jobbank.dk) via CLI, with detail lookup by job ID.
Files
Jobbank Search Skill
Search live Danish job listings from Akademikernes Jobbank — Denmark's primary job portal for highly educated candidates. No authentication needed. Uses the RSS feed for search (up to 100 results) and JSON-LD parsing for detailed job information.
When to use this skill
Invoke this skill when the user wants to:
- Search for jobs, positions, or career opportunities in Denmark
- Find academic, graduate, trainee, Ph.d., or postdoc positions
- Look for jobs by keyword, industry, location, education background, or work function
- Find remote or hybrid positions in Denmark
- Get full details for a specific job posting on jobbank.dk
- Check what positions are available at a specific company on jobbank.dk
- Browse jobs suitable for new graduates or people with international backgrounds
Commands
Search jobs
bun run skills/jobbank-search/cli/src/cli.ts search [flags]Key flags:
--key <text>— keyword search (title, company, keyword)--exclude <text>— exclude keywords from results--type <code>— job type:3=Fuldtidsjob,6=Graduate/trainee,13=Deltidsjob,8=Vikariat,12=Ph.d. & Postdoc,11=Freelance,9=Praktikplads,4=Studiejob (repeatable)--location <code>— region:2=Storkøbenhavn,8=Østjylland (Aarhus),7=Midtjylland,6=Nordjylland,13=Fyn (repeatable)--work-area <code>— function:31=IT-Software,43=Data & Analyse,26=Ledelse,29=Marketing (repeatable)--industry <code>— sector:10331=IT & Tele,10442=Forskning & Uddannelse,10358=Finans (repeatable)--education <code>— education field:24=IT,21=Økonomi & Revision,34=Samfundsvidenskab (repeatable)--remote <value>—helt(fully remote) ordelvist(partially remote)--suitable-for <code>—2=Nyuddannede,4=International baggrund,5=Erfarne--company <id>— filter by company ID--since <YYYY-MM-DD>— jobs posted on or after this date--limit <n>— cap results returned by CLI--format json|table|plain
RSS limitation: The RSS feed returns max 100 items per request. No pagination is available via RSS.meta.totalshows the true count;resultsis capped at 100.
Full job detail
bun run skills/jobbank-search/cli/src/cli.ts detail <id> [--format json|plain]id is the numeric job ID from search results. Fetches the job page and parses the embedded Schema.org JobPosting JSON-LD for structured data.
---
How to use effectively
Start with `search`, then use `detail` for full description.
1. Use search with --key and/or filters to find matching jobs with IDs 2. Call detail <id> to get the full HTML job description, exact deadline, and company details
Use repeatable flags for multi-value filters. Most filter flags can be repeated to match any of the values:
# IT or Finance industry, Copenhagen or Aarhus
bun run skills/jobbank-search/cli/src/cli.ts search \
--industry 10331 --industry 10358 \
--location 2 --location 8Filter codes are documented in the README at skills/jobbank-search/cli/README.md.
---
Usage examples
Find data scientist jobs in Copenhagen
bun run skills/jobbank-search/cli/src/cli.ts search \
--key "data scientist" \
--location 2 \
--format tableGraduate trainee positions for new graduates
bun run skills/jobbank-search/cli/src/cli.ts search \
--type 6 \
--suitable-for 2 \
--format tableRemote IT software jobs
bun run skills/jobbank-search/cli/src/cli.ts search \
--work-area 31 \
--remote helt \
--format tablePh.d. and postdoc positions in research
bun run skills/jobbank-search/cli/src/cli.ts search \
--type 12 \
--industry 10442 \
--format tableRecent full-time jobs posted since March 1
bun run skills/jobbank-search/cli/src/cli.ts search \
--type 3 \
--since 2026-03-01 \
--format tableFull details for a specific job
bun run skills/jobbank-search/cli/src/cli.ts detail 1234567 --format plainIT jobs in Aarhus or Copenhagen
bun run skills/jobbank-search/cli/src/cli.ts search \
--key developer \
--location 2 --location 8 \
--work-area 31 \
--format table---
Output formats
| Format | Best for |
|---|---|
json | Default — programmatic use, passing IDs to detail |
table | Quick human-readable list of results |
plain | Single-job detail views (detail command) |
All errors are written to stderr as { "error": "...", "code": "..." } and the process exits with code 1.
---
Notes
- Data is from the public jobbank.dk RSS feed and HTML pages — no credentials required.
- RSS feed returns max 100 results per query. For higher counts,
meta.totalshows the true total. - The
detailcommand fetches a full job page and extracts the JSON-LD structured data block. locationvalues are region codes (e.g.2= Storkøbenhavn), not city names.- All filter codes are documented in
skills/jobbank-search/cli/README.md.
{
"name": "jobbank-cli",
"version": "1.0.0",
"description": "CLI for Akademikernes Jobbank (jobbank.dk) — job search for highly educated candidates",
"type": "module",
"main": "src/cli.ts",
"bin": {
"jobbank": "src/cli.ts"
},
"scripts": {
"start": "bun run src/cli.ts",
"test": "bun test --timeout 30000",
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@bunli/core": "latest",
"@bunli/utils": "latest",
"node-html-parser": "^6.1.13",
"zod": "^3.23.0"
},
"devDependencies": {
"@types/bun": "latest",
"typescript": "^5.4.0"
}
}
jobbank-cli
CLI for Akademikernes Jobbank — Denmark's job portal for highly educated candidates.
Data sources:
- RSS feed:
https://jobbank.dk/job/rss?{params}— 100 items max, all search filters work - Job detail:
https://jobbank.dk/job/{id}/— JSON-LD (Schema.org JobPosting) embedded in page HTML
Authentication: None required. A browser User-Agent header is required to bypass bot protection. Format: RSS XML (search), HTML with embedded JSON-LD (detail).
---
Installation
cd skills/jobbank-search/cli
bun install---
Commands
| Command | Description |
|---|---|
search | Search job listings via RSS feed |
detail | Full detail for a single job posting |
All commands accept --format json|table|plain (default: json). All errors are written to stderr as { "error": "...", "code": "..." } and the process exits with code 1.
---
Filter Reference Tables
Job Types (--type / cvtype)
| Code | Label |
|---|---|
| 3 | Fuldtidsjob |
| 6 | Graduate/trainee |
| 13 | Deltidsjob |
| 8 | Vikariat |
| 12 | Ph.d. & Postdoc |
| 11 | Freelance |
| 15 | Iværksætterprojekt |
| 14 | Event |
| 9 | Praktikplads |
| 4 | Studiejob |
| 5 | Studieprojekt/speciale |
Location / Region (--location / amt)
| Code | Label |
|---|---|
| 2 | Storkøbenhavn |
| 3 | Nordsjælland |
| 14 | Østsjælland |
| 4 | Vestsjælland |
| 5 | Sydsjælland & Øer |
| 13 | Fyn |
| 12 | Sønderjylland |
| 11 | Sydvestjylland |
| 9 | Vestjylland |
| 10 | Sydøstjylland |
| 7 | Midtjylland |
| 8 | Østjylland (Aarhus) |
| 6 | Nordjylland |
| 20 | Bornholm |
| 21 | Øresundsregionen |
| 22 | Grønland & Færøerne |
| 23 | Udlandet (Sverige) |
| 24 | Udlandet (Norge) |
| 19 | Udlandet (øvrige) |
Work Area / Function (--work-area / erf)
| Code | Label |
|---|---|
| 20 | Administration |
| 38 | Arkitektur & Design |
| 22 | Bank & Forsikring |
| 43 | Data & Analyse |
| 47 | Eksport |
| 41 | Forskning & Udvikling |
| 28 | Human Resources |
| 49 | Indkøb |
| 34 | Internet & Multimedia |
| 32 | IT - Hardware |
| 33 | IT - Netværk & Telekomm. |
| 31 | IT - Software |
| 24 | Jura |
| 35 | Kommunikation, Media & SoMe |
| 46 | Konstruktion & Beregning |
| 37 | Kunst & Kultur |
| 26 | Ledelse & Planlægning |
| 29 | Marketing & Reklame |
| 40 | Medicinal & Sundhed |
| 45 | Naturvidenskab |
| 23 | Organisation & Forening |
| 52 | Politik & Samfund |
| 44 | Produktion |
| 27 | Projektledelse |
| 50 | Rådgivning & Support |
| 30 | Salg |
| 39 | Socialvæsen |
| 42 | Teknik |
| 25 | Topledelse |
| 48 | Transport & Logistik |
| 36 | Undervisning |
| 21 | Økonomi & Forvaltning |
Education Field (--education / udd)
| Code | Label |
|---|---|
| 20 | Administration |
| 43 | Anlæg, Byggeri & Konstruktion |
| 29 | Arkitektur, Kunst & Design |
| 47 | Elektro & Telekommunikation |
| 32 | Fødevarer & Veterinær |
| 38 | Human Resources |
| 28 | Humaniora |
| 24 | IT |
| 23 | Jura |
| 44 | Kemi, Biotek & Materialer |
| 45 | Klima, Miljø & Energi |
| 37 | Landbrug & Natur |
| 22 | Marketing & Business |
| 48 | Maskin & Design |
| 46 | Matematik, Fysik & Nano |
| 31 | Medicinal & Sundhed |
| 30 | Naturvidenskab |
| 41 | Organisation & Ledelse |
| 35 | Produktion, Logistik & Transport |
| 34 | Samfundsvidenskab |
| 25 | Sprog, Media & Kommunikation |
| 33 | Teknik & Teknologi |
| 26 | Undervisning & Pædagogik |
| 21 | Økonomi & Revision |
Industry (--industry / branche)
| Code | Label |
|---|---|
| 10359 | Advokat & Revision |
| 11669 | Byggeri & Anlæg |
| 11634 | Elektronik & Maskin |
| 16791 | Fagforeninger, A-kasser & Pensionskasser |
| 10358 | Finans, Forsikring & Pension |
| 10442 | Forskning & Uddannelse |
| 15407 | Fødevarer & Dagligvarer |
| 10364 | Handel & Service |
| 10331 | IT & Tele |
| 17209 | Klima, Energi & Forsyning |
| 16826 | Kommuner |
| 10341 | Kultur, Medier & Underholdning |
| 10333 | Medicinal, Biotek & Kemi |
| 10363 | Papir, Møbel & Materialer |
| 11626 | Regioner, Sundhed- & Socialvæsen |
| 15586 | Rådgivning & Konsulentservice |
| 10362 | Stat, Politik & Samfund |
| 10440 | Transport |
| 12450 | Vikar & Rekruttering |
Remote Work (--remote / fjernarbejde)
| Value | Label |
|---|---|
helt | Fully remote |
delvist | Partially remote |
Suitable For (--suitable-for / andet)
| Code | Label |
|---|---|
| 2 | Nyuddannede |
| 4 | International baggrund |
| 5 | Erfarne |
---
search — Search job listings
Endpoint: GET https://jobbank.dk/job/rss?{params}
bun run src/cli.ts search [flags]Flags
| Flag | Type | Default | Description |
|---|---|---|---|
--key | string | — | Keyword search (title, company, keyword) |
--exclude | string | — | Exclude keywords (antikey) |
--type | number | — | Job type code (cvtype). Repeatable for multiple: --type 3 --type 6 |
--education | number | — | Education field code (udd). Repeatable. |
--location | number | — | Region code (amt). Repeatable. |
--work-area | number | — | Work area / function code (erf). Repeatable. |
--industry | number | — | Industry code (branche). Repeatable. |
--suitable-for | number | — | Suitable-for code (andet). Repeatable. |
--company | number | — | Company ID (virk) |
--remote | string | — | Remote work: helt or delvist |
--since | string | — | Posted on or after date, format YYYY-MM-DD (oprettet) |
--limit | number | — | Cap total results returned by CLI (client-side) |
--format | string | json | Output format: json, table, plain |
Important limitation: The RSS feed returns a maximum of 100 items per request. There is no pagination via RSS — thepage=parameter has no effect on the RSS endpoint. If your query matches more than 100 jobs, only the first 100 are returned. Themeta.totalfield reflects the true total count (fetched separately from the HTML search page title), whileresultsis capped at 100.
Multi-value flags: Flags marked "Repeatable" map to params that accept multiple values in the API (repeated query params). Pass them multiple times:--type 3 --type 6sendscvtype=3&cvtype=6.
RSS Parsing
The CLI fetches the RSS feed and parses each <item> as follows:
- id: extracted from the URL path —
/job/{id}/{company-slug}/{title-slug}— the first numeric segment after/job/ - title: from
<title>(CDATA) - description, company, location, jobType, deadline: parsed from the
<description>field, which has the format:"JobType hos Company, Location (Ansøgningsfrist: DD.MM.YYYY)"or"JobType hos Company, Location (Ansøgningsfrist: løbende)" - url: from
<link> - posted: from
<pubDate>, normalized to ISO 8601
Example
bun run src/cli.ts search --key python --location 2 --type 3 --limit 10
bun run src/cli.ts search --key "data scientist" --remote helt
bun run src/cli.ts search --industry 10331 --work-area 31 --format table
bun run src/cli.ts search --education 24 --suitable-for 2 --since 2026-03-01Response shape
{
"meta": {
"total": 457
},
"results": [
{
"id": "1234567",
"title": "Senior Data Scientist",
"company": "Novo Nordisk",
"location": "Bagsværd",
"jobType": "Fuldtidsjob",
"description": "Fuldtidsjob hos Novo Nordisk, Bagsværd (Ansøgningsfrist: 12.04.2026)",
"url": "https://jobbank.dk/job/1234567/novo-nordisk/senior-data-scientist",
"posted": "2026-03-02T00:00:00+01:00",
"deadline": "2026-04-12"
}
]
}Field details
| Field | Type | Notes |
|---|---|---|
id | string | Numeric job ID extracted from URL |
title | string | Job title |
company | string | Company name, parsed from description |
location | string | Location string, parsed from description |
jobType | string | Employment type (e.g. "Fuldtidsjob", "Graduate/trainee"), parsed from description |
description | string | Raw RSS description field (single-line summary) |
url | string | Full URL to job posting |
posted | string | Publication date in ISO 8601 |
deadline | string \ | null |
meta.totalis fetched from the HTML page<title>in a secondary request (pattern:"{N} relevante job og karriereopslag"). If the secondary request fails,meta.totalisnull.
---
detail — Full job detail
Endpoint: GET https://jobbank.dk/job/{id}/
bun run src/cli.ts detail <id> [--format json|plain]The id is the numeric job ID from search results (the id field). The short URL https://jobbank.dk/job/{id}/ redirects to the full slug URL and returns HTTP 200.
The CLI fetches the HTML page and extracts the <script type="application/ld+json"> block containing a Schema.org JobPosting object.
Example
bun run src/cli.ts detail 1234567
bun run src/cli.ts detail 1234567 --format plainResponse shape
{
"id": "1234567",
"url": "https://jobbank.dk/job/1234567/",
"title": "Senior Data Scientist",
"description": "<p>Full HTML description of the role...</p>",
"datePosted": "2026-03-02",
"deadline": "2026-04-12",
"employmentType": ["FULL_TIME"],
"company": {
"name": "Novo Nordisk",
"logo": "https://jobbank.dk/images/dynamic/company/logo/12345/"
},
"location": {
"streetAddress": "",
"city": "Bagsværd",
"postalCode": "",
"country": "DK"
}
}Field details
| Field | Type | Notes |
|---|---|---|
id | string | Numeric job ID (from identifier.value in JSON-LD) |
url | string | Canonical URL of the job posting |
title | string | Job title |
description | string | Full HTML job description body |
datePosted | string | Publication date in ISO 8601 format (YYYY-MM-DD) |
deadline | string \ | null |
employmentType | string[] | Schema.org employment type values, e.g. ["FULL_TIME"] |
company.name | string | Hiring organization name |
company.logo | string \ | null |
location.streetAddress | string | Street address (may be empty) |
location.city | string | City (may be empty for international jobs) |
location.postalCode | string | Postal code (may be empty) |
location.country | string | Country code (may be empty) |
location fields may be empty strings for international jobs or postings that do not specify a physical location.---
Error handling
All errors are written to stderr in JSON format and exit with code 1:
{ "error": "Job not found", "code": "NOT_FOUND" }
{ "error": "Failed to fetch RSS feed: 403 Forbidden", "code": "API_ERROR" }
{ "error": "No JSON-LD found on job page", "code": "PARSE_ERROR" }
{ "error": "--key or at least one filter is required", "code": "MISSING_REQUIRED" }---
Implementation notes
User-Agent
All HTTP requests must include a browser User-Agent header. Without it, Jobbank routes traffic through a bot protection layer that returns invalid responses:
Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36RSS description parsing
The RSS <description> field follows this pattern:
Fuldtidsjob, Graduate/trainee hos Novo Nordisk, Bagsværd (Ansøgningsfrist: 12.04.2026)
Fuldtidsjob hos DTU, Lyngby (Ansøgningsfrist: løbende)Parse strategy: 1. Split on hos — left side is job type(s), right side is Company, Location (Ansøgningsfrist: Deadline) 2. From the right side, extract the parenthetical (Ansøgningsfrist: ...) for deadline 3. Remaining text is Company, Location — split on first , to separate company from location
JSON-LD extraction
On detail pages, find <script type="application/ld+json"> containing "@type": "JobPosting" and parse the JSON. Map fields:
identifier.value→idurl→urltitle→titledescription→description(HTML)datePosted→datePostedvalidThrough→deadline(may be absent →null)employmentType→employmentType(array)hiringOrganization.name→company.namehiringOrganization.logo→company.logojobLocation.address.streetAddress→location.streetAddressjobLocation.address.addressLocality→location.cityjobLocation.address.postalCode→location.postalCodejobLocation.address.addressCountry→location.country
Rate limiting
No explicit rate limits are enforced, but Cloudflare is present. Add a 300–500ms delay between sequential requests (e.g. when fetching total count from HTML in addition to the RSS feed). The search command makes at most 2 requests (RSS + HTML for total count).
import { createCLI } from "@bunli/core"
import { search } from "./commands/search.js"
import { detail } from "./commands/detail.js"
const cli = await createCLI({
name: "jobbank-cli",
version: "1.0.0",
description: "CLI for Akademikernes Jobbank (jobbank.dk) — job search for highly educated candidates",
})
cli.command(search)
cli.command(detail)
await cli.run()
export const BASE_URL = "https://jobbank.dk"
export const USER_AGENT =
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
export function writeError(error: string, code: string): void {
process.stderr.write(JSON.stringify({ error, code }) + "\n")
}
export async function fetchWithUA(url: string): Promise<Response> {
const maxRetries = 6
let delay = 500
for (let attempt = 0; attempt <= maxRetries; attempt++) {
const response = await fetch(url, {
headers: { "User-Agent": USER_AGENT },
})
if (response.status === 429 || response.status >= 500) {
if (attempt === maxRetries) {
throw new Error(`Request failed: ${response.status} ${response.statusText}`)
}
const jitter = Math.floor(Math.random() * 500)
await new Promise((resolve) => setTimeout(resolve, delay + jitter))
delay = Math.min(delay * 2, 5000)
continue
}
return response
}
throw new Error("Request failed after max retries")
}
export interface RssItem {
title: string
description: string
link: string
pubDate: string
}
function extractCdata(xml: string, tag: string): string {
// Try CDATA first
const cdataRe = new RegExp(`<${tag}><\\!\\[CDATA\\[(.*?)\\]\\]><\\/${tag}>`, "s")
const cdataMatch = xml.match(cdataRe)
if (cdataMatch) return cdataMatch[1].trim()
// Plain content
const plainRe = new RegExp(`<${tag}>(.*?)<\\/${tag}>`, "s")
const plainMatch = xml.match(plainRe)
return plainMatch ? plainMatch[1].trim() : ""
}
function extractLink(xml: string): string {
// <link> in RSS can conflict with atom namespace — extract text node after <link>
// Try CDATA variant first
const cdataMatch = xml.match(/<link><!\[CDATA\[(.*?)\]\]><\/link>/s)
if (cdataMatch) return cdataMatch[1].trim()
// Plain link
const plainMatch = xml.match(/<link>(.*?)<\/link>/s)
if (plainMatch) return plainMatch[1].trim()
// Some RSS feeds put the URL as text after <link> without a closing tag (self-closing style)
// Try matching href in atom:link
return ""
}
function parseRssItems(xml: string): RssItem[] {
const items: RssItem[] = []
// Split on <item> boundaries
const itemMatches = xml.matchAll(/<item>([\s\S]*?)<\/item>/g)
for (const match of itemMatches) {
const itemXml = match[1]
const title = extractCdata(itemXml, "title")
const description = extractCdata(itemXml, "description")
const link = extractLink(itemXml)
const pubDate = extractCdata(itemXml, "pubDate") || itemXml.match(/<pubDate>(.*?)<\/pubDate>/)?.[1]?.trim() || ""
items.push({ title, description, link, pubDate })
}
return items
}
export async function rssFetch(params: Record<string, string | string[]>): Promise<RssItem[]> {
const searchParams = new URLSearchParams()
for (const [key, value] of Object.entries(params)) {
if (Array.isArray(value)) {
for (const v of value) {
searchParams.append(key, v)
}
} else {
searchParams.append(key, value)
}
}
const url = `${BASE_URL}/job/rss?${searchParams.toString()}`
const response = await fetchWithUA(url)
if (!response.ok) {
throw new Error(`Failed to fetch RSS feed: ${response.status} ${response.statusText}`)
}
const xml = await response.text()
return parseRssItems(xml)
}
export interface ParsedDescription {
jobType: string
company: string
location: string
deadline: string | null
}
export function parseRssDescription(desc: string): ParsedDescription {
// Format: "JobType hos Company, Location (Ansøgningsfrist: DD.MM.YYYY)"
// or: "JobType hos Company, Location (Ansøgningsfrist: løbende)"
// or multiple types: "Fuldtidsjob, Graduate/trainee hos Company, Location (Ansøgningsfrist: ...)"
let jobType = ""
let company = ""
let location = ""
let deadline: string | null = null
const hosIdx = desc.indexOf(" hos ")
if (hosIdx === -1) {
// Can't parse — return desc as company
return { jobType: "", company: desc, location: "", deadline: null }
}
jobType = desc.substring(0, hosIdx).trim()
let rest = desc.substring(hosIdx + 5) // skip " hos "
// Extract deadline from parenthetical at the end
const deadlineMatch = rest.match(/\(Ans[øo]gningsfrist:\s*(.*?)\)\s*$/)
if (deadlineMatch) {
const deadlineStr = deadlineMatch[1].trim()
if (deadlineStr.toLowerCase() === "løbende" || deadlineStr.toLowerCase() === "lobende") {
deadline = null
} else {
deadline = deadlineStr
}
// Remove the deadline portion from rest
rest = rest.substring(0, deadlineMatch.index).trim()
}
// rest is now "Company, Location"
// Split on first ", " to get company and location
const firstComma = rest.indexOf(", ")
if (firstComma !== -1) {
company = rest.substring(0, firstComma).trim()
location = rest.substring(firstComma + 2).trim()
} else {
company = rest.trim()
location = ""
}
return { jobType, company, location, deadline }
}
export function extractJobIdFromUrl(url: string): string {
// URL format: https://jobbank.dk/job/{id}/{company-slug}/{title-slug}
const match = url.match(/\/job\/(\d+)\//)
return match ? match[1] : ""
}
import { join } from "path";
const CLI_PATH = join(import.meta.dir, "../src/cli.ts");
export interface CLIResult {
stdout: string;
stderr: string;
exitCode: number;
}
export async function runCLI(args: string[]): Promise<CLIResult> {
const proc = Bun.spawn(["bun", "run", CLI_PATH, ...args], {
stdout: "pipe",
stderr: "pipe",
});
const [stdout, stderr, exitCode] = await Promise.all([
new Response(proc.stdout).text(),
new Response(proc.stderr).text(),
proc.exited,
]);
return { stdout: stdout.trim(), stderr: stderr.trim(), exitCode };
}
export function parseJSON<T = unknown>(result: CLIResult): T {
if (result.exitCode !== 0) {
throw new Error(
`CLI exited with code ${result.exitCode}. stderr: ${result.stderr}`
);
}
try {
return JSON.parse(result.stdout) as T;
} catch {
throw new Error(
`Failed to parse JSON. stdout: ${result.stdout}\nstderr: ${result.stderr}`
);
}
}
Jobbank.dk URL Reference
Complete reference for constructing search URLs on Akademikernes Jobbank.
Base URL
https://jobbank.dk/job/Full URL Pattern
https://jobbank.dk/job/?key={search}&antikey={exclude}&cvtype={type_id}&udd={edu_id}&amt={location_id}&erf={workarea_id}&branche={industry_id}&andet={suitability_id}&virk={company}&fjernarbejde={remote}&oprettet={YYYY-MM-DD}&page={num}Multiple values: Repeat the parameter name
cvtype=3&cvtype=13&amt=2&amt=3Query Parameters
| Parameter | Description | Format |
|---|---|---|
key | Search keywords | URL-encoded string (use + for spaces) |
antikey | Exclude keywords | URL-encoded string |
cvtype | Job type ID | Numeric ID (see table below) |
udd | Education area ID | Numeric ID (see table below) |
amt | Location/region ID | Numeric ID (see table below) |
erf | Work area ID | Numeric ID (see table below) |
branche | Industry ID | Numeric ID (see table below) |
andet | Suitability ID | Numeric ID (see table below) |
virk | Company name | URL-encoded string |
fjernarbejde | Remote work | helt (fully) or delvist (partially) |
oprettet | Posted since date | YYYY-MM-DD format |
page | Page number | Integer (1-indexed) |
Filter Tables
Job Type (cvtype)
| Label | ID |
|---|---|
| Fuldtidsjob (Full-time) | 3 |
| Graduate/trainee | 6 |
| Deltidsjob (Part-time) | 13 |
| Vikariat (Temporary) | 8 |
| Ph.d. & Postdoc | 12 |
| Freelance | 11 |
| Iværksætterprojekt (Entrepreneurship) | 15 |
| Event | 14 |
| Praktikplads (Internship) | 9 |
| Studiejob (Student job) | 4 |
| Studieprojekt/speciale (Study project/thesis) | 5 |
Education Area (udd)
| Label | ID |
|---|---|
| Administration | 20 |
| Anlæg/Byggeri/Konstruktion (Construction) | 43 |
| Arkitektur/Kunst/Design (Architecture/Art/Design) | 29 |
| Elektro/Telekommunikation (Electronics/Telecom) | 47 |
| Fødevarer/Veterinær (Food/Veterinary) | 32 |
| Human Resources | 38 |
| Humaniora (Humanities) | 28 |
| IT | 24 |
| Jura (Law) | 23 |
| Kemi/Biotek/Materialer (Chemistry/Biotech/Materials) | 44 |
| Klima/Miljø/Energi (Climate/Environment/Energy) | 45 |
| Landbrug/Natur (Agriculture/Nature) | 37 |
| Marketing/Business | 22 |
| Maskin/Design (Mechanical/Design) | 48 |
| Matematik/Fysik/Nano (Math/Physics/Nano) | 46 |
| Medicinal/Sundhed (Medicine/Health) | 31 |
| Naturvidenskab (Natural Sciences) | 30 |
| Organisation/Ledelse (Organization/Management) | 41 |
| Produktion/Logistik/Transport (Production/Logistics/Transport) | 35 |
| Samfundsvidenskab (Social Sciences) | 34 |
| Sprog/Media/Kommunikation (Language/Media/Communication) | 25 |
| Teknik/Teknologi (Engineering/Technology) | 33 |
| Undervisning/Pædagogik (Education/Pedagogy) | 26 |
| Økonomi/Revision (Economics/Accounting) | 21 |
Location/Region (amt)
| Label | ID |
|---|---|
| Storkøbenhavn (Greater Copenhagen) | 2 |
| Nordsjælland (North Zealand) | 3 |
| Østsjælland (East Zealand) | 14 |
| Vestsjælland (West Zealand) | 4 |
| Sydsjælland & Øer (South Zealand & Islands) | 5 |
| Fyn (Funen) | 13 |
| Sønderjylland (South Jutland) | 12 |
| Sydvestjylland (Esbjerg) | 11 |
| Vestjylland (West Jutland) | 9 |
| Sydøstjylland (Southeast Jutland) | 10 |
| Midtjylland (Central Jutland) | 7 |
| Østjylland (Aarhus) | 8 |
| Nordjylland (North Jutland) | 6 |
| Bornholm | 20 |
| Øresundsregionen (Øresund Region) | 21 |
| Grønland & Færøerne (Greenland & Faroe Islands) | 22 |
| Udlandet - Sverige (Abroad - Sweden) | 23 |
| Udlandet - Norge (Abroad - Norway) | 24 |
| Udlandet - øvrige (Abroad - other) | 19 |
Work Area (erf)
| Label | ID |
|---|---|
| Administration | 20 |
| Arkitektur/Design (Architecture/Design) | 38 |
| Bank/Forsikring (Banking/Insurance) | 22 |
| Data/Analyse (Data/Analysis) | 43 |
| Eksport (Export) | 47 |
| Forskning/Udvikling (Research/Development) | 41 |
| Human Resources | 28 |
| Indkøb (Procurement) | 49 |
| Internet/Multimedia | 34 |
| IT-Hardware | 32 |
| IT-Netværk/Telekomm. (IT-Network/Telecom) | 33 |
| IT-Software | 31 |
| Jura (Law) | 24 |
| Kommunikation/Media/SoMe (Communication/Media/Social Media) | 35 |
| Konstruktion/Beregning (Construction/Calculation) | 46 |
| Kunst/Kultur (Art/Culture) | 37 |
| Ledelse/Planlægning (Management/Planning) | 26 |
| Marketing/Reklame (Marketing/Advertising) | 29 |
| Medicinal/Sundhed (Medicine/Health) | 40 |
| Naturvidenskab (Natural Sciences) | 45 |
| Organisation/Forening (Organization/Association) | 23 |
| Politik/Samfund (Politics/Society) | 52 |
| Produktion (Production) | 44 |
| Projektledelse (Project Management) | 27 |
| Rådgivning/Support (Consulting/Support) | 50 |
| Salg (Sales) | 30 |
| Socialvæsen (Social Services) | 39 |
| Teknik (Engineering) | 42 |
| Topledelse (Executive Management) | 25 |
| Transport/Logistik (Transport/Logistics) | 48 |
| Undervisning (Education) | 36 |
| Økonomi/Forvaltning (Finance/Administration) | 21 |
Industry (branche)
| Label | ID |
|---|---|
| Advokat/Revision (Law/Accounting) | 10359 |
| Byggeri/Anlæg (Construction) | 11669 |
| Elektronik/Maskin (Electronics/Machinery) | 11634 |
| Fagforeninger/A-kasser/Pensionskasser (Unions/Unemployment funds/Pension funds) | 16791 |
| Finans/Forsikring/Pension (Finance/Insurance/Pension) | 10358 |
| Forskning/Uddannelse (Research/Education) | 10442 |
| Fødevarer/Dagligvarer (Food/Groceries) | 15407 |
| Handel/Service (Trade/Service) | 10364 |
| IT/Tele | 10331 |
| Klima/Energi/Forsyning (Climate/Energy/Utilities) | 17209 |
| Kommuner (Municipalities) | 16826 |
| Kultur/Medier/Underholdning (Culture/Media/Entertainment) | 10341 |
| Medicinal/Biotek/Kemi (Pharmaceuticals/Biotech/Chemistry) | 10333 |
| Papir/Møbel/Materialer (Paper/Furniture/Materials) | 10363 |
| Regioner/Sundhed/Socialvæsen (Regions/Health/Social services) | 11626 |
| Rådgivning/Konsulentservice (Consulting services) | 15586 |
| Stat/Politik/Samfund (Government/Politics/Society) | 10362 |
| Transport | 10440 |
| Vikar/Rekruttering (Temp/Recruitment) | 12450 |
Suitability (andet)
| Label | ID |
|---|---|
| Nyuddannede (New graduates) | 2 |
| Personer med international baggrund (People with international background) | 4 |
| Erfarne (Experienced) | 5 |
Remote Work (fjernarbejde)
| Label | Value |
|---|---|
| Helt hjemmearbejde (Fully remote) | helt |
| Delvist hjemmearbejde (Partially remote) | delvist |
Job Detail URL Pattern
https://jobbank.dk/job/{id}/{company-slug}/{title-slug}/Example: https://jobbank.dk/job/12345/novo-nordisk/senior-data-scientist/
RSS Feed URL
Every search has an RSS equivalent:
https://jobbank.dk/job/rss?{same_query_params}Example:
https://jobbank.dk/job/rss?key=python&amt=2&cvtype=3Pagination
- Pages are 1-indexed (first page is
page=1or no page parameter) - Approximately 20 results per page
- Use
&page=2,&page=3, etc. to navigate
Job Card Extraction
CSS Selectors
| Element | Selector |
|---|---|
| Job card container | div.job-item |
| Job ID | div.job-item[name] (use getAttribute("name")) |
| Job title | .job-header |
| Job type/company/location | .job-teaser |
| Job description excerpt | .job-description |
| Date updated | .job-date-updated |
| Application deadline | .job-date-application |
| Job detail link | a[href^="/job/"] (prepend https://jobbank.dk) |
Working Extraction Code
async page => {
const jobs = await page.evaluate(() => {
return Array.from(document.querySelectorAll("div.job-item")).map(item => {
const id = item.getAttribute("name");
const link = item.querySelector("a[href^='/job/']");
const url = link ? "https://jobbank.dk" + link.getAttribute("href") : null;
const header = item.querySelector(".job-header");
const teaser = item.querySelector(".job-teaser");
const desc = item.querySelector(".job-description");
const dateUpdated = item.querySelector(".job-date-updated");
const deadline = item.querySelector(".job-date-application");
return {
id,
title: header ? header.textContent.trim() : null,
teaser: teaser ? teaser.textContent.trim() : null,
description: desc ? desc.textContent.trim() : null,
url,
dateUpdated: dateUpdated ? dateUpdated.textContent.trim() : null,
deadline: deadline ? deadline.textContent.trim() : null,
};
});
});
return JSON.stringify(jobs, null, 2);
}Examples
Simple Keyword Search
https://jobbank.dk/job/?key=data+scientistFull-Time IT Jobs in Copenhagen
https://jobbank.dk/job/?key=developer&cvtype=3&udd=24&amt=2Remote Software Development Positions
https://jobbank.dk/job/?erf=31&fjernarbejde=heltGraduate/Trainee Positions in Multiple Cities
https://jobbank.dk/job/?cvtype=6&amt=2&amt=8Project Management Jobs Suitable for New Graduates
https://jobbank.dk/job/?key=project+manager&erf=27&andet=2Jobs in IT/Telecom Industry, Excluding Senior Positions
https://jobbank.dk/job/?key=developer&antikey=senior&branche=10331Data Analysis Jobs Posted in Last 7 Days
https://jobbank.dk/job/?key=data+analysis&erf=43&oprettet=2026-02-03Second Page of Python Jobs in Aarhus
https://jobbank.dk/job/?key=python&amt=8&page=2Specific Company Search (Novo Nordisk)
https://jobbank.dk/job/?virk=novo+nordiskPh.D. and Postdoc Positions in Natural Sciences
https://jobbank.dk/job/?cvtype=12&udd=30Notes
- URL encoding: Use
+or%20for spaces in text parameters (key,antikey,virk) - Multiple filters: Repeat parameter names for multiple values in same category
- Date format: Use ISO format
YYYY-MM-DDforoprettetparameter - Remote work: Only two values:
helt(fully) ordelvist(partially) - Case sensitivity: Filter IDs are case-sensitive, use exact values from tables
- Empty results: Invalid filter combinations may return zero results
- RSS monitoring: Use RSS feeds for automated job monitoring and alerts