# Jobindex.dk Scraper (`corvuslab/jobindex-scraper`) Actor

Turn jobindex.dk — Denmark's largest job board — into clean, structured job data. Filter by keyword, location, employment type and category, monitor for new postings, and get alerts on Slack, Telegram, Discord or webhook. Company info, salaries and apply links included.

- **URL**: https://apify.com/corvuslab/jobindex-scraper.md
- **Developed by:** [Corvuslab](https://apify.com/corvuslab) (community)
- **Categories:** Jobs, Automation, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.99 / 1,000 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp.md).

If your project is in a different language, use the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).


# README

## Jobindex.dk Scraper

Extract job listings from **[jobindex.dk](https://www.jobindex.dk)** — Denmark's
largest job portal — by keyword and location. Get clean, structured job data:
titles, companies, locations (with coordinates), posting dates, application
deadlines and apply links — ready to export as JSON, CSV or Excel, or pull
straight into your app via the API.

Fast and reliable: this scraper reads the structured data jobindex already
embeds in each search page, so it doesn't depend on a headless browser.

### What does Jobindex.dk Scraper do?

Give it a search keyword (e.g. `python`, `sygeplejerske`, `data scientist`) and,
optionally, a location, and it returns every matching job as a structured record.
For each job you get the title, company and its profile/metadata, a normalised
location with latitude/longitude, posting and deadline dates, and the direct
application URL. Turn on **Fetch job details** to also pull the job summary and
full description text.

> New to Apify? You can sign up for free and use the included monthly platform
> credit to try this Actor.

### Key features

- 🔍 **Keyword & location search** of jobindex.dk, with pagination handled for you
- 🧱 **Structured output** — clean fields, no messy HTML to post-process
- 🏢 **Company metadata** included for free: name, profile URL, homepage, career page, reviews link, follower count, logo
- 📍 **Geocoded locations** — city, postal code, full address, latitude & longitude
- 📝 **Optional full descriptions & salary** — enable *Fetch job details* for the summary, full description and any stated salary (DKK)
- 🔁 **Batch searches** — run several keywords in one go, automatically deduplicated
- ♻️ **Incremental mode** — recurring runs return only what changed (new / updated / expired jobs)
- 🔔 **Notifications** — send a run summary to Telegram, Slack, Discord or any webhook
- 🗓️ **Dates & deadlines** — posted date, last-seen date, application deadline
- ⏱️ **Recency control** — sort by newest first and/or limit to jobs from the last N days
- 🌍 **Remote flag** — see which jobs allow working from home
- 🤖 **Compact & Markdown modes** — lean, LLM-friendly output when you need it
- 💸 **Pay per result** — only pay for what you actually scrape

### How to use Jobindex.dk Scraper (no code)

1. Click **Try for free** / open the Actor in **Apify Console**.
2. Enter a keyword in the **Search query** field (e.g. `python`).
3. *(Optional)* Add a **Location** such as `København` or `Aarhus`.
4. Set **Max results per query** (default 25).
5. *(Optional)* Enable **Fetch job details** to include full descriptions.
6. Click **Start**. When the run finishes, preview the results and **export** them
   as JSON, CSV or Excel from the **Output** tab.

You can also run it on a **schedule** (e.g. every morning) using Apify Schedules,
or connect it to Google Sheets, Zapier, Make and others via Apify integrations.

### Input

| Field | Type | Default | Description |
| --- | --- | --- | --- |
| `query` | string | — | A search query (e.g. `python`, `sygeplejerske`). Supports search operators — see below. |
| `queries` | array | — | Run several **separate** searches in one run. Results are deduplicated by job ID. |
| `location` | string | — | Optional city/area filter (e.g. `København`, `Aarhus`). |
| `employmentType` | array | — | Filter by employment type (multi-select). See **Advanced filters**. |
| `workPlace` | array | — | Filter by workplace: `onsite`, `remote`, `hybrid`. |
| `workHours` | array | — | Filter by `fulltime` / `parttime`. |
| `subCategories` | array | — | Filter by one or more job categories. See **Advanced filters**. |
| `maxResults` | integer | `25` | Max jobs **per query**. `0` = unlimited (hard-capped at 5000). |
| `sortBy` | string | `relevance` | Order results by `relevance` or by `date` (newest first). |
| `maxAgeDays` | integer | — | Only include jobs posted within the last N days. |
| `fetchJobDetail` | boolean | `false` | Also open each job's page for a summary and full description text. |
| `descriptionFormat` | string | `all` | Which description formats to include: `all`, `text`, `html`, `markdown`. |
| `descriptionMaxLength` | integer | `0` | Truncate description/summary to N characters (`0` = no limit). |
| `compact` | boolean | `false` | Return only core fields — handy for AI/agent workflows. |
| `excludeEmptyFields` | boolean | `false` | Remove null/empty fields from each record. |
| `incrementalMode` | boolean | `false` | On recurring runs, only return jobs that changed. See **Incremental mode**. |
| `emitUnchanged` | boolean | `false` | In incremental mode, also return jobs with no changes (`UNCHANGED`). |
| `emitExpired` | boolean | `false` | In incremental mode, also return jobs that disappeared (`EXPIRED`). |
| `skipReposts` | boolean | `false` | In incremental mode, suppress re-posted ads (a new listing matching an expired one). |
| `stateKey` | string | auto | Identifier for incremental state. Blank = derived from your search settings. |
| `telegramToken` / `telegramChatId` | string | — | Send a run summary to Telegram. See **Notifications**. |
| `slackWebhookUrl` | string | — | Post a run summary to a Slack channel. |
| `discordWebhookUrl` | string | — | Post a run summary to a Discord channel. |
| `webhookUrl` | string | — | POST `{metadata, items}` to any URL after each run. |
| `webhookHeaders` | object | — | Custom HTTP headers for the generic webhook. |
| `notificationLimit` | integer | `10` | Max jobs listed per chat message (1–20). |
| `notifyOnlyChanges` | boolean | `false` | With incremental mode, only notify about new/updated jobs. |

#### Example input

```json
{
    "query": "python",
    "location": "København",
    "maxResults": 50,
    "fetchJobDetail": true
}
````

Batch search across several keywords:

```json
{
    "queries": ["data scientist", "machine learning", "data engineer"],
    "maxResults": 100
}
```

#### Search operators

The `query` (and each entry in `queries`) supports jobindex's search operators:

| Operator | Example | Meaning |
| --- | --- | --- |
| space | `python ai` | Match **any** of the words (broad) |
| `+` | `+python +ai` | **Require** every `+` word (AND) — only jobs matching both |
| `-` | `python -senior` | **Exclude** jobs containing the word |
| `"..."` | `"python developer"` | Match the **exact phrase** |

Tip: prefix **every** term with `+` to require all of them. `python ai` is an
"either" search; `+python +ai` returns only jobs that mention both.

> **`query` vs `queries`:** `queries` runs each entry as its *own* search and
> merges the results (a union). To require several words in one search, use the
> `+` operator in a single `query`, e.g. `+python +ai`.

#### Advanced filters

All filters are multi-select — pick one or more values; leaving a filter empty
means "any". Within a filter, multiple values broaden the search (OR).

| Filter | Accepted values |
| --- | --- |
| `employmentType` | permanent, temporary, student, apprentice, graduate, internship, flexJob, volunteer, youth, freelance, hourly |
| `workPlace` | onsite, remote, hybrid — *fully remote jobs are uncommon on jobindex; hybrid is much more frequent* |
| `workHours` | fulltime, parttime |
| `subCategories` | 80+ job categories, e.g. Software development & programming, IT operations & support, Retail, Nurse & midwife, Marketing, Finance & accounting, Research, Construction & civil works — see the dropdown in the Console for the full list |

```json
{
    "query": "developer",
    "location": "København",
    "employmentType": ["permanent", "graduate"],
    "workPlace": ["hybrid"],
    "workHours": ["fulltime"],
    "subCategories": ["Software development & programming"]
}
```

### Incremental mode (change tracking)

Turn a one-off scrape into a **job monitor**. Enable `incrementalMode` and the
Actor remembers what it saw on previous runs, so each recurring run returns
**only the jobs that changed** — perfect for scheduled runs and alerts.

Every record gets a `changeType` field:

| `changeType` | Meaning | Returned by default? |
| --- | --- | --- |
| `NEW` | Seen for the first time | ✅ Yes |
| `UPDATED` | Content changed since last run (title, deadline, location…) | ✅ Yes |
| `REAPPEARED` | Was gone, now back on the board | ✅ Yes |
| `UNCHANGED` | No change since last run | Only if `emitUnchanged` |
| `EXPIRED` | Disappeared since last run | Only if `emitExpired` |

How it works:

- **First run builds a baseline** — every job is `NEW` and the state is saved.
  Subsequent runs emit only the differences.
- **State is remembered between runs** and keyed by `stateKey`. Leave it blank to
  derive one automatically from your search settings, or set a fixed value to keep
  one monitor's state stable even if you tweak the query. Use distinct keys to run
  several independent monitors.
- **Reposts:** a brand-new listing whose content matches a previously expired job
  is flagged with `isRepost`, `repostOfId` and `repostDetectedAt`. Set
  `skipReposts` to drop them.
- **Expiry needs a complete scrape.** To reliably detect `EXPIRED` jobs, set
  **Max results to `0`** (unlimited). If `maxResults` cuts the results short, the
  jobs beyond the cut-off can't be told apart from vanished ones, so expiry
  detection is safely skipped for that run.

```json
{
    "query": "python developer",
    "location": "København",
    "incrementalMode": true,
    "emitExpired": true,
    "maxResults": 0
}
```

Pair this with **Apify Schedules** (e.g. run every morning) and **notifications**
(below) to get alerted only about new and changed jobs.

### Notifications

Get a summary of each run pushed straight to your channels — great with
incremental mode and a schedule. All channels are optional; configure any
combination.

| Channel | What you provide | Where it posts |
| --- | --- | --- |
| **Telegram** | `telegramToken` (from @BotFather) + `telegramChatId` | Your chat/channel |
| **Slack** | `slackWebhookUrl` (an incoming webhook) | That webhook's channel |
| **Discord** | `discordWebhookUrl` (a channel webhook) | That channel |
| **Webhook** | `webhookUrl` (+ optional `webhookHeaders`) | A JSON `POST` to your URL |

- **Chat channels** (Telegram/Slack/Discord) receive a readable digest of up to
  `notificationLimit` jobs (1–20), each tagged with its `changeType` in
  incremental mode. They only fire when there's something to report.
- **The generic webhook** receives a JSON `{ "metadata": {…}, "items": [...] }`
  body after **every** run (even zero results), so you can wire the Actor into
  any tool. Add `webhookHeaders` (e.g. an `Authorization` header) if your
  endpoint needs them.
- **`notifyOnlyChanges`** (with incremental mode) limits alerts to `NEW` and
  `UPDATED` jobs — no pings for unchanged listings.

> Tokens and webhook URLs are stored as **secret** inputs (encrypted, hidden in
> the UI and logs). WhatsApp isn't supported out of the box — bridge to it via
> the generic webhook if you need it.

```json
{
    "query": "python developer",
    "incrementalMode": true,
    "notifyOnlyChanges": true,
    "maxResults": 0,
    "slackWebhookUrl": "https://hooks.slack.com/services/…"
}
```

### Output

Each item in the dataset is one job. Every record includes the core job fields
(`title`, `url`, `applyUrl`, ids and status flags), company metadata (name,
profile URL, homepage, career page, reviews link, followers, logo), a geocoded
location (city, postal code,
address, latitude/longitude), and dates & deadlines. Ads with a recruitment
video add a few video fields, and enabling **Fetch job details** adds the job
summary, full description (as text, HTML and/or Markdown) and any stated salary
(`salaryMin` / `salaryMax` / `salaryPeriod` in DKK — often empty, since Danish
ads rarely list pay). In **incremental
mode** each record also carries a `changeType` (and repost fields). The exact
field list and types are in the Console's **Output** tab; here's a full example
record:

```json
{
    "jobId": "h1674866",
    "title": "Python Software Developer",
    "company": "Minerva Imaging ApS",
    "companyId": 58277,
    "companyUrl": "https://www.jobindex.dk/virksomhed/58277/minerva-imaging-aps",
    "companyHomepage": "https://www.minervaimaging.com",
    "companyFollowers": 103,
    "companyLogo": "https://www.jobindex.dk/img/logo/MinervaImaging_logo.png",
    "companyCareerUrl": "https://www.minervaimaging.com/careers",
    "companyRatingUrl": "https://www.jobindex.dk/virksomhed/58277/minerva-imaging-aps#evalueringer",
    "location": "Ølstykke",
    "city": "Ølstykke",
    "zipCode": "3650",
    "address": "Lyshøjvej 21, 3650 Ølstykke",
    "latitude": 55.7683518,
    "longitude": 12.1414453,
    "isRemote": false,
    "url": "https://www.jobindex.dk/vis-job/h1674866",
    "applyUrl": "https://minervaimaging.career.emply.com/apply/python-software-developer/kjb2r6/en",
    "quickApplyAvailable": false,
    "postedDate": "2026-06-16",
    "lastSeenDate": "2026-07-13",
    "deadline": "2026-07-31T21:59:59Z",
    "deadlineAsap": false,
    "hasVideo": true,
    "videoThumbnail": "https://www.jobindex.dk/img/logo/MinervaImaging_logo.png",
    "videoUrl": "https://www.jobindex.dk/c?t=h1674866&ctx=w&...",
    "searchQuery": "python",
    "scrapedAt": "2026-06-18T20:37:58Z",
    "summary": "Are you passionate about robust software design …",
    "description": "Full job description text … (only with Fetch job details enabled)",
    "salaryMin": 40000,
    "salaryMax": 48000,
    "salaryPeriod": "monthly",
    "salaryCurrency": "DKK"
}
```

**Notes**

- Some jobs are hosted on external career sites, so the full `description` may be
  empty — `applyUrl` always links to the live ad.
- With **`compact`** enabled, each record is reduced to: `jobId`, `title`,
  `company`, `location`, `isRemote`, `postedDate`, `deadline`, `url`, `applyUrl`,
  `searchQuery`.
- With **`excludeEmptyFields`** enabled, `null`/empty fields are omitted entirely.

### Using the API

You can run this Actor from your own code. Example with the Apify Python client:

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_API_TOKEN>")

run_input = {
    "query": "software engineer",
    "location": "København",
    "maxResults": 25,
}

run = client.actor("corvuslab/jobindex-scraper").call(run_input=run_input)

print("Results: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)
```

The Actor also works with the JavaScript/TypeScript client, the Apify CLI and the
REST API.

### Pricing

This Actor uses the **pay-per-event** pricing model:

| Event | Price |
| --- | --- |
| Run start | $0.01 |
| Per job result | $0.002 |

Example costs:

| Results | Cost |
| --- | --- |
| 10 | $0.03 |
| 25 | $0.06 |
| 100 | $0.21 |
| 500 | $1.01 |

You only pay for what you scrape, and runs respect your account's charging limit.

### Use cases

- **Job market research** — track demand for roles, skills or industries in Denmark.
- **Recruitment & sourcing** — monitor new openings and competitor hiring.
- **Job board aggregation** — feed listings into your own site, ATS or CRM.
- **AI / LLM pipelines** — supply clean, structured job data to your models or agents.
- **Location-based alerts** — watch for jobs in a specific city or area.
- **Company analysis** — follow which companies are actively hiring.

### FAQ

**How many results can I get?**
Up to 5000 per query. Set `maxResults` to `0` for "as many as available" (capped at 5000).

**Can I search several keywords at once?**
Yes — use the `queries` list. Results are deduplicated across all queries.

**Can I get full job descriptions?**
Yes — enable **Fetch job details**. Note that ads hosted on external career sites may not expose full text.

**Does it extract salary?**
When **Fetch job details** is on, the Actor pulls any salary stated in the ad
(`salaryMin`, `salaryMax`, `salaryPeriod` in DKK). Most Danish listings don't
publish a salary, so these fields are often empty — that's normal.

**Can I run it automatically?**
Yes — use Apify Schedules to run it on a recurring basis, and Apify integrations
(Google Sheets, Zapier, Make, webhooks) to deliver the results. Combine this with
**incremental mode** so each scheduled run only returns new and changed jobs.

**How do I get only new jobs on each run?**
Enable `incrementalMode`. The first run saves a baseline; after that, runs return
only `NEW`, `UPDATED` and `REAPPEARED` jobs. Set `maxResults` to `0` and
`emitExpired` to also track jobs that disappear.

**Can it notify me when new jobs appear?**
Yes — add a Telegram, Slack, Discord or generic webhook target under
**Notifications**, and combine with incremental mode + `notifyOnlyChanges` so you
only get pinged about new and updated jobs. WhatsApp isn't built in; use the
generic webhook to bridge to it.

**Is scraping jobindex.dk legal?**
This Actor collects publicly available listing data. You are responsible for
using it in line with jobindex.dk's terms and applicable laws (including GDPR).
Scrape responsibly.

**Found a bug or need a feature?**
Open an issue on the Actor's page — feedback is welcome.

# Actor input Schema

## `query` (type: `string`):

Search query, e.g. "python" or "sygeplejerske". Operators: +word (require), -word (exclude), "exact phrase". Tip: "+python +ai" returns only jobs matching both.

## `queries` (type: `array`):

Run several keyword searches in one run. Results are deduplicated by job ID across all queries.

## `location` (type: `string`):

Optional Danish city or region to filter by (e.g. "København", "Aarhus", "Odense", "Region Hovedstaden"). Leave blank to search all of Denmark.

## `employmentType` (type: `array`):

Filter by type of employment. Select one or more; leave empty for any.

## `workPlace` (type: `array`):

Filter by where the work is done. Select one or more; leave empty for any. Note: fully remote jobs are rare on jobindex.

## `workHours` (type: `array`):

Filter by full-time or part-time. Select one or more; leave empty for any.

## `subCategories` (type: `array`):

Filter by one or more jobindex job categories. Leave empty for all categories.

## `maxResults` (type: `integer`):

Maximum number of jobs to return per query. Use 0 for unlimited (hard-capped at 5000).

## `sortBy` (type: `string`):

Order results by jobindex relevance or by posting date (newest first).

## `maxAgeDays` (type: `integer`):

Only include jobs posted within the last N days. Leave empty for no age limit.

## `fetchJobDetail` (type: `boolean`):

Open each job's detail page to add a summary, full description, and any stated salary (DKK). Slower and uses more requests. Note: most Danish ads don't state a salary.

## `descriptionMaxLength` (type: `integer`):

Truncate the description/summary text to this many characters. Use 0 for no limit. Only applies when 'Fetch job details' is enabled.

## `descriptionFormat` (type: `string`):

Which description representations to include (only with 'Fetch job details'): all, plain text, HTML, or Markdown.

## `compact` (type: `boolean`):

Return only core fields (title, company, location, dates, URLs) — handy for AI/agent workflows.

## `excludeEmptyFields` (type: `boolean`):

Remove fields that are null or empty from each output record.

## `incrementalMode` (type: `boolean`):

Only emit jobs that are NEW, UPDATED or REAPPEARED since the previous run. The first run builds a baseline (everything is NEW). Each record gets a 'changeType' field.

## `emitUnchanged` (type: `boolean`):

When incremental mode is on, also include jobs with no detected changes (changeType UNCHANGED).

## `emitExpired` (type: `boolean`):

When incremental mode is on, also emit jobs that have disappeared since the last run (changeType EXPIRED). Requires a complete scrape — set Max results to 0 for reliable expiry tracking.

## `skipReposts` (type: `boolean`):

When incremental mode is on, suppress new listings whose content matches a previously expired job (re-posted ads).

## `stateKey` (type: `string`):

Identifier for the incremental state. Leave blank to derive it automatically from your search settings. Set a fixed value to share one state across runs even if you tweak the search, or a unique value to keep separate monitors apart.

## `telegramToken` (type: `string`):

Bot token from @BotFather. Requires a Telegram chat ID too.

## `telegramChatId` (type: `string`):

Chat or channel ID to send messages to (e.g. from @userinfobot).

## `slackWebhookUrl` (type: `string`):

Slack incoming webhook URL. The run summary is posted to its channel.

## `discordWebhookUrl` (type: `string`):

Discord channel webhook URL. The run summary is posted to its channel.

## `webhookUrl` (type: `string`):

Receives a JSON POST of {metadata, items} after every run (fires even with zero results). Use it to integrate any service.

## `webhookHeaders` (type: `object`):

Optional custom HTTP headers for the generic webhook (e.g. an Authorization header), as a JSON object.

## `notificationLimit` (type: `integer`):

Maximum number of jobs listed in each chat message (Telegram/Slack/Discord). The webhook always receives the full list.

## `notifyOnlyChanges` (type: `boolean`):

With incremental mode on, only notify about NEW and UPDATED jobs (skip unchanged/reappeared/expired).

## Actor input object example

```json
{
  "query": "python",
  "maxResults": 25,
  "sortBy": "relevance",
  "fetchJobDetail": false,
  "descriptionMaxLength": 0,
  "descriptionFormat": "all",
  "compact": false,
  "excludeEmptyFields": false,
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "skipReposts": false,
  "notificationLimit": 10,
  "notifyOnlyChanges": false
}
```

# Actor output Schema

## `jobs` (type: `string`):

All scraped job listings as JSON (the run's default dataset).

## `jobsOverview` (type: `string`):

Job listings limited to the key columns from the Overview view.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "query": "python"
};

// Run the Actor and wait for it to finish
const run = await client.actor("corvuslab/jobindex-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "query": "python" }

# Run the Actor and wait for it to finish
run = client.actor("corvuslab/jobindex-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "query": "python"
}' |
apify call corvuslab/jobindex-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=corvuslab/jobindex-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "Jobindex.dk Scraper",
        "description": "Turn jobindex.dk — Denmark's largest job board — into clean, structured job data. Filter by keyword, location, employment type and category, monitor for new postings, and get alerts on Slack, Telegram, Discord or webhook. Company info, salaries and apply links included.",
        "version": "0.1",
        "x-build-id": "KvmnPJvHHctv6FFNz"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/corvuslab~jobindex-scraper/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-corvuslab-jobindex-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/corvuslab~jobindex-scraper/runs": {
            "post": {
                "operationId": "runs-sync-corvuslab-jobindex-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/corvuslab~jobindex-scraper/run-sync": {
            "post": {
                "operationId": "run-sync-corvuslab-jobindex-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "properties": {
                    "query": {
                        "title": "Search query 🔍",
                        "type": "string",
                        "description": "Search query, e.g. \"python\" or \"sygeplejerske\". Operators: +word (require), -word (exclude), \"exact phrase\". Tip: \"+python +ai\" returns only jobs matching both."
                    },
                    "queries": {
                        "title": "Search queries 📑",
                        "type": "array",
                        "description": "Run several keyword searches in one run. Results are deduplicated by job ID across all queries.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "location": {
                        "title": "Location 📍",
                        "type": "string",
                        "description": "Optional Danish city or region to filter by (e.g. \"København\", \"Aarhus\", \"Odense\", \"Region Hovedstaden\"). Leave blank to search all of Denmark."
                    },
                    "employmentType": {
                        "title": "Employment type 💼",
                        "type": "array",
                        "description": "Filter by type of employment. Select one or more; leave empty for any.",
                        "items": {
                            "type": "string",
                            "enum": [
                                "permanent",
                                "temporary",
                                "student",
                                "apprentice",
                                "graduate",
                                "internship",
                                "flexJob",
                                "volunteer",
                                "youth",
                                "freelance",
                                "hourly"
                            ],
                            "enumTitles": [
                                "Permanent (Fastansættelse)",
                                "Temporary (Tidsbegrænset)",
                                "Student job (Studiejob)",
                                "Apprentice (Elev/lærling)",
                                "Graduate/trainee",
                                "Internship (Praktikplads)",
                                "Flex job (Fleksjob)",
                                "Volunteer (Frivillig)",
                                "Youth/part-time (Ungarbejder)",
                                "Freelance",
                                "Hourly/on-call (Timeansat)"
                            ]
                        }
                    },
                    "workPlace": {
                        "title": "Workplace 🏠",
                        "type": "array",
                        "description": "Filter by where the work is done. Select one or more; leave empty for any. Note: fully remote jobs are rare on jobindex.",
                        "items": {
                            "type": "string",
                            "enum": [
                                "onsite",
                                "remote",
                                "hybrid"
                            ],
                            "enumTitles": [
                                "On-site (Tilbydes ikke)",
                                "Remote (100% hjemmearbejde)",
                                "Hybrid (Muligt)"
                            ]
                        }
                    },
                    "workHours": {
                        "title": "Working hours ⏰",
                        "type": "array",
                        "description": "Filter by full-time or part-time. Select one or more; leave empty for any.",
                        "items": {
                            "type": "string",
                            "enum": [
                                "fulltime",
                                "parttime"
                            ],
                            "enumTitles": [
                                "Full-time (Fuldtid)",
                                "Part-time (Deltid)"
                            ]
                        }
                    },
                    "subCategories": {
                        "title": "Job categories 🗂️",
                        "type": "array",
                        "description": "Filter by one or more jobindex job categories. Leave empty for all categories.",
                        "items": {
                            "type": "string",
                            "enum": [
                                "Courier & delivery",
                                "Childcare",
                                "Retail",
                                "Retail management",
                                "Property service",
                                "Hairdressing & personal care",
                                "Hotel, restaurant & kitchen",
                                "Cleaning",
                                "Service",
                                "Security",
                                "Plumbing & sheet metal",
                                "Construction & civil works",
                                "Electrician",
                                "Industrial production",
                                "Iron & metal",
                                "Warehouse",
                                "Agriculture, forestry & fishing",
                                "Painting & surface treatment",
                                "Mechanics & automotive",
                                "Food & beverage",
                                "Textile & craft",
                                "Transport",
                                "Wood & furniture",
                                "Carpentry & joinery",
                                "Database",
                                "Internet & web",
                                "IT operations & support",
                                "IT management",
                                "Software development & programming",
                                "Telecom & data communication",
                                "ERP & business systems",
                                "Civil engineering",
                                "Electrical engineering",
                                "Chemistry & biotech",
                                "Engineering management",
                                "Mechanical engineering",
                                "Pharma & food technology",
                                "Production & process engineering",
                                "Academic & policy work",
                                "Real estate agent",
                                "Finance & insurance",
                                "Purchasing",
                                "Law",
                                "Office & administration",
                                "Logistics & freight",
                                "Public administration",
                                "Translation & languages",
                                "Secretary & reception",
                                "Financial management",
                                "Finance & accounting",
                                "School & institution management",
                                "Management",
                                "HR & personnel",
                                "Project management",
                                "Sales management",
                                "Executive & board",
                                "Business development",
                                "Design",
                                "Graphics",
                                "Communication & journalism",
                                "Culture & church",
                                "Marketing",
                                "Sales",
                                "Self-employment",
                                "Telemarketing",
                                "Doctor / physician",
                                "Medical secretary",
                                "Care & nursing assistance",
                                "Psychology & psychiatry",
                                "Social work",
                                "Nurse & midwife",
                                "Dentist & clinic staff",
                                "Technical health work",
                                "Therapy & rehabilitation",
                                "Library",
                                "Research",
                                "Teacher",
                                "Pedagogue / educator",
                                "Adult education",
                                "Defense & intelligence",
                                "Other"
                            ],
                            "enumTitles": [
                                "Courier & delivery (Bud og udbringning)",
                                "Childcare (Børnepasning)",
                                "Retail (Detailhandel)",
                                "Retail management (Detailledelse)",
                                "Property service (Ejendomsservice)",
                                "Hairdressing & personal care (Frisør og personlig pleje)",
                                "Hotel, restaurant & kitchen (Hotel, restaurant og køkken)",
                                "Cleaning (Rengøring)",
                                "Service (Service)",
                                "Security (Sikkerhed)",
                                "Plumbing & sheet metal (Blik og rør)",
                                "Construction & civil works (Bygge og anlæg)",
                                "Electrician (Elektriker)",
                                "Industrial production (Industriel produktion)",
                                "Iron & metal (Jern og metal)",
                                "Warehouse (Lager)",
                                "Agriculture, forestry & fishing (Landbrug, skov og fiskeri)",
                                "Painting & surface treatment (Maling og overfladebehandling)",
                                "Mechanics & automotive (Mekanik og auto)",
                                "Food & beverage (Nærings- og nydelsesmiddel)",
                                "Textile & craft (Tekstil og kunsthåndværk)",
                                "Transport (Transport)",
                                "Wood & furniture (Træ- og møbelindustri)",
                                "Carpentry & joinery (Tømrer og snedker)",
                                "Database (Database)",
                                "Internet & web (Internet og WWW)",
                                "IT operations & support (IT-drift og support)",
                                "IT management (IT-ledelse)",
                                "Software development & programming (Systemudvikling og programmering)",
                                "Telecom & data communication (Tele- og datakommunikation)",
                                "ERP & business systems (Økonomi- og virksomhedssystemer)",
                                "Civil engineering (Bygge- og anlægsteknik)",
                                "Electrical engineering (Elektroteknik)",
                                "Chemistry & biotech (Kemi og bioteknik)",
                                "Engineering management (Ledelse inden for ingeniør og teknik)",
                                "Mechanical engineering (Maskinteknik)",
                                "Pharma & food technology (Medicinal og levnedsmiddel)",
                                "Production & process engineering (Produktions- og procesteknik)",
                                "Academic & policy work (Akademisk og politisk arbejde)",
                                "Real estate agent (Ejendomsmægler)",
                                "Finance & insurance (Finans og forsikring)",
                                "Purchasing (Indkøb)",
                                "Law (Jura)",
                                "Office & administration (Kontor)",
                                "Logistics & freight (Logistik og spedition)",
                                "Public administration (Offentlig administration)",
                                "Translation & languages (Oversættelse og sprog)",
                                "Secretary & reception (Sekretær og reception)",
                                "Financial management (Økonomiledelse)",
                                "Finance & accounting (Økonomi og regnskab)",
                                "School & institution management (Institutions- og skoleledelse)",
                                "Management (Ledelse)",
                                "HR & personnel (Personale og HR)",
                                "Project management (Projektledelse)",
                                "Sales management (Salgsledelse)",
                                "Executive & board (Topledelse og bestyrelse)",
                                "Business development (Virksomhedsudvikling)",
                                "Design (Design og formgivning)",
                                "Graphics (Grafisk)",
                                "Communication & journalism (Kommunikation og journalistik)",
                                "Culture & church (Kultur og kirke)",
                                "Marketing (Marketing)",
                                "Sales (Salg)",
                                "Self-employment (Selvstændig virksomhedsdrift)",
                                "Telemarketing (Telemarketing)",
                                "Doctor / physician (Læge)",
                                "Medical secretary (Lægesekretær)",
                                "Care & nursing assistance (Pleje og omsorg)",
                                "Psychology & psychiatry (Psykologi og psykiatri)",
                                "Social work (Socialrådgivning)",
                                "Nurse & midwife (Sygeplejerske og jordemoder)",
                                "Dentist & clinic staff (Tandlæge og klinikpersonale)",
                                "Technical health work (Teknisk sundhedsarbejde)",
                                "Therapy & rehabilitation (Terapi og genoptræning)",
                                "Library (Bibliotek)",
                                "Research (Forskning)",
                                "Teacher (Lærer)",
                                "Pedagogue / educator (Pædagog)",
                                "Adult education (Voksenuddannelse)",
                                "Defense & intelligence (Forsvar og efterretning)",
                                "Other (Øvrige)"
                            ]
                        }
                    },
                    "maxResults": {
                        "title": "Max results per query 🔢",
                        "minimum": 0,
                        "maximum": 5000,
                        "type": "integer",
                        "description": "Maximum number of jobs to return per query. Use 0 for unlimited (hard-capped at 5000).",
                        "default": 25
                    },
                    "sortBy": {
                        "title": "Sort by ↕️",
                        "enum": [
                            "relevance",
                            "date"
                        ],
                        "type": "string",
                        "description": "Order results by jobindex relevance or by posting date (newest first).",
                        "default": "relevance"
                    },
                    "maxAgeDays": {
                        "title": "Max age (days) 🗓️",
                        "minimum": 1,
                        "maximum": 60,
                        "type": "integer",
                        "description": "Only include jobs posted within the last N days. Leave empty for no age limit."
                    },
                    "fetchJobDetail": {
                        "title": "Fetch job details 📄",
                        "type": "boolean",
                        "description": "Open each job's detail page to add a summary, full description, and any stated salary (DKK). Slower and uses more requests. Note: most Danish ads don't state a salary.",
                        "default": false
                    },
                    "descriptionMaxLength": {
                        "title": "Description max length ✂️",
                        "minimum": 0,
                        "type": "integer",
                        "description": "Truncate the description/summary text to this many characters. Use 0 for no limit. Only applies when 'Fetch job details' is enabled.",
                        "default": 0
                    },
                    "descriptionFormat": {
                        "title": "Description format 📝",
                        "enum": [
                            "all",
                            "text",
                            "html",
                            "markdown"
                        ],
                        "type": "string",
                        "description": "Which description representations to include (only with 'Fetch job details'): all, plain text, HTML, or Markdown.",
                        "default": "all"
                    },
                    "compact": {
                        "title": "Compact output 📦",
                        "type": "boolean",
                        "description": "Return only core fields (title, company, location, dates, URLs) — handy for AI/agent workflows.",
                        "default": false
                    },
                    "excludeEmptyFields": {
                        "title": "Exclude empty fields 🧹",
                        "type": "boolean",
                        "description": "Remove fields that are null or empty from each output record.",
                        "default": false
                    },
                    "incrementalMode": {
                        "title": "Incremental mode 🔁",
                        "type": "boolean",
                        "description": "Only emit jobs that are NEW, UPDATED or REAPPEARED since the previous run. The first run builds a baseline (everything is NEW). Each record gets a 'changeType' field.",
                        "default": false
                    },
                    "emitUnchanged": {
                        "title": "Also emit unchanged jobs",
                        "type": "boolean",
                        "description": "When incremental mode is on, also include jobs with no detected changes (changeType UNCHANGED).",
                        "default": false
                    },
                    "emitExpired": {
                        "title": "Also emit expired jobs",
                        "type": "boolean",
                        "description": "When incremental mode is on, also emit jobs that have disappeared since the last run (changeType EXPIRED). Requires a complete scrape — set Max results to 0 for reliable expiry tracking.",
                        "default": false
                    },
                    "skipReposts": {
                        "title": "Skip reposts",
                        "type": "boolean",
                        "description": "When incremental mode is on, suppress new listings whose content matches a previously expired job (re-posted ads).",
                        "default": false
                    },
                    "stateKey": {
                        "title": "State key 🔑",
                        "type": "string",
                        "description": "Identifier for the incremental state. Leave blank to derive it automatically from your search settings. Set a fixed value to share one state across runs even if you tweak the search, or a unique value to keep separate monitors apart."
                    },
                    "telegramToken": {
                        "title": "Telegram bot token",
                        "type": "string",
                        "description": "Bot token from @BotFather. Requires a Telegram chat ID too."
                    },
                    "telegramChatId": {
                        "title": "Telegram chat ID",
                        "type": "string",
                        "description": "Chat or channel ID to send messages to (e.g. from @userinfobot)."
                    },
                    "slackWebhookUrl": {
                        "title": "Slack webhook URL",
                        "type": "string",
                        "description": "Slack incoming webhook URL. The run summary is posted to its channel."
                    },
                    "discordWebhookUrl": {
                        "title": "Discord webhook URL",
                        "type": "string",
                        "description": "Discord channel webhook URL. The run summary is posted to its channel."
                    },
                    "webhookUrl": {
                        "title": "Generic webhook URL",
                        "type": "string",
                        "description": "Receives a JSON POST of {metadata, items} after every run (fires even with zero results). Use it to integrate any service."
                    },
                    "webhookHeaders": {
                        "title": "Webhook headers",
                        "type": "object",
                        "description": "Optional custom HTTP headers for the generic webhook (e.g. an Authorization header), as a JSON object."
                    },
                    "notificationLimit": {
                        "title": "Jobs per notification",
                        "minimum": 1,
                        "maximum": 20,
                        "type": "integer",
                        "description": "Maximum number of jobs listed in each chat message (Telegram/Slack/Discord). The webhook always receives the full list.",
                        "default": 10
                    },
                    "notifyOnlyChanges": {
                        "title": "Notify only about changes",
                        "type": "boolean",
                        "description": "With incremental mode on, only notify about NEW and UPDATED jobs (skip unchanged/reappeared/expired).",
                        "default": false
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
