# How to Integrate New Danish Job Portals into the AI Job Search System

> Integrate new Danish job portals into the AI job search system easily. Add declarative markdown skills, query templates, and register in the CLI manifest with no core code changes.

- Repository: [Mads Lorentzen/ai-job-search](https://github.com/MadsLorentzen/ai-job-search)
- Tags: how-to-guide
- Published: 2026-07-09

---

**You can integrate new Danish job portals into the AI Job Search system by creating a declarative markdown skill file in `/.claude/skills/job-scraper/`, adding query templates to [`search-queries.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/search-queries.md), and registering the portal in the CLI manifest—no core code changes required.**

The AI Job Search repository (MadsLorentzen/ai-job-search) uses a modular, skill-based architecture that lets you extend job scraping capabilities to any Danish portal without modifying the core engine. By leveraging declarative markdown definitions found in `/.claude/skills/job-scraper/` and standardized query templates, you can teach the system to crawl new job boards in minutes while maintaining compatibility with downstream agents like the cover-letter generator and CV matcher.

## The Modular Job Scraper Architecture

The integration workflow relies on a **zero-code onboarding** philosophy where portals are defined declaratively rather than through imperative code. According to the repository source code, the system processes Danish job portals through four distinct stages:

1. **Portal Discovery** – The CLI reads markdown skill files containing base URLs, authentication requirements, and pagination rules.
2. **Query Construction** – Templates translate generic search parameters into portal-specific URL parameters.
3. **HTML Parsing** – CSS selectors extract structured data from raw job listings.
4. **Schema Normalization** – All results conform to a unified JSON schema (`title`, `company`, `location`, `salary`, `url`) that downstream agents consume.

This architecture lives primarily in the `/.claude/skills/job-scraper/` directory, with CLI tooling located at `/.agents/skills/job-scraper/cli/`.

## Step-by-Step Integration Guide

Follow these three steps to add support for any Danish job portal, from large national sites to niche industry boards.

### Step 1: Create the Portal Definition

Create a new markdown file in `/.claude/skills/job-scraper/` following the structure defined in [`SKILL.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/SKILL.md). This file acts as the single source of truth for how the system interacts with the portal.

```markdown

# DanishJobPortal – Example

**name:** DanishJobPortal
**base_url:** https://www.danishjobs.dk/search
**method:** GET

## Parameters

| key      | description                     |
|----------|---------------------------------|
| title    | Job title keyword               |
| city     | City name (e.g., Copenhagen)    |
| page     | Page number for pagination       |

## Pagination

type: offset
parameter: page
start: 1
step: 1

## Result extraction (CSS selectors)

listing: div.job-card
title:   h2.title
company: span.company
location: span.location
salary: span.salary
url: a.details::attr(href)

```

The **pagination** section supports multiple strategies including `offset`, `cursor`, and `infinite_scroll`. For complex pagination requiring JavaScript execution, place a helper file alongside the markdown definition.

### Step 2: Add Query Templates

Extend [`/.claude/skills/job-scraper/search-queries.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main//.claude/skills/job-scraper/search-queries.md) with portal-specific query syntax. This file maps abstract search concepts (title, city, remote status) to the URL parameters each Danish portal expects.

```markdown

## DanishJobPortal – Generic query

title={{title}}&city={{city}}&page={{page}}

```

The double-brace syntax (`{{parameter}}`) enables the CLI to interpolate user-provided values at runtime. For Danish portals with unique parameter naming (e.g., `stilling` instead of `title` or `by` instead of `city`), simply adjust the right-hand side of the template while keeping the generic placeholder names consistent.

### Step 3: Register with the CLI

Register the new skill in the CLI manifest so the system can discover it dynamically. Update [`/.agents/skills/job-scraper/cli/package.json`](https://github.com/MadsLorentzen/ai-job-search/blob/main//.agents/skills/job-scraper/cli/package.json) (or the repository's top-level [`manifest.json`](https://github.com/MadsLorentzen/ai-job-search/blob/main/manifest.json) if applicable) to include the new portal definition. No JavaScript code changes are required—the CLI loads markdown definitions at runtime based on the manifest entries.

## Testing Your Danish Portal Integration

Verify the integration using the built-in CLI client. From the repository root:

```bash
npm install
node .agents/skills/job-scraper/cli/cli.js search \
    --portal DanishJobPortal \
    --title "data scientist" \
    --city "Aarhus"

```

The CLI executes the following workflow:

1. Loads the portal definition from [`/.claude/skills/job-scraper/jobportal-dk-example.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main//.claude/skills/job-scraper/jobportal-dk-example.md).
2. Retrieves the query template from [`search-queries.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/search-queries.md).
3. Interpolates parameters (`title="data scientist"`, `city="Aarhus"`).
4. Performs an HTTP GET to the constructed URL.
5. Parses HTML using the defined CSS selectors.
6. Outputs normalized JSON:

```json
[
  {
    "title": "Data Scientist",
    "company": "Nordic Analytics",
    "location": "Aarhus",
    "salary": "65000–80000 DKK",
    "url": "https://www.danishjobs.dk/job/12345"
  }
]

```

## Downstream Agent Compatibility

Because the job scraper normalizes all Danish portal results into a consistent JSON schema, downstream agents immediately gain support for your new integration. The **cover-letter generator**, **CV matcher**, and **interview-prep** skills (located in `/.claude/skills/`) consume this standardized format without requiring portal-specific logic.

When you add a new Danish job portal, the entire application workflow—from search to tailored cover letter generation—automatically becomes available for that source.

## Summary

- **Zero-code integration**: Create markdown files in `/.claude/skills/job-scraper/` rather than writing Python or JavaScript.
- **Three files to modify**: Portal definition ([`SKILL.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/SKILL.md) template), query templates ([`search-queries.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/search-queries.md)), and CLI manifest ([`package.json`](https://github.com/MadsLorentzen/ai-job-search/blob/main/package.json)).
- **Standardized output**: All portals produce identical JSON schemas, ensuring compatibility with cover-letter and CV agents.
- **Template-based queries**: Use `{{parameter}}` syntax to bridge generic search terms with portal-specific URL structures.
- **CSS selector parsing**: Define extraction rules declaratively in the portal markdown file.

## Frequently Asked Questions

### Do I need programming skills to add a new Danish job portal?

No programming is required for standard integrations. You only need to create markdown files following the templates in [`/.claude/skills/job-scraper/SKILL.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main//.claude/skills/job-scraper/SKILL.md) and [`search-queries.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/search-queries.md). Only portals with complex authentication mechanisms or JavaScript-rendered content require small custom JavaScript helpers placed alongside the skill file.

### What is the required file structure for portal definitions?

Each portal requires a single markdown file placed in `/.claude/skills/job-scraper/` containing YAML-like frontmatter for metadata (name, base_url, method) and sections for Parameters, Pagination, and Result extraction with CSS selectors. Reference the canonical template in [`SKILL.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/SKILL.md) for the exact schema.

### How does the system handle different URL parameters for Danish job sites?

The [`search-queries.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/search-queries.md) file acts as a translation layer. You define how generic parameters like `title` and `city` map to portal-specific query strings (e.g., `q=software&l=Copenhagen` vs `stilling=software&by=København`). The CLI substitutes actual values into these templates at runtime using the `{{parameter}}` syntax.

### Where do I register the portal so the CLI recognizes it?

Register the new skill in [`/.agents/skills/job-scraper/cli/package.json`](https://github.com/MadsLorentzen/ai-job-search/blob/main//.agents/skills/job-scraper/cli/package.json) or the repository's top-level skill manifest. The CLI dynamically loads all portal definitions listed in this manifest at runtime, requiring no changes to the core CLI code itself.