FINN.no Jobs Scraper - Listings, Employers & Contacts
Pricing
from $0.80 / 1,000 results
FINN.no Jobs Scraper - Listings, Employers & Contacts
Scrape active FINN.no job listings by search filters or URL. Extract 30+ fields including full descriptions, employer details, deadlines, contacts, languages, sectors, industries, keywords and apply URLs.
Pricing
from $0.80 / 1,000 results
Rating
0.0
(0)
Developer
Abot API
Maintained by CommunityActor stats
1
Bookmarked
6
Total users
2
Monthly active users
11 days ago
Last modified
Categories
Features
Share
Finn.no Scraper: Job Ads, Employers, Deadlines and Contacts
Finn.no Scraper turns Finn.no, Norway's largest classifieds portal, into a structured feed of job ads. Search by keyword, location, employment type, industry, occupation, sector, working language and posting date, or paste any finn.no/job URL, including a single ad, and get the full ad back: employer, address, deadline, contact persons, working languages, sector, industries, keywords, the apply URL and the complete description. Export to JSON, CSV or Excel, or pull results into your app through the API.
Why This Scraper?
- 30+ fields per ad, not just the search card. Full HTML and plain-text description, employer address and homepage, deadline, contact persons, working languages, sector, industries and the apply URL.
- Two ways to scrape. Build a search from keyword, location, employment type, industry, occupation, sector, working language and posted-since filters, or paste any finn.no/job URL, including a single
/job/ad/<id>link, and every result page is collected automatically. - Post-filter for sponsored cards. Drop "Betalt plassering" placements with one checkbox, applied to both a built search and a pasted URL's results.
- Per-search budget. A multi-keyword run splits the item cap across every keyword instead of exhausting it on the first one.
- Daily change monitoring. Turn on Incremental mode to get only the listings that are new or changed on each scheduled run, with unchanged and expired tracking available as opt-ins.
- Resume an interrupted crawl. Paste a previous run or dataset ID to continue a large walk without re-collecting what's already saved, or let the actor recover automatically after a platform migration.
- Pipe results into your apps. Send a summary of every ad straight into Notion, Linear or Airtable through MCP connectors, on top of the full dataset.
Use Cases
- Recruiters and staffing agencies: track competing job ads for a role, and compare deadlines, sectors, employment forms and ad requirements across employers.
- Job boards and aggregators: mirror Finn.no job listings into your own search index, newsletter or alert feed.
- Market and labor researchers: measure which sectors, industries and locations are hiring, and how demand shifts week to week.
- Norwegian-language NLP teams: build labeled datasets of real job ad text for classification, extraction or search-ranking models.
- HR and compliance analytics teams: monitor a defined set of employers' own postings for consistency and changes over time.
Data You Get
Sample shape: values are illustrative placeholders, not from a live ad.
| Field | Example |
|---|---|
id / idInt | "471234567" / 471234567 |
url | "https://www.finn.no/job/ad/471234567" |
title | "Backend-utvikler" |
employer | "Nordlys Data AS" |
employerHomepage | "https://www.example-employer.no/" |
city | "Oslo" |
postalCode | "0150" |
locationDisplay | "Oslo" |
latitude / longitude | 59.9139 / 10.7522 |
datePosted / applicationDeadline | "2026-02-10" / "2026-03-10" |
employmentForm / employmentType | "Fast" / ["FULL_TIME"] |
sector | "Privat" |
industries | ["IT", "Telekommunikasjon"] |
workingLanguages | ["Norsk", "Engelsk"] |
contactPersons | [{ "name": "Kari Fjeldstad", "phones": ["+4791234567"], "emails": ["kari.fjeldstad@example-employer.no"] }] |
keywords | ["backend", "kotlin", "cloud"] |
applyUrl | "https://www.example-employer.no/careers/471234567" |
descriptionText | "Full ad description as plain text goes here." |
numberOfPositions | 5 |
labels | ["Delvis hjemmekontor"] |
salary | null (an object with currency, min, max, unit or text when the ad publishes pay) |
searchKey | "developer" (the keyword or search URL that found the ad) |
sponsored / scrapedAt | false / "2026-02-10T08:00:00.000Z" |
The dataset has three views in the Console: Overview, Detail and Changes (the incremental change columns). Each ad also carries the employer's own website URL and logo, address country, a location filter link, epoch-millisecond timestamps alongside every date field, the raw Norwegian deadline string, a last-modified time, the employment type you requested, the job function and internal job title, the full HTML description, and a breadcrumb trail. When Incremental mode is on, every row also carries changeType (NEW, UPDATED, UNCHANGED, REAPPEARED or EXPIRED), changedFields, firstSeenAt and lastSeenAt; EXPIRED only appears on a run that has "Emit expired" turned on and itself completes a full, uncapped, non-resumed scan of that same search; REAPPEARED only appears for a listing that an earlier such run marked no longer present, so neither shows up on a plain incremental run.
How to Use
- Pick a Search mode:
search(build from keyword, location and filters) orurl(paste finn.no/job links). - Fill in the fields for that mode. In
urlmode the search-builder fields are ignored, because the pasted link already carries its own filters. - Set Max listings and, if you want a hard page ceiling, Max pages to control run size and cost.
- Click Start, then download the dataset as JSON, CSV or Excel, or read it through the API.
Search by keyword and location:
{"mode": "search","queries": ["prosjektleder"],"location": "1.20001.20061","employmentType": "fulltime","maxListings": 100,"fetchDetails": true}
Multiple keywords with an industry filter, newest first:
{"mode": "search","queries": ["sykepleier", "fysioterapeut"],"industry": "9","publishedSince": "7","sortBy": "published_desc","maxPages": 3,"maxListings": 150}
URL mode default (behaviour change): if you switch to URL mode and leave the urls field untouched, the actor now scrapes the pre-filled examples, one per distinct target (search with filters, keyword search, paginated, sorted, legacy search.html, ad detail, and the jobs landing page, which maps to the all-jobs search). Clear or replace the list to scrape your own links. Tracking parameters such as utm_* are stripped and every host (apex, mobile) and legacy form (numeric-id-only, finnkode) you paste is normalised to the canonical finn.no form. Result volume is still bounded by maxListings.
URL mode: a search link plus one single ad:
{"mode": "url","urls": ["https://www.finn.no/job/search?q=developer&extent=3947&location=1.20001.20061","https://www.finn.no/job/ad/471234567"],"skipSponsored": true,"maxListings": 200}
Daily monitoring: only what changed since yesterday (uncapped so a complete scan is possible):
{"mode": "search","queries": ["developer"],"location": "1.20001.20061","incrementalMode": true,"maxListings": 0}
Run it from your code
Python:
from apify_client import ApifyClientclient = ApifyClient("<YOUR_APIFY_TOKEN>")run = client.actor("abotapi/finn-no-jobs-scraper").call(run_input={"mode": "search","queries": ["developer"],"location": "1.20001.20061",})for listing in client.dataset(run["defaultDatasetId"]).iterate_items():print(listing["title"], listing["employer"])
JavaScript:
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });const run = await client.actor('abotapi/finn-no-jobs-scraper').call({mode: 'search',queries: ['developer'],location: '1.20001.20061',});const { items } = await client.dataset(run.defaultDatasetId).listItems();
Or connect it to Make, Zapier, n8n, Google Sheets or webhooks from the Integrations tab.
Caps: Max listings and Max pages
Max listings stops the whole run once that many records have been collected, split fairly across every keyword in a multi-keyword search; it defaults to 20 when left empty, only an explicit 0 means unlimited. Max pages limits how many search-result pages are walked per keyword (or per pasted URL); each page holds up to about 50 ads. Leave Max pages empty or at 0 for no page limit, and set Max listings to 0 for no item limit, to walk every result page Finn.no has for that search.
Resume and recurring updates
There are two different things here, pick the one that matches what you're doing:
| Need | Use |
|---|---|
| A crawl stopped and should continue | resumeFromRunId, plus automatic checkpoint recovery |
| Run the same search every day and receive only changes | incrementalMode |
| Keep separate daily campaigns for similar searches | distinct stateKey values |
| A normal full snapshot | leave both off |
Resume (resumeFromRunId) continues one specific interrupted or previous large crawl: paste a run ID or dataset ID and this run skips listings already collected there, returning only the remaining new ones. This also works for a full re-walk of the same search, not only for a pasted list of single-ad URLs: a leading page whose listings were already collected by the earlier run is walked through rather than stopping the search immediately. If new ads have shifted into those same early pages since the earlier run, the walk can still end once it hits a page with no new listings at all, so resume soon after the interruption for the most complete result. A checkpoint is also saved automatically as the run progresses; if the run is interrupted by an Apify platform migration, or you click Resurrect on a failed run, it picks back up from where it left off in the same run, without re-saving already-collected listings. No input is needed for that; it's automatic.
Incremental mode (incrementalMode) is for a schedule, for example daily: the actor remembers the previous run of the same search by itself, so you never paste a run ID. The first run returns everything as NEW. Later runs return only NEW and UPDATED (with changedFields listing what changed); unchanged listings are suppressed and not billed unless emitUnchanged is on. REAPPEARED and EXPIRED are opt-in and cannot happen by default: turning on emitExpired marks a listing EXPIRED only once a run has completed a full, uncapped scan of the tracked search with no resumeFromRunId involved; only a listing that a complete emitExpired scan marked no longer present can later come back as REAPPEARED on a subsequent run that finds it again. If emitExpired has never been turned on, or a run never completes a full scan, no listing is ever marked EXPIRED, and REAPPEARED never occurs either. State is isolated per search/URL setup and per fetchDetails setting, so toggling detail fetching starts a fresh baseline instead of diffing detail fields against a card-only snapshot. When a new version of the actor starts reading extra fields from an ad (for example sector, employment form or apply link), listings already tracked that only gain those fields are not re-reported as UPDATED; their stored snapshot is simply refreshed in place, and any real change to the ad still comes back as UPDATED. Set stateKey to name or deliberately share a monitoring campaign.
Scheduled-run example, same search run daily:
Day 1 (first run ever for this search): every listing comes back as "changeType": "NEW".
Day 2 (the schedule fires again, identical input): brand-new postings come back as "changeType": "NEW", listings whose title, deadline or other fields changed come back as "changeType": "UPDATED" with changedFields naming what changed, and listings still there and unchanged are not returned at all unless emitUnchanged is on. Nothing is marked REAPPEARED or EXPIRED in this example, because emitExpired was never turned on.
Send results into your apps (MCP connectors)
Optionally pipe the scraped results into the apps you already use, via Model Context Protocol (MCP) connectors. This is an extra delivery step after the scrape, the Apify dataset is never changed.
What gets written to the connector: a condensed, human-readable summary of each record, not the full JSON. Each item becomes one entry with a title and its key fields flattened to plain text. The complete record always stays in the Apify dataset.
- Authorize a connector once under Apify, then Settings, then Integrations (Notion, Linear, Airtable, or Apify).
- Select it in the "Pipe results into your apps" input field. (If the picker is empty, you haven't authorized a connector yet.)
- For Notion, also set
notionParentPageUrlto the page where items should be created.
The connection is mediated by Apify's MCP proxy, so this actor never sees your third-party credentials. Leave the field empty to skip.
Input Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
mode | string | search | search (use the builder fields below) or url (paste finn.no/job URLs). |
queries | array | prefill: ["developer"] | Search mode only, ignored in URL mode. Free-text search terms; one search per keyword. Empty runs a browse with no keyword filter. |
location | string | (empty) | Search mode only, ignored in URL mode. One of these place names: Norge, Norway, Oslo, Akershus, Vestland, Bergen, Trøndelag, Trondheim, Rogaland, Stavanger, or a Finn.no hierarchical location code from ?location= on finn.no/job, for example 1.20001.20061 for Oslo. Any other name stops the run with an error that lists the accepted names; copy the code from the finn.no/job address bar for other places. |
employmentType | string | fulltime | Search mode only; a pasted URL's own extent/manager_role parameter is read instead. any, fulltime (Heltid), parttime (Deltid) or management (Lederstillinger). |
industry | string | (empty) | Search mode only, ignored in URL mode. Finn.no industry code from ?industry=. |
occupation | string | (empty) | Search mode only, ignored in URL mode. Finn.no occupation/job-function code from ?occupation=. |
sector | string | any | Search mode only, ignored in URL mode. any, private or public. |
workLanguage | string | any | Search mode only, ignored in URL mode. Workplace working language filter. |
publishedSince | string | any | Search mode only, ignored in URL mode. Limit to ads posted within the last N days. |
sortBy | string | default | Search mode only; a pasted URL keeps whatever ?sort= it already carries. default (relevance), published_desc (newest) or deadline_asc (closest deadline). |
urls | array | default and prefill: 10 example URLs, one per distinct search or listing target, with a single ad-detail example | URL mode only, ignored in search mode. One or more finn.no/job URLs; each is paged forward independently. Other accepted forms (legacy ad.html?finnkode= links, numeric ad ids, m.finn.no, bare finn.no, utm-tagged links) work when pasted but are not in the default. The search-builder fields above are ignored in this mode. |
skipSponsored | boolean | false | Applies in both search mode and URL mode. Drop "Betalt plassering" cards after fetch, including from a pasted URL's results. |
maxPages | integer | (empty) | Max search-result pages per keyword (or per pasted URL). Leave empty, or 0, to walk every page Finn.no has. Finn.no itself shows at most 50 pages (about 2,500 ads) per search, so a larger search stops there; use filters to split it. EXPIRED detection in incremental mode needs a search that fits in those 50 pages. |
maxListings | integer | 20 | Stop after this many listings across all searches. 0 means unlimited, still bounded by maxPages. |
fetchDetails | boolean | true | Fetch full ad details: description, deadline, employer details, sector, industries, working languages, contact persons, keywords and apply URL. Each delivered listing whose detail page is read is charged as one detail-page event on top of the per-result charge. |
resumeFromRunId | string | (empty) | A previous run ID or dataset ID to continue a full-catalogue walk. Listings already collected there (matched by id) are skipped. |
incrementalMode | boolean | false | Recurring monitoring of this same search. First run returns everything as NEW; later runs return only NEW/UPDATED by default. |
stateKey | string | (empty) | Name this monitoring campaign to keep its state stable, or to deliberately share state across differently-configured runs. |
emitUnchanged | boolean | false | Incremental mode only. Also return (and bill) listings unchanged since the last run, marked UNCHANGED. |
emitExpired | boolean | false | Incremental mode only. Also return (and bill) listings no longer found, marked EXPIRED, once a run has fully scanned the tracked search. |
ignoreFieldsForChanges | array of strings | [] | Incremental mode only. Output field names left out of the change comparison, so a change in them alone does not make a listing UPDATED. They are still returned. Adding or removing a field here does not mark tracked listings UPDATED; later real changes are compared under the current list. |
proxy | object | prefill: useApifyProxy: true | Connection settings. The default connection is enough for normal use; you can pick another proxy group or paste your own proxy URLs. |
mcpConnectors | array | (empty) | Optional: send a summary of each record to apps you authorized under Integrations. |
notionParentPageUrl | string | (empty) | Notion connector only: page under which item pages are created. |
maxNotifyListings | integer | 50 | Cap on items written to each connector per run. Does not affect the dataset. |
Output Example
Sample shape: values are illustrative placeholders, not from a live ad.
{"id": "471234567","idInt": 471234567,"url": "https://www.finn.no/job/ad/471234567","title": "Backend-utvikler","subtitle": "Bygg skyplattformer for norske kunder","employer": "Nordlys Data AS","employerUrl": "https://www.example-employer.no/","employerHomepage": "https://www.example-employer.no/","employerLogo": "https://images.finncdn.no/dynamic/default/0000/0/0/000000_0000000000.png","streetAddress": "Storgata 12","postalCode": "0150","city": "Oslo","addressCountry": "NO","locationDisplay": "Oslo","locationLink": "https://www.finn.no/job/search?location=1.20001.20061","latitude": 59.9139,"longitude": 10.7522,"datePosted": "2026-02-08","datePostedEpochMs": 1770508800000,"relativePosted": "2 dager siden","applicationDeadline": "2026-03-10","applicationDeadlineEpochMs": 1773100800000,"applicationDeadlineRaw": "10.03.2026","lastModified": "2026-02-09T14:00:00.000+02:00","lastModifiedEpochMs": 1770638400000,"employmentForm": "Fast","employmentType": ["FULL_TIME"],"employmentTypeRequested": "fulltime","sector": "Privat","industries": ["IT", "Telekommunikasjon"],"jobFunction": "Systemutvikling","internalJobTitle": "Backend Engineer II","workingLanguages": ["Norsk", "Engelsk"],"contactPersons": [{ "name": "Kari Fjeldstad", "phones": ["+4791234567"], "emails": ["kari.fjeldstad@example-employer.no"] }],"keywords": ["backend", "kotlin", "cloud"],"applyUrl": "https://www.example-employer.no/careers/471234567","descriptionHtml": "<p>Full HTML ad description goes here.</p>","descriptionText": "Full ad description as plain text goes here.","breadcrumb": ["FINN", "Jobb", "Sok", "Oslo"],"sponsored": false,"scrapedAt": "2026-02-10T08:00:00.000Z"}
Plan Requirement
Finn.no jobs load over Apify's datacenter proxies, so residential proxies are not required and the default proxy setting works on a plan that includes Apify proxy access. If a connection is refused the run retries on another one. You can also pick a different proxy group or paste your own proxy URLs in the proxy field.
Apify free plans have no proxy access at all. When the selected connection is unavailable the log says: "The selected connection is unavailable to this run. Apify free plans do not include residential proxies. Continuing on a backup connection; some pages may still be unavailable." If no page can be read the run fails with one of these messages: "The proxy connection was refused for this account, so no page could be read. Your Apify plan does not include proxy access; a paid plan is required", or "No results: residential proxies are unavailable on this run (Apify free plans do not include them), and the backup connection could not load the requested Finn.no pages", or "Could not load any results page (the requests failed), so nothing was collected. This is a connection problem, not an empty search." Upgrade at https://apify.com/pricing or supply your own proxy URLs.
FAQ
How much does it cost?
You pay for the Apify run itself and per record returned. With Fetch detail pages on (the default), each delivered listing whose detail page is read is also charged as one detail-page event on top of the per-record price; a pasted single-ad URL is itself read from its detail page, so its row carries the detail charge too. Listings whose detail page is refused, and listings incremental mode suppresses as unchanged, are never charged. The Pricing tab shows current rates. Use Max listings (and Max pages) to cap the cost of any run.
Is it legal to scrape Finn.no?
This actor collects only publicly available job listing data. You are responsible for how you use it: follow Finn.no's terms and the laws that apply to you, and get legal advice if you plan to store or redistribute employer and contact details commercially.
Can I get only new or changed listings on a schedule?
Yes. Schedule the actor from the Schedules tab and turn on Incremental mode. Each run then returns only new and updated listings, and unchanged ones are suppressed and not billed unless you turn on "Emit unchanged".
Why did my run return an empty dataset instead of failing?
If no Finn.no page could be loaded at all, the run ends as FAILED with a message that names a connection problem, so it is never mistaken for an empty search. If some pages loaded but a search returned nothing, the run finishes normally and its status message distinguishes a genuinely empty search (zero matches for your filters) from unreadable pages or a page layout change. Check the message and retry. An ad or page that Finn.no itself reports as gone (HTTP 404, for example an expired ad or an unknown location code) is skipped with a clear log line and counted as pagesNotFound in the run summary; it is not treated as a connection problem, and a run made only of such pages ends with a "not found" status message instead of a failure. The run does still fail outright for a few input problems unrelated to blocking: a location that is neither a supported place name nor a Finn.no location code (the error lists the accepted names), an unreadable resumeFromRunId, combining it with incrementalMode on a search that already has saved state, or the actor's connection setup itself failing at startup.
Does Resume work for a full search, or only for pasted single-ad URLs?
Both. Pasting a previous run or dataset ID into resumeFromRunId skips every listing already collected there, whether you're resuming a list of single ads or re-walking the same keyword search: an already-collected leading page is walked through rather than stopping the search immediately. If new ads have shifted into those same early pages since the earlier run, the walk can still end once it hits a page with no new listings at all, so resume soon after the interruption for the most complete result.
Can I use it with AI agents or MCP?
Yes. Call it from any Apify integration or MCP client, and use the connector field to push a summary of results into Notion, Linear or Airtable.
🔗 Want more classifieds data?
Pair this actor with these related scrapers from the same team:
| 🏷️ Leboncoin FR Pull public leboncoin.fr listings across cars, property, jobs, electronics, furniture... | 🏷️ Subito.it Scraper Scrape Subito.it listings across cars, real estate, marketplace, jobs, and more. Search... |
| 🏷️ Kijiji.ca Scraper Scrape Kijiji.ca listings across property, vehicles, jobs, electronics, furniture... | 🏷️ 2dehands & 2ememain Scrape classifieds from 2dehands.be and 2ememain.be by keyword, filters, or URLs. Returns... |
| 🏷️ Mudah.my Cars, Property, Jobs & Marketplace Scraper Scrape Mudah.my listings across every category: cars, motorcycles, property, mobiles... | 🏷️ Kleinanzeigen.de Scraper Scrape Kleinanzeigen.de ads by keyword, category, location, price, seller type, or search... |
💬 Support
- 🐞 Found a bug or a missing field? Open a ticket on the Issues tab. We usually reply within hours.
- 🛠️ Need another site, extra fields or a private build? Email contact@abotapi.com.
- ⭐ Enjoying it? A quick review on the actor page helps other users find it.