Platsbanken Scraper — Sweden Job Ads & Employer Leads
Pricing
$0.90 / 1,000 per job ad returneds
Platsbanken Scraper — Sweden Job Ads & Employer Leads
Scrape Sweden's national job board Platsbanken (Arbetsformedlingen open data): 40,000+ live ads with employer, organisation number, municipality, occupation, deadline, the named contact person's e-mail and phone, and the ATS vendor behind the apply link. No login, no API key.
Pricing
$0.90 / 1,000 per job ad returneds
Rating
0.0
(0)
Developer
Scrapers Delight
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
a day ago
Last modified
Categories
Share
🇸🇪 Platsbanken Scraper — Sweden Job Ads & Employer Leads
Scrape Platsbanken, Sweden's national vacancy board run by Arbetsförmedlingen (the Swedish Public Employment Service), through its official JobTech Dev open API. Every ad on the board is published as open data for reuse: no login, no API key, no cookie, no CAPTCHA.
40,000+ live ads, running from 2025-07-09 to today and growing by roughly 2,100–2,500 new ads every day. The exact size decays daily as ads expire, so no fixed figure is pinned here: the board measured 41,246 ads on 2026-09-05, and the actor reads the true total from the API at the start of every run and prints it in the log.
This is not a job-board dump. It is a Swedish employer lead list with a hiring trigger attached: every row carries the employer's organisation number (the Bolagsverket / SCB registry key), the named contact person with a direct e-mail and phone number where the ad has one, and the ATS vendor resolved from the apply link's host.
🎯 What does Platsbanken Scraper do?
| 🏢 Company identity | employer_name and employer_workplace (100%), employer_url (63.5–63.7%) and the Swedish org_number (99.2–99.4%) |
| 📞 A human to call | contact_name, contact_email, contact_phone flattened out of application_contacts[] — 45.1–47.5% of ads name a human, 39.8–41.1% give a direct e-mail, 32.0–35.6% a phone; 57.8–59.6% are reachable |
| 🛠 The incumbent ATS | ats_vendor + apply_host resolved from the apply URL. 40.5–50.2% resolve to one of 49 named vendors; 11.9–17.0% have no apply link at all — the displacement list |
| 📍 Precise location | Municipality + 4-digit code, region + code, city, postcode, street address, latitude/longitude |
| 💼 The role | Occupation, occupation group, occupation field, employment type, full/part-time, duration, scope-of-work band, experience and driving-licence requirements |
| 🗓 Timing | publication_date, application_deadline, plus derived days_live and days_to_deadline |
| 📝 Full ad text | description_text — 100% filled. Where the technographic signals live |
| 🔁 Incremental monitoring | A named state key so a daily run only ever delivers — and only ever bills for — genuinely new vacancies |
(Every percentage on this page was measured from real runs of this actor, re-measured
2026-09-07. Each is a RANGE across two differently-shaped runs made the same day — a full
calendar-day sweep (run iDb78sXSeDHqPH5gN, 2,268 ads) and the 2,000 most recently published ads
(run jP1jGT0bR51D6UdWf). Fill moves several points between a weekday's ads and a slice that
reaches back over a weekend, so a single number would be a promise this board does not make. Run
ids and the full tables are in Measured output fill below, and the same two runs are quoted on
the Input tab.)
🧭 Who buys this data
- Recruitment and staffing agencies — "every Swedish company that posted a vacancy yesterday, with a named contact and a phone number, minus the other agencies." Turn on Exclude staffing and recruitment agencies and you get end clients, not competitors.
- HR-tech and ATS vendors — Only employers with NO apply link is a live list of companies hiring with no applicant-tracking system in the loop. Only employers on these ATS vendors is the competitive-displacement list: Varbi, Visma Recruit, Recman, ReachMee, Teamtailor, Bullhorn, Workday and 42 more.
- B2B sales teams selling to growing companies — a vacancy is a growth signal, and
org_numberjoins it straight to a Swedish company record for revenue and headcount enrichment. - Labour-market analysts and job aggregators — the whole 40,000+ ad corpus, complete, with the taxonomy concept ids for joining to taxonomy.jobtechdev.se.
⚠️ The trap this actor exists to survive
A single query against this API can return at most 2,100 rows — limit caps at 100
(101 → HTTP 400) and offset caps at 2000 (2001 → HTTP 400). And a one-day window is already too
big: re-measured 2026-09-05, ads published on 09-01 numbered 2,478, on 09-02 2,131, on
09-03 2,115 and on 09-04 2,299. (A given day's count drifts by a few tens as late ads
register and expired ones leave the index, which is why every figure on this page carries its date.)
A crawler that partitions by day therefore drops ads on a run that still reports SUCCEEDED. We measured exactly that, twice on the same window, minutes apart:
| Same one-day window, 2026-09-04, 2,297 ads in scope | Ads delivered | Run |
|---|---|---|
| Sweep the whole corpus: ON (adaptive bisection) | 2,297 of 2,297 — 100% | forqADgU3QsybN16A |
| Sweep the whole corpus: OFF | 2,100 — 197 ads lost | 90JMcjeegPEppVjdy |
The ON run took 92 seconds, split the day into 2 date windows, delivered 2,297 rows with 0
duplicates and 0 dropped by filters, and chargedEventCounts came back at exactly 2,297 — so
delivered == billed. Peak memory 97 MB of the 512 MB it declares.
The OFF run does not lose the ads silently: it logs
2,297 ads match but a single query can only reach 2100 before it starts, and
197 more ad(s) match this filter but cannot be paged to when it stops. But it still returns 2,100
rows and a SUCCEEDED status.
Turn on "Sweep the whole corpus (auto-partition)" for any run that should return more than
2,100 ads. It probes each publication-date window with a free limit=0 call, reads the true
total, and bisects until every slice fits under the ceiling.
🚀 Quick start
Every Swedish job ad published in the last 24 hours, complete:
{"publishedWithinDays": 1,"fullCorpusMode": true,"maxItems": 0}
Reachable nurse vacancies in Stockholm, agencies excluded:
{"searchQueries": ["sjuksköterska"],"region": ["01"],"onlyWithContact": true,"excludeStaffingAgencies": true,"maxItems": 500}
ATS displacement list — companies hiring with no applicant-tracking system:
{"onlyWithoutAts": true,"onlyWithOrgNumber": true,"dedupeByEmployer": true,"publishedWithinDays": 7,"fullCorpusMode": true,"maxItems": 0}
A daily monitor that never re-bills you for an ad you already have:
{"stateKey": "sweden-daily","publishedWithinDays": 1,"fullCorpusMode": true,"occupationField": ["apaJ_2ja_LuF"],"maxItems": 0}
🇸🇪 Three Swedish gotchas this actor handles for you
1. Freetext is diacritic-sensitive. Measured live on 2026-09-05: q=sjukskoterska returns 0
ads; q=sjuksköterska returns 1,915. Never transliterate å ä ö.
2. Region and municipality codes are ZERO-PADDED STRINGS, and a bad one is a silent success.
Measured 2026-09-05: region=01 returns 10,681 ads. region=1 returns HTTP 200 with total 0 —
not an error, just an empty run that looks fine. This actor validates and pads every code, accepts
the plain Swedish place name (Stockholm, Göteborg, Västra Götaland) and refuses to run on a
code it cannot resolve rather than handing you a clean, empty, billed result.
3. AND/OR only bites with smart matching off. Arbetsförmedlingen enriches freetext with
machine-learned synonyms. Measured 2026-09-05 on sjuksköterska stockholm: with enrichment on, AND
and OR both return 236; with Turn off smart freetext matching on, OR returns 12,106 and AND
returns 313.
📊 Measured output fill
Every number below comes from one real platform run I made, not an estimate: a complete day sweep
of 2026-09-04, re-run on 2026-09-07 as run iDb78sXSeDHqPH5gN at the actor's declared
512 MB, outputShape: full — 2,268 read, 2,268 delivered, 2,268 unique ids, zero duplicates,
172 seconds, delivered == billed. 70 columns per row.
The same window was swept on 2026-09-05 as run forqADgU3QsybN16A and returned 2,297 ads: ads
are removed from the index as their deadlines pass, so the same calendar day shrinks slightly every
time you re-measure it. Every fill percentage in the table below reproduced within 0.3 points
across those two sweeps (ats_vendor 50.0% → 50.2%, apply_url 88.1% → 88.1%, has_ats:false
11.9% → 11.9%, contact_name 47.5% → 47.5%, is_reachable 57.8% → 57.8%). What does move is the
SLICE — see the 2,000-newest run below the table.
| Field | Fill over the 2,268 delivered rows of iDb78sXSeDHqPH5gN |
|---|---|
id, platsbanken_ad_url, webpage_url, headline, employer_name, employer_workplace, occupation, occupation_group, occupation_field, employment_type, salary_type, publication_date, application_deadline, last_publication_date, description_text, source_type, source_type_label, number_of_vacancies, days_live, days_to_deadline, timestamp, scraped_at | 100% |
org_number | 99.4% |
municipality, municipality_code, region, region_code | 98.7% |
experience_required (true on) | 93.3% |
city, postcode, latitude, longitude | 92.6% |
working_hours_type, scope_of_work_min/max | 90.1% |
duration | 89.9% |
apply_url | 88.1% |
apply_host | 88.0% |
logo_url | 81.3% |
street_address | 67.3% |
employer_url | 63.7% |
is_reachable (a direct e-mail or phone on the ad) | 57.8% |
ats_vendor (resolves to a named vendor) | 50.2% |
contact_name, contacts, contacts_count | 47.5% |
contact_email | 41.1% |
apply_reference | 37.4% |
contact_phone | 35.6% |
driving_license_required (true on) | 33.6% |
contact_title | 28.3% |
nice_to_have_work_experiences | 21.8% |
driving_license (specific class) | 16.2% |
apply_email | 13.8% |
is_staffing_agency (our own flag, true on) | 13.4% |
has_ats: false — no apply link at all | 11.9% |
must_have_languages | 11.3% |
must_have_work_experiences | 7.5% |
nice_to_have_languages / nice_to_have_skills | 5.1% / 4.3% |
must_have_education, apply_other, must_have_skills, labels | 1.4%–2.3% |
Those 2,268 ads came from 951 distinct organisation numbers (964 distinct employer names), so One row per employer turns a vacancy list into a company list at roughly 42% of the rows — and you are only billed for the rows you keep.
The other slice — and why the two disagree
A second, independent run over the 2,000 most recently published ads across all of Sweden
(run jP1jGT0bR51D6UdWf, 2026-09-07, 2,000 delivered / 2,000 unique ids / 140 seconds):
org_number 99.2%, a named contact on 45.1%, reachable 59.6%, contact_email 39.8%,
contact_phone 32.0%, a named ATS vendor on 40.5%, no apply link on 17.0%, and 918
distinct organisation numbers.
Read that ATS number carefully if the displacement list is why you are here. A weekday's ads resolve to a named vendor 50.2% of the time; a rolling 2,000 that reaches back across a weekend resolves 40.5%. Same actor, same parser, same day — the ads are different. The honest range is 40–50%, and the corresponding no-apply-link bucket is 12–17%. A single day and a rolling 2,000 are different slices of the board; neither is rounded to flatter the other, and this page quotes both rather than the better one.
⚠️ Five of those 70 columns are near-constant — read this before you count fields
Measured across the same 2,268 rows. These columns are shipped because they are what the API returns, but on a Swedish-market run they carry little or no row-level signal:
| Column | What it actually did over 2,268 rows |
|---|---|
workplace_model | One value on every row — "Arbete på plats". A dead API field, not a dead parser: see the no-op note below. Zero signal. |
removed | false on every row. The search index only carries live ads, so this can only ever be false here. It exists so a row's provenance is explicit. Zero signal. |
access_to_own_car | false on every row. Arbetsförmedlingen barely populates it. Zero signal today. |
country | 99.6% "Sverige" — but not a constant: 9 of 2,268 rows were abroad (Spanien 3, Schweiz 2, Norge, Cypern, Finland, Polen). Low signal, occasionally the point. |
country_code | The same 7 values as country (199 = Sweden on 99.6%). Low signal. |
All three constants held on the 2026-09-07 2,000-newest run (jP1jGT0bR51D6UdWf) too:
workplace_model one distinct value ("Arbete på plats" on 2,268 of 2,268 in the day sweep),
removed true on 0 rows, access_to_own_car true on 0 rows.
So of the 70 columns, 65 carry genuine row-level variation and three (workplace_model,
removed, access_to_own_car) are constants on this market. Nothing is hidden and nothing is
dropped — you just should not budget them as fields.
🔎 The ATS wedge, measured
Vendors resolved from the apply-URL host across the 2,268-ad day sweep (iDb78sXSeDHqPH5gN,
2026-09-07):
| Vendor | Ads | Vendor | Ads |
|---|---|---|---|
| Varbi | 317 | Recruto | 19 |
| Visma Recruit | 175 | Ena Portal | 11 |
| ReachMee | 113 | CareerHub | 10 |
| Recman | 112 | Ledigajobb.se | 8 |
| Teamtailor | 74 | Intelliplan | 8 |
| Bullhorn (Aplitrak) | 65 | Wopify | 8 |
| EasyApply | 46 | SmartRecruiters | 7 |
| Ponty | 40 | Webcruiter | 5 |
| Avaron | 29 | SAP SuccessFactors | 4 |
| Talentech | 29 | Jobbet.se | 4 |
| PowerInit | 27 | Jobtip / TalentAdore | 2 each |
| Workday | 23 | Greenhouse | 1 |
49 vendor host patterns are recognised in total (counted in src/taxonomy.js); 25 of them
appeared in this one day's ads, resolving 1,139 of 2,268 rows = 50.2%.
37.8% of ads point at the employer's own careers page or an unrecognised host — those come back
with apply_host filled and ats_vendor: null, which is a real answer, not a miss. There were
538 distinct apply hosts in the day, so the long tail is genuine and a host is never guessed at.
11.9% have no apply link at all (has_ats: false) — the displacement target.
Do not budget 50.2% as a constant. The same parser over the 2,000 most recently published ads
the same day (jP1jGT0bR51D6UdWf) resolved 40.5%, because a rolling 2,000 reaches back across
a weekend and weekend ads carry apply links less often. Across the two runs the honest span is
40–50% named vendor and 12–17% no apply link at all. Both runs are quoted, in full, above.
🚫 What this actor does NOT give you, stated plainly
- No salary figures. Re-measured 2026-09-05 over the 2,000 most recently published ads
(run
BEjkBLIO8FBrVejcC): the API's ownsalary_descriptionis filled on 3 of 2,000 — so thin that it is not emitted as a column at all. Thesalary_typecolumn is a pay structure label ("Fast månads- vecko- eller timlön"), not an amount, and it is named accordingly. Two rivals in this lane advertise salary data on their store page and cannot deliver it; we would rather say so than repeat it. workplace_modeldoes not filter, and does not vary. Re-measured 2026-09-05: the on-site concept returns the entire corpus unfiltered — the on-site total equalled the unfiltered total exactly, 41,246 = 41,246 — and remote and hybrid each return 0, because every live ad carries the on-site concept. The input is exposed and passed through honestly, with a runtime warning, and the output column is therefore a constant (see the near-constant table above). For remote work use Remote-friendly ads (phrase-matched, 675 live ads on 2026-09-05) instead.removedandaccess_to_own_carare constants on this market —falseon every row measured. They are shipped as-is rather than quietly dropped; see the near-constant table above.- Almost no employer e-mail or phone from the employer record. Re-measured over 2,000 ads on
2026-09-05 (run
BEjkBLIO8FBrVejcC):employer.emailis filled on 0 of 2,000 andemployer.phone_numberon 2 of 2,000. The contact details this actor returns come fromapplication_contacts[]and the application e-mail — real fields, honestly rated above. - Taxonomy skill and education tags are sparse —
must_have_skills1.5% andmust_have_education2.3% over the 2,268-row day (iDb78sXSeDHqPH5gN, 2026-09-07). They are returned, but they are not the product. qfields: "trait"is not offered. The API's own spec advertises it; re-checked 2026-09-05, the live API rejects it with HTTP 400 (trait is not a valid enum value). Shipping it would hand you a run that dies on the first call.- No paid "detail" step. Re-verified 2026-09-05 on 5 ads: the API's
/ad/{id}endpoint returns the same 38 keys with byte-identical values as the search hit; the search hit carries exactly one extra key, the internalrelevancescore. A second "detail" billing event would charge you for nothing, so there is one event and one price.
💰 Pricing
$0.0009 per job ad returned — one event, no start fee, no per-result auto-event.
- Ads dropped by your filters are never billed.
- Duplicates are collapsed before billing, so a partitioned sweep or several overlapping search keywords never charge you twice for one ad.
- With a state key, a daily monitor only bills for genuinely new vacancies.
- The whole live corpus: about 40,000 ads × $0.0009 ≈ $36–38 (41,246 ads measured 2026-09-05 = $37.12; the board hovers just above 40,000, so budget in that band rather than on a fixed figure).
- A daily monitor over every new Swedish ad: ~2,100–2,500/day ≈ $57–67 per month. The 2,268-ad day sweep above billed exactly 2,268 events = $2.04.
❓ FAQ
Is this legal? Platsbanken's ad data is published by Arbetsförmedlingen, a Swedish government agency, through JobTech Dev explicitly for reuse as open data. There is no authentication, no API key, no rate limit encountered across ~200 probe calls, and no anti-abuse control anywhere on the path. This actor reads the documented public endpoint and nothing else.
Does it need residential proxies? No — datacenter is the default and it is what every run on this page used. The full escalation ladder was measured at build time (2026-09-04) and all four tiers returned identical bytes: direct 1,449 ms, Apify datacenter 1,644 ms, residential SE 1,666 ms, residential US 2,849 ms. Residential is slower for no benefit. On the datacenter default, the 2,297-row sweep above completed in 92 seconds with no retries and no blocked requests.
How many requests does a full sweep take? Roughly 40–80 for a wide sweep. The one-day complete
sweep of 2,297 ads took 2 date windows and about 25 API calls (a free limit=0 probe per
window plus 100 rows per page) in 92 seconds.
Can I get every ad ever posted? The live corpus goes back to 2025-07-09 (confirmed
2026-09-05: the oldest live ad is dated 2025-07-09T15:34:40, and everything published before
2026-01-01 is down to 7 ads). Set fullCorpusMode: true with no date filter and maxItems: 0; the
sweeper finds the oldest ad in scope and works forward.
How do I watch specific companies? Put company names or Swedish organisation numbers into Employers. Dashes and spaces in an org number are stripped for you.
Read this before you budget the run. The API's own employer= parameter is a full-text match over
the employer name, not an exact one, and on its own it is useless for account-based prospecting:
measured 2026-09-06, employer="Norrköpings kommun" matched 4,061 ads — every municipality in
Sweden, because kommun alone matches 4,234 — while Norrköpings kommun itself had 43. A company
that does not exist, "Kommun som inte finns AB", matched 33,533 (81% of the board), and
employer="Spotify AB" matched 29,291 where employer="Spotify" matched 0.
This actor sends that parameter to narrow the crawl and then re-checks every row against the name
or organisation number you actually typed, dropping the rest before billing. Proven 2026-09-07:
employer=["Norrköpings kommun"] delivered and billed 22 rows, every one of them that employer
(tnSYbgB5S1WhKyEaA) — 22 and not 43, because 2,100 of the 4,061 full-text matches is as far as one
query reaches, which the run says out loud; the non-existent company delivered and billed 0
(qIiduKkmWRb4D8RcT).
Two consequences to expect in the log: "dropped by filters" will be large, and because a single
query reaches at most 2,100 rows, a common word in the company name can hit that ceiling before the
scan is finished — the run says so explicitly and tells you to turn on Sweep the whole corpus.
An organisation number is matched exactly and has none of that problem, so prefer it where you have
it.
How do I search by geography instead of by municipality? Set Centre point to
latitude,longitude and a Radius. Measured 2026-09-05 from central Stockholm
(59.3293,18.0686): 10 km → 7,016 ads, 100 km → 12,652.
Are the labels in Swedish? By default yes — that is what Arbetsförmedlingen publishes. Set
Label language to English to swap the closed vocabularies (occupation field, employment type,
working hours, duration, workplace model, pay structure) for an English gloss, or to Both to get
extra _en columns. Place names, specific occupation names and the ad text stay Swedish; machine-
translating 290 municipality names would be a fabrication, not data.
What is the stable key for deduplication? id — 100% present, unique per ad (2,268 distinct ids
across 2,268 rows in iDb78sXSeDHqPH5gN, and 2,000 across 2,000 in jP1jGT0bR51D6UdWf), and equal
to the trailing segment of webpage_url. Measured 2026-09-05 over 2,000 ads, the API's
external_id is filled on 4 of 2,000 (0.2%) and original_id on 0 of 2,000, so neither can be used
as a key.
Can it return removed or expired ads? No. The search index only carries live ads; every row this
actor returned measured removed: false (2,268 of 2,268 in iDb78sXSeDHqPH5gN, 2,000 of 2,000 in
jP1jGT0bR51D6UdWf). Fetching a specific expired ad id returns nothing, and that is named in the
log — "Not delivered and NOT billed:
Does it work as a scheduled monitor? Yes. Give it a State key, schedule it daily with
publishedWithinDays: 1 and fullCorpusMode: true, and it delivers only ads it has never delivered
under that key before. State is kept in a named key-value store, so it survives across runs.
What happens if my filter matches nothing? The run exits cleanly with zero rows — a FAILED run would be permanent on this actor's scorecard — and the log line names which of the possible causes actually applied on that run, from the state the actor is holding at the time: the API itself returned nothing; or your Skip first N results started past the end of the result set; or the client-side filters dropped every row that was read (with the filters named, and a warning if the 2,100-row per-query ceiling meant only part of the match set was ever examined); or everything read had already been delivered under your state key.
Which filter values are checked before the run starts? Region, municipality, country, occupation field, employment type, working hours, duration, driving licence, workplace model, occupation collection, freetext fields, sort and ATS vendor are resolved against a table, and an unresolvable value fails the run up front naming the bad value — because this API answers a bad code with HTTP 200 and zero rows rather than an error. Six inputs have no table to resolve against and are sent verbatim: Occupation group, Occupation name, Skill, Language, Label and Employers. For those the actor logs a notice at the start of the run saying so, and repeats it if the run comes back empty. Nothing is billed either way.
Which timezone are the date filters in? Date filters are evaluated in UTC, while
publication_date, application_deadline and last_publication_date come back from the API in
Stockholm local time (+01:00 winter, +02:00 summer). A one-day UTC window therefore puts about
1% of its rows in the neighbouring local calendar day — measured 2026-09-06, a
2026-09-03T00:00:00 .. 2026-09-04T00:00:00 request delivered 2,095 rows dated 09-03 and 20 dated
09-04 (run Rcl2sclubWqOAgXcL). The run logs this whenever a date filter is set.
Do the two "max" caps count rows read or rows delivered? Delivered — both of them, which is also what you are billed for. Max results and Max ads per search keyword keep paging past rows your client-side filters drop until the cap is filled or the source runs out, and if a run ends short of the cap the log says which of the two it was: the source was exhausted (a complete answer) or the 2,100-row per-query ceiling stopped it (a truncated one, fixed by turning on Sweep the whole corpus).
⚖️ Legal and fair use
Platsbanken ad data is open data published by Arbetsförmedlingen through
JobTech Dev for reuse, under Sweden's open-data framework. This actor reads
only the documented public jobsearch.api.jobtechdev.se endpoints, sends no authentication, and
bypasses no access control of any kind.
Personal data is your responsibility. application_contacts[] carries the names, direct e-mail
addresses and telephone numbers of identifiable individuals at the hiring employer. Under the GDPR
you are the controller for whatever you do with those rows: have a lawful basis, honour objection and
erasure requests, and observe Swedish and EU rules on unsolicited commercial contact. Arbetsförmedlingen
publishes these details so candidates can apply — that is not the same as consent to be added to a
marketing list, and this actor makes no representation that it is.