Aurelix

Guides

LinkedIn job alerts in Slack without logging in to LinkedIn

Get a Slack message with only the new LinkedIn jobs for your keywords and country, every morning, without a LinkedIn account or cookies. Working input, real output and the Python version.

LinkedIn Jobs Country Filter Scraper: price and examples

LinkedIn's own job alerts live in your LinkedIn inbox, need an account, and are hard to share with a team. If you want new postings for a few searches dropped into a Slack channel (a recruiting team, a job-board newsletter, a sales team watching who hires), you need three things: the public job search, a memory of what you already saw, and a Slack webhook.

This guide sets that up with no LinkedIn login at all, first with no code, then in Python.

Disclosure: I'm Aurelix, and I built the LinkedIn Jobs Scraper used below. It is a paid tool, priced per job returned; the current price is on its tool page.

What you need

  1. A Slack incoming webhook URL: in Slack, create an app (or use an existing one), turn on Incoming Webhooks and add one for your channel. It looks like https://hooks.slack.com/services/T000/B000/XXXX.
  2. An Apify account (the free plan works) to run the scraper on a schedule.

The scraper reads LinkedIn's public job search, the pages any logged-out visitor sees. It never logs in and never uses cookies, so there is no account to get blocked.

Option 1: no code

Open the tool on the Apify Store and use this input:

{
  "keywords": ["data engineer", "analytics engineer"],
  "country": "CA",
  "datePosted": "past24h",
  "maxResultsPerSearch": 100,
  "incremental": true,
  "stateKey": "ca-data-jobs",
  "slackWebhookUrl": "https://hooks.slack.com/services/T000/B000/XXXX"
}

What each part does:

  • country uses LinkedIn's exact country id, and results whose location names another country are dropped.
  • incremental turns on Only new jobs since last run: the run remembers every job it returned under stateKey and returns (and charges) only jobs it has not seen before.
  • slackWebhookUrl posts a short digest after each run: how many new jobs were saved and the top 10 with links.

Then add a schedule (Apify Console > Schedules, for example every weekday at 8:00) that runs this saved input. The first run returns everything that matches; later runs post only what is new.

Option 2: Python, with your own message format

If you want a different message (grouped by company, with the salary when LinkedIn shows one), run the scraper from Python and post to the webhook yourself:

pip install apify-client requests
import os
from collections import defaultdict

import requests
from apify_client import ApifyClient

client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("aurelix/linkedin-jobs").call(run_input={
    "keywords": ["data engineer"],
    "country": "CA",
    "datePosted": "past24h",
    "maxResultsPerSearch": 100,
    "incremental": True,
    "stateKey": "ca-data-jobs-python",
})
jobs = list(client.dataset(run["defaultDatasetId"]).iterate_items())

by_company = defaultdict(list)
for j in jobs:
    by_company[j["company"]].append(j)

lines = [f"*{len(jobs)} new data engineer jobs in Canada*"]
for company, items in sorted(by_company.items(), key=lambda x: -len(x[1])):
    for j in items:
        pay = f" · {j['salary_text']}" if j.get("salary_text") else ""
        lines.append(f"• <{j['job_url']}|{j['title']}> at {company}, {j['location']}{pay}")

if jobs:
    requests.post(os.environ["SLACK_WEBHOOK_URL"], json={"text": "\n".join(lines[:40])}, timeout=30)

Run it from cron, GitHub Actions or an Apify schedule. Because incremental keeps its memory on Apify under the stateKey, the script itself stores nothing.

What the rows look like

Real rows from a run searching "data engineer" in Canada, trimmed to the fields used above:

title company location seniority job type
Data Engineer Samsara Toronto, Ontario, Canada Mid-Senior level Full-time
Ingénieur principal de données LGS, an IBM Company Montreal, Quebec, Canada Mid-Senior level Full-time
Data Engineer (Snowflake, Azure Data Factory, Python & SQL) Cognizant Halifax, Nova Scotia, Canada Mid-Senior level Full-time

Each row also has job_url, posted_date, company_url, applicants, industries, salary_text (where LinkedIn shows one) and the full description.

Tips

  • One search per keyword. keywords runs one search each; to separate alerts per team, give each schedule its own stateKey and webhook.
  • Paste your own search. If you already filter on linkedin.com, copy the URL from the address bar into searchUrls; its filters are kept.
  • Cheaper list mode. Set includeDetails to false for title, company, location and link only, without opening each job page.

Limits

  • LinkedIn shows at most about 1,000 jobs per search; split broad searches by keyword or location.
  • The scraper uses Apify's datacenter proxy only. LinkedIn rate-limits those addresses, so very large runs can stop early; the run's status message says which searches did.
  • Salary appears only when LinkedIn shows it on the posting.

To send the same jobs to a spreadsheet instead of Slack, see Export new LinkedIn jobs to Google Sheets every day.

Links

Tools used