Jobindex.dk Scraper
Pricing
from $1.99 / 1,000 results
Jobindex.dk Scraper
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.
Pricing
from $1.99 / 1,000 results
Rating
0.0
(0)
Developer
Corvuslab
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
a day ago
Last modified
Categories
Share
Extract job listings from 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)
- Click Try for free / open the Actor in Apify Console.
- Enter a keyword in the Search query field (e.g.
python). - (Optional) Add a Location such as
KøbenhavnorAarhus. - Set Max results per query (default 25).
- (Optional) Enable Fetch job details to include full descriptions.
- 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
{"query": "python","location": "København","maxResults": 50,"fetchJobDetail": true}
Batch search across several keywords:
{"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.
queryvsqueries:queriesruns each entry as its own search and merges the results (a union). To require several words in one search, use the+operator in a singlequery, 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 |
{"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
NEWand 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,repostOfIdandrepostDetectedAt. SetskipRepoststo drop them. - Expiry needs a complete scrape. To reliably detect
EXPIREDjobs, set Max results to0(unlimited). IfmaxResultscuts 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.
{"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
notificationLimitjobs (1–20), each tagged with itschangeTypein 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. AddwebhookHeaders(e.g. anAuthorizationheader) if your endpoint needs them. notifyOnlyChanges(with incremental mode) limits alerts toNEWandUPDATEDjobs — 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.
{"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:
{"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
descriptionmay be empty —applyUrlalways links to the live ad. - With
compactenabled, each record is reduced to:jobId,title,company,location,isRemote,postedDate,deadline,url,applyUrl,searchQuery. - With
excludeEmptyFieldsenabled,null/empty fields are omitted entirely.
Using the API
You can run this Actor from your own code. Example with the Apify Python client:
from apify_client import ApifyClientclient = 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.