LinkedIn Job Search Scraper — Job Postings & LinkedIn Salary avatar

LinkedIn Job Search Scraper — Job Postings & LinkedIn Salary

Pricing

$30.00 / 1,000 jobs

Go to Apify Store
LinkedIn Job Search Scraper — Job Postings & LinkedIn Salary

LinkedIn Job Search Scraper — Job Postings & LinkedIn Salary

Scrape LinkedIn job listings from any LinkedIn job search or search URL — keyword, location or company, last 24 hours, week or month, job type, experience level and remote filters. No login, no cookies. Every posting: description, salary, apply link, company and hiring-team contacts; only new jobs.

Pricing

$30.00 / 1,000 jobs

Rating

0.0

(0)

Developer

Andrey Afanasenko

Andrey Afanasenko

Maintained by Community

Actor stats

0

Bookmarked

111

Total users

9

Monthly active users

6 days ago

Last modified

Security

Limited permissions

Share

Apify Actor

Search LinkedIn jobs by keyword, location, or company and enrich any job posting URL — full description, salary, hiring-team contacts and company details. Built for recruiters, sourcers, and lead-gen teams. No login or cookies.

LinkedIn's own job APIs are partner-only — access is granted to hiring customers, for their own postings. This LinkedIn job scraper reads any public posting without a login or cookies and returns 54 columns per row: the apply link, the full description, salary_display and structured salary_details, the hiring_team contacts, seniority, job type and job functions, plus company size, industry and HQ.

🎯 What it does

You feed the Actor LinkedIn job criteria — keywords + location, a list of company URLs, or specific job-posting URLs — it returns clean, structured rows ready for a CRM, sheet, or downstream agent.

ModeInputWhat it returns
1: Search Jobs (searchJobs)Keywords + LinkedIn geo code + optional date / type / level / workplace filters — or a LinkedIn job search linkFull job rows — each result comes with its description, salary and hiring team. Cost cap: maxItemsMode1.
2: Fetch Job Details (fetchJobDetails)LinkedIn job posting URLs (one per line) — and, if you switch it on, job links from company career sites1 row per URL, 41 detail columns incl. apply link, full description, salary, hiring team, company. Cost cap: maxItemsMode2.
3: Search Company Jobs (searchCompanyJobs)LinkedIn company URLs + optional keywordFull job rows for every open posting at the listed companies. Cost cap: maxItemsMode3.

⚠️ Pick your mode first. All three modes have their own dedicated input section. Filling the wrong mode's fields is the #1 reason runs return zero rows. Always set "Operation Mode" before touching anything else.

What each column is actually for:

You getWhy it matters
hiring_team — the recruiter or manager on the posting, with their LinkedIn profileWho to write to. This is the column that turns a job board into a lead list, and it is the one LinkedIn's own API will not give you for postings you don't own
salary_display plus structured salary_details (min, max, period, currency)Benchmark pay by role and city without parsing prose. The structured object is what you aggregate; the display string is what you show
salary_from_textThe pay range the description itself states — often there when salary_display is empty (measured: pay known for 71 postings in 146 instead of 29)
visa_sponsorship, experience_years_minWhether the posting says the employer sponsors work visas (yes / no, empty when it does not say), and the fewest years of experience it asks for
job_functions and industriesWhat kind of role it is and in which market — countable and groupable, not free text you have to read
applies and viewsHow contested the posting is. 3 applicants at 400 views is a different job from 300 at 400
experience_level, job_type, remote_allowClosed values, so you can sort and filter instead of guessing from the title
employee_count, employee_range, industries, company_description, the seven hq_* fieldsAccount scoring in the same row as the vacancy — no second lookup to decide whether this company is worth the outreach
contact_emailThe email address the employer put in the job description for applying or asking — accommodation, privacy and fraud-report mailboxes are left out. Most postings have none; it is filled only when the description gives one
company_is_agencyWhether the posting comes from a staffing or recruiting agency rather than the employer — true when LinkedIn lists the company under Staffing and Recruiting, Executive Search or Temporary Help
apply_url and apply_methodWhere a candidate applies: the employer's own application page (company_site), or the LinkedIn posting when it is Easy Apply only (easy_apply)
posted, expire_at, closed, expiredWhether the role is still live and when the posting is due to come down. A stale posting wastes an outreach slot
job_url, job_urn, company_linkedin_urlThe keys you join on. A posting's title changes; its URN does not

54 columns in all: 10 job columns plus 41 detail columns on every delivered row, in all three modes, plus row_status / why_withheld and the input echo. skills is kept for compatibility and is always null.

7 advanced filters — title contains / NOT contains, company name allow-list, location contains, minimum salary, posted-within-N-days, remote-only — applied to LinkedIn jobs in all modes, plus two exclusions that run before the charge: excluded companies and staffing agencies.

Your own questions about each job (from 13 November 2026) — ask up to five questions ("Is the role open to candidates outside the US?") and every job comes back with short answers read from the posting, in the ai_answers column. $0.003 per answered job, only when you set questions (details below).

📦 What you get — output sample

One row per LinkedIn job, the same shape in every mode. If a job record cannot be read, the row still arrives with its search columns and the detail columns null.

{
"input": "https://www.linkedin.com/jobs/view/4344628911",
"company": "Qualcomm",
"company_linkedin_url": "https://www.linkedin.com/company/qualcomm",
"company_logo": "https://media.licdn.com/dms/image/.../qualcomm_logo",
"job_title": "New Graduate - AI SW SDK Engineer",
"job_url": "https://www.linkedin.com/jobs/view/4344628911",
"job_urn": "4344628911",
"location": "Santa Clara, CA",
"posted_time": "2026-05-18 11:33:11",
"remote": "On-site",
"salary": "",
"applies": 0,
"apply_method": "company_site",
"apply_url": "https://careers.qualcomm.com/careers/job/…",
"benefits": [],
"closed": false,
"company_description": "Qualcomm is a world leader in 3G, 4G and next-generation wireless technologies …",
"company_id": "1337",
"company_is_agency": false,
"company_name": "Qualcomm",
"company_public_id": "qualcomm",
"contact_email": "",
"employee_count": 38000,
"employee_range": "10001+",
"experience_level": "Entry level",
"experience_years_min": 0,
"expire_at": "2026-06-17 11:33:11",
"expired": "2026-06-17 11:33:11",
"follower_count": 4600000,
"hiring_team": { "linkedin_url": "https://www.linkedin.com/in/jane-r", "name": "Jane Recruiter", "profile_image_url": null, "title": "University Talent Acquisition" },
"hq_address_line1": "5775 Morehouse Drive",
"hq_address_line2": null,
"hq_city": "San Diego",
"hq_country": "US",
"hq_full_address": "5775 Morehouse Drive, San Diego, CA, US",
"hq_postalcode": "92121",
"hq_region": "California",
"industries": ["Wireless Services", "Semiconductor Manufacturing"],
"job_description": "We are looking for a New Graduate AI SW SDK Engineer to join …",
"job_functions": ["Engineering", "Information Technology"],
"job_id": "4344628911",
"job_location": "Santa Clara, CA, United States",
"job_type": "Full-time",
"posted": "2026-05-18 11:33:11",
"remote_allow": "On-site",
"salary_details": null,
"salary_display": "$110,000 - $140,000",
"salary_from_text": "$110,000 - $140,000 per year",
"skills": null,
"specialities": ["wireless", "semiconductor", "5G"],
"views": 1240,
"visa_sponsorship": "",
"row_status": "delivered",
"why_withheld": "N/A"
}

Detail columns (applies through views) are filled in all three modes. apply_url is the employer's application page when the posting links to one (apply_method = company_site, most postings), otherwise the LinkedIn posting itself, where the Easy Apply button sits (easy_apply); both are empty when the posting says nothing about applying. expire_at is the date LinkedIn takes the posting down — expired carries the same date (or false) for older parsers. hiring_team is one object when the posting names one person, a list when it names several, and {} when it names nobody. skills is always null: job rows no longer carry a skills list. salary_from_text, visa_sponsorship and experience_years_min are read from the description by fixed rules and are filled only when the description states them — an empty value means "not stated", never "no". contact_email is the first address the employer published in the description for applying or asking about the job (a careers or recruiting mailbox, or the recruiter's own), "" when there is none — about one posting in twenty-five has one. company_is_agency is true for a staffing or recruiting agency, false for any other company, null when LinkedIn names no industry for it or the job's full record did not come back.

On the free plan, the results also tell you what was left behind. When the run hits the free cap with matches still remaining, the jobs that were already fetched but not delivered are published at the end of the same dataset with row_status = "withheld_free_plan" and a why_withheld sentence naming exactly what is locked and how many jobs the search matched in total. Title, company and location are real on those rows; the link, salary and details are not. Those rows are never charged, and a paid run never produces them.

⚡ How to use

  1. Choose Operation Mode at the top of the input panel.
  2. The matching 🎯 Mode N section appears below — fill its primary field (keywords / job URLs / company URLs).
  3. Optionally tune the 🔬 Advanced filtering section.
  4. Set the per-mode cap (maxItemsModeN) if you want a hard ceiling. Leave at 0 for unlimited (paid plan only).
  5. Click Save & Start.

If you start a run with no input filled in, the Actor uses a built-in demo seed and caps the output at 10 rows — see the USER_MESSAGE record in the run's Storage tab.

Recruiting a daily delta? Save your filled-in search as an Apify Task, set Skip jobs already delivered to you (skipDelivered), and schedule it to run daily — each run brings only the postings you have not received yet, and the ones you already have are not charged again. See the Only new jobs section below.

🧭 When to use it

  • Recruiting and sourcing — a LinkedIn job search by keyword, narrowed by the geo cascade (198 countries → 1,147 regions and states → 932 major cities → any LinkedIn-indexed place typed by hand) and by searchJobsDatePosted, searchJobsExperienceLevels, searchJobsJobTypes and searchJobsUnder10Applicants — so you see the roles worth applying pressure to, not the whole board.
  • Hiring-team contacts for outbound — Mode 2 returns hiring_team on each posting alongside company_linkedin_url, employee_range and industries, which is the account context you need in the same row as the person.
  • LinkedIn salary benchmarking — salary_display for reading and salary_details (min, max, period, currency) for aggregating, grouped by job_title, location and experience_level.
  • Competitor hiring intelligence — Mode 3 takes company LinkedIn URLs and returns every open role at each of them, so new job postings at named accounts show up as ordinary rows — with skipDelivered on, a daily run returns only the roles that are new since the last one.
  • Market research — job_functions, industries and experience_level per posting, with applies and views to tell a contested role from a quiet one.
  • Bulk enrichment of LinkedIn jobs by URL — paste a list of posting links and get all 41 detail columns per link; postings that returned nothing are named in the run report and are never charged.

🎯 Example tasks

Ready-to-run examples — each opens a pre-configured task you can run in one click (no setup):

  • Scrape LinkedIn jobs by keyword — Search LinkedIn job postings by keyword and location — export title, company, location, salary, posted date and apply link.
  • Find Data Analyst jobs on LinkedIn — Export current Data Analyst job openings from LinkedIn — company, location, salary, posted date and direct apply link.
  • Search Product Manager jobs on LinkedIn — Find Product Manager roles on LinkedIn by location — export company, title, salary, posted date and apply link as structured data.
  • Scrape remote jobs on LinkedIn — Export current remote job openings from LinkedIn by keyword — company, title, salary, posted date and direct apply link as structured data.
  • Find Software Engineer jobs on LinkedIn — Export current Software Engineer job openings from LinkedIn — company, location, salary, posted date and direct apply link as structured data.
  • Scrape jobs by company on LinkedIn — Get every open job at any company from its LinkedIn page — title, location, salary, posted date and apply link as structured data.

🛠 Operation modes

🎯 Mode 1: Search Jobs

Search LinkedIn jobs by keyword + location with optional filters (date posted, job type, experience level, workplace type, Easy Apply, low-applicant). 25 jobs per page; the Actor paginates until your cap is reached.

Required: searchJobsKeywords (free text) — or searchJobsUrls. Geo defaults to United States.

Or paste a LinkedIn search link (searchJobsUrls). Run the search on linkedin.com/jobs, copy the address, paste it — the link is the search: its keywords, location (geoId, or location= text, resolved like Custom Location), date posted (f_TPR), job type (f_JT), experience level (f_E), on-site / remote / hybrid (f_WT), industries (f_I), companies (f_C), salary floor (f_SB2), Easy Apply (f_AL), Under 10 applicants (f_EA) and "Most recent" sort are used, and the search fields of the form are not read. A date window LinkedIn has but the search does not (the last hour, say) is widened to the next one; any other link option the search cannot apply is named in the log. Several links run one after another under one job limit. The 🔬 filters, the exclusions and Only new jobs apply to link searches as to any other.

Geo targeting — four fields, most specific non-empty wins (top to bottom on the input panel):

  • Country (searchJobsGeoCode) — 198 countries, required default (US).
  • State / province / region (searchJobsState, optional) — ~1500 entries covering top 70 markets. Overrides Country.
  • Major city (searchJobsCity, optional) — top 1000 cities globally by population. Overrides State + Country.
  • Custom Location (searchJobsLocationCustom, optional) — type ANY LinkedIn-indexed place ("Cluj-Napoca", "Tartu", "Pune", "Brooklyn") or paste a numeric LinkedIn geoId. Resolved live at run start. Overrides everything above.

⚠️ Make sure Operation Mode is "Mode 1: Search Jobs". Mode-2 / Mode-3 fields are ignored here.

🎯 Mode 2: Fetch Job Details

Enrich an array of LinkedIn job-posting URLs with all 41 detail columns (apply link, description, salary, hiring team, company, …). Use this after Mode 1 to drill into the postings you care about.

Required: fetchJobDetailsUrls (array of LinkedIn job URLs). Optional toggle: include hiring team (default on).

If some links return nothing (the posting was closed, filled, or removed), the run report says exactly how many — empty links are never charged.

Job links from company career sites — Workday, Greenhouse, Lever, Ashby, SmartRecruiters, Workable, BambooHR, Personio, Teamtailor, Jobvite, iCIMS, Recruitee, UltiPro, Oracle Recruiting Cloud, ADP Workforce Now. This Actor reads LinkedIn only, so by default such a link is skipped and not charged, and the run message names it. Switch on Read career-site job links (fetchJobDetailsCareerSites, off by default) and Mode 2 hands those links to our Company Career Page Jobs actor:

  • it starts as a second run in your own Apify account, after the LinkedIn links, and is listed in your runs next to this one;
  • that run charges its own price — $0.03 per company, one charge for all the linked jobs of one company; a job that is no longer open, a board it cannot find and a link it cannot read are free. This run charges nothing for those links — the LinkedIn price ($0.03 per job) applies to LinkedIn links only;
  • it gets what is left of this run's maximum cost per run, and ends before this run does;
  • its jobs are copied into this dataset in the same columns, with source = "career_site", the career system in ats, and charged_by / charged_by_run_url naming the run that charged — see the Career-site jobs view. LinkedIn-only columns (hiring team, company HQ, applicants) stay empty on those rows;
  • the run message, the status line and RUN_SUMMARY.careerSites say how many jobs came back and what that run charged;
  • the 🔬 filters, the exclusions, maxItemsMode2 and job questions apply to LinkedIn jobs only — when you set them, the run names them as not applied to the career-site links.

Links on other sites (Indeed, job boards, a company's own page) are skipped and not charged either way.

⚠️ Make sure Operation Mode is "Mode 2: Fetch Job Details". Mode-1 / Mode-3 fields are ignored here.

🎯 Mode 3: Search Company Jobs

Find every open job at one or more LinkedIn companies. The Actor resolves each company URL to its LinkedIn numeric id, then runs a single search across all of them — all locations, every role. Leave the keyword blank to get the complete listing; add one core keyword (e.g. "engineer") only when you need to narrow.

Some companies hire on their own careers site and list no jobs on their LinkedIn page. When a search comes back empty, the run report names each company with its live open-roles count — so you can tell "no roles listed" apart from "keywords too narrow".

Required: searchCompanyJobsCompanyUrls (array of company LinkedIn URLs). Optional: searchCompanyJobsKeywords. To limit by location, set the Mode 1 location fields — otherwise Mode 3 searches worldwide. Large companies can list 1,000+ open roles — set maxItemsMode3 to bound cost.

⚠️ Make sure Operation Mode is "Mode 3: Search Company Jobs". Mode-1 / Mode-2 primary fields are ignored here (only the Mode-1 location fields carry over, and only when you change them).

💸 Pricing

PAY_PER_EVENT — one price per job across all modes, plus one optional add-on you switch on yourself.

EventPriceWhen it fires
JOB_ANALYZED$0.030 per jobOnce per row pushed to the dataset (any mode).
JOB_QUESTIONS_ANSWERED$0.003 per job — from 13 November 2026Only when you set Your questions about each job (aiQuestions): once per delivered job that comes back with an answer to every question. A job that could not be answered is not charged for it. Leave the field empty and this never fires.

Career-site links in Mode 2 (only with Read career-site job links on) are read by a separate run of our Company Career Page Jobs actor in your account, which charges $0.03 per company — not per job — and this run charges nothing for them. Both charges are listed in your Apify billing under their own runs, and each copied row names the run that charged for it.

Apify platform compute (memory + storage + bandwidth) is on top of that and small for this workload — a 20-job Mode 1 run typically costs well under $0.05 in platform overhead.

🔬 Advanced filtering

Filters apply after the fetch, before the row lands in the dataset. The paid event still fires on the raw fetch — filters narrow your output, not your bill.

FilterEffect
filterTitleContainsDrop rows whose job title doesn't contain this substring.
filterTitleNotContainsDrop rows whose job title contains this substring (great for excluding "senior" or "intern").
filterCompanyNamesAllow-list — drop rows whose company name doesn't match one of the entries.
filterLocationContainsSubstring match against location.
filterSalaryMinDrop rows whose listed salary is below this. Note: LinkedIn rarely surfaces salary on search rows, so any non-zero floor drops most of them.
filterPostedWithinDaysDrop rows posted more than N days ago.
filterRemoteOnlyDrop rows not flagged remote.

🚫 Leave out companies and agencies — not charged

Unlike the filters above, these two run before the charge: a LinkedIn job left out here is never charged, in any mode.

FieldEffect
excludeCompaniesLeave out jobs from these companies — a company name (matched as whole words in any letter case: "Robert Half" also leaves out "Robert Half Technology", "Meta" does not leave out "Metabolic") or its LinkedIn company link. In Modes 1 and 3 the job is dropped before it is even read.
excludeStaffingAgenciesLeave out jobs posted by staffing and recruiting agencies — companies LinkedIn lists under Staffing and Recruiting, Executive Search or Temporary Help.

How agencies are recognised: by the company's own LinkedIn industry, the same value shown in company_is_agency on every row. An agency listed under another industry (an IT-services firm that also places contractors, for example) is kept — the rule never drops a direct employer for sounding like an agency. A search that is almost all agencies stops after a few hundred have been left out and says so, so narrow it (keywords, location) to reach more employers. A job whose company cannot be checked — LinkedIn names no industry for it, or its full record did not come back — is delivered free, with company_is_agency empty, so you can judge it by the company name; it is never charged. If every job found was left out, the run ends at $0 and USER_MESSAGE says how many each setting removed (all_excluded_v1).

⏭️ Only new jobs

Run the same search every day and get only what is new. Set Skip jobs already delivered to you (skipDelivered) in Mode 1 or Mode 3:

ValueLeaves out jobs delivered to you…
off (default)— nothing is skipped
7 / 30 / 90 / 180 / 365…in the last 7, 30, 90, 180 or 365 days. From the API any number of days works, e.g. "45"
forever…ever (never repeat)
  • Skipped jobs are not fetched and not charged. The run takes the next matches instead, so maxItemsModeN counts new jobs only.
  • Per search. A job counts as delivered for the same search — the same keywords, location, companies and search options (date posted, job type, level, workplace, industries, Easy Apply, under 10 applicants). The 🔬 filters and the job limit don't change it, so narrowing a filter keeps your history. A different search keeps its own.
  • Where the history lives: a key-value store named li-delivered-… in your own Apify Storage. Every Mode 1 and Mode 3 run records what it delivered — with the option on or off — starting with version 0.0.53, so the history is already there on the day you switch it on. Delete the store to start over.
  • Mode 2 never skips — there you name the exact postings you want.
  • If every match was already delivered, the run ends at $0 and USER_MESSAGE says so (no_new_jobs_delivered_v1). If the history cannot be read, the run stops before fetching anything, charges nothing, and asks you to start it again in a minute.
  • Two runs of the same search that overlap in time may still both deliver the same job.

🤖 Your questions about each job (AI)

Set Your questions about each job (aiQuestions) — up to five questions, one per line — and every LinkedIn job the run delivers comes back with an ai_answers object: { "your question": "short answer" }, read from the posting text only.

  • Starts on 13 November 2026. Before that date the questions are ignored: the jobs are delivered as always, the log says the answers start on 13 November, and nothing extra is charged.
  • Price: $0.003 per answered job, on top of the $0.03 job price — so a 100-job run with questions costs $3.30. Charged only on a job that comes back with an answer to every question; a job whose posting has no text, or that could not be answered, is delivered without ai_answers and is not charged for them.
  • Answers are short (about 15 words) and come from the posting alone. When the posting does not say, the answer is exactly Not stated — the Actor never guesses.
  • Any wording works: "Is relocation offered?", "Which programming languages are required?", "Is this a contract or a permanent role?". Three common questions are already answered for free in every row: pay range (salary_from_text), visa sponsorship (visa_sponsorship) and years of experience (experience_years_min).
  • All three modes, LinkedIn jobs only — jobs left out by your exclusions are never asked about, and career-site jobs read in Mode 2 do not get answers.
  • One run spends at most a fixed amount on answers (enough for thousands of long postings); past it the remaining jobs are delivered without answers and are not charged for them, and the log says so.

🎁 Free vs paid plan

Free planAny paid Apify plan
Jobs per runup to 20, in full (links, salary, details)your own maxItemsModeN — no cap
All 3 modes✓✓
Advanced filters✓✓
Matches beyond the cap🔒 preview rows: title, company & location visible; links, salary & details locked✓ delivered in full
Scheduling via Tasks✓ (capped at 20 jobs/run)✓ full runs

The first 20 jobs of a free run are complete rows — nothing inside them is masked. When a search matches more than 20, the next few matches are previewed at the end of your dataset (row_status = 'withheld_free_plan'; preview rows are never charged) and the run's USER_MESSAGE (Storage tab) says exactly how many jobs matched in total. Any paid plan lifts the cap — re-run the same search and the full result set, links and salary included, is yours.

📊 Output destinations

The Actor writes to the standard Apify dataset, which is reachable as JSON / CSV / Excel / RSS / HTML / XML via the Dataset API. Ready-to-pipe into:

📡 Live status & storage records

The Actor exposes a live progress page while it works (Apify Console → Live View) and writes named records to the run's key-value store. These are stable, machine-readable surfaces — read them with Actor.getValue(<KEY>) from another Actor, or over the REST API, instead of parsing the log.

RecordWhat is in it
status.htmlThe live status page — progress, mode, what has been fetched so far. Rendered during the run, not after it.
RUN_SUMMARYComplete run statistics: status, jobs found and charged, filter breakdown, cost, and any free-plan caps applied. skipDelivered holds what the delivery history did this run (window, jobs on record, skipped, recorded). careerSites (Mode 2) holds what became of career-site links: handed over or not, jobs back, the second run's id and link, and what it charged (careerJobsChargedUsd; thisRunChargedForThemUsd is always 0).
USER_MESSAGEA plain-English explanation of what happened, written whenever a run needs one — including every zero-result run, so a run that returned nothing still tells you why.
FREE_LIMITS_APPLIEDStable id codes for the free-plan ceilings this run hit; empty or absent on paid runs. Switch on this instead of grepping the log.

🤖 Programmatic / API use

Most of this Actor's traffic is machines, not people. Everything the input panel does is available over the API.

Start a run and get the rows back in one call:

curl -X POST "https://api.apify.com/v2/acts/afanasenko~linkedin-jobs-scraper/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
-H 'Content-Type: application/json' \
-d '{
"operationMode": "searchJobs",
"searchJobsKeywords": "data engineer",
"searchJobsLocationCustom": "Berlin",
"searchJobsDatePosted": "Past week",
"maxItemsMode1": 100
}'

Or fire it and poll, which is what you want for large company sweeps:

# start
curl -X POST "https://api.apify.com/v2/acts/afanasenko~linkedin-jobs-scraper/runs?token=$APIFY_TOKEN" \
-H 'Content-Type: application/json' \
-d '{"operationMode":"fetchJobDetails","fetchJobDetailsUrls":["https://www.linkedin.com/jobs/view/4344628911"]}'
# then, per run id
curl "https://api.apify.com/v2/actor-runs/<RUN_ID>?token=$APIFY_TOKEN"
curl "https://api.apify.com/v2/actor-runs/<RUN_ID>/dataset/items?format=csv&token=$APIFY_TOKEN"
curl "https://api.apify.com/v2/actor-runs/<RUN_ID>/key-value-store/records/RUN_SUMMARY?token=$APIFY_TOKEN"
  • Any format, no re-run — the same dataset is readable as JSON, CSV, Excel, XML, RSS or HTML via ?format=.
  • Scheduled — save an input as a Task and give it a schedule; add "skipDelivered": "forever" (or a number of days) and each run returns only jobs you have not received yet, with no diffing on your side.
  • Webhooks — point ACTOR.RUN.SUCCEEDED at your endpoint and skip polling entirely.
  • Agents — the Actor is callable through Apify's MCP server, so an assistant can run it as a tool and read the rows back without you writing glue code.
  • For unattended pipelines, branch on RUN_SUMMARY and FREE_LIMITS_APPLIED rather than on row count: a short result set and a capped result set are different situations, and those two records tell them apart.

❓ FAQ

Q: Do I need a LinkedIn account, cookies, or login? No. You don't connect your LinkedIn account and there's nothing to log in to — just enter keywords, company URLs, or job URLs and run. Only public LinkedIn job data is returned.

Q: My run returned zero rows. 99% of the time this is "wrong mode selected." Open the input panel, check Operation Mode at the top, then make sure the matching 🎯 Mode N section is the one you filled in.

Q: How do I get more results from a search? Keep your keywords to the core job title — extra words ("senior", "MBA", "remote") sharply narrow results and can return nothing. Broaden the location, widen "Date posted" to "Past month" or "Any time", and turn off narrow filters like "Easy Apply only". A single clear title in a big market (e.g. "Data Analyst" in the United States) returns the most.

Q: Salary is empty on most rows. salary_display is filled when the employer entered a salary in LinkedIn's own field — most don't. Many state the pay in the description instead: that range is in salary_from_text (in a sample of 146 postings, pay was known for 71 this way instead of 29).

Q: My target city / town isn't in the City dropdown. Use the Custom Location field — type any LinkedIn-indexed place (e.g. "Cluj-Napoca", "Tartu", "Brooklyn"). The Actor resolves it live against LinkedIn's typeahead at run start and logs the resolved name + ID so you can verify the pick.

Q: Can I paste a LinkedIn job search link instead of filling in the form? Yes — put it in Or paste LinkedIn job search links (searchJobsUrls) in Mode 1. The keywords, location and filters in the link are used as they are; the log shows what was read and names any link option the search cannot apply. From the API, urls works too.

Q: I already have a LinkedIn geoId from the URL — can I paste it directly? Yes. Paste the numeric ID (e.g. 102095887 for California) into Custom Location. The Actor detects 6–10 digit numeric inputs and skips the typeahead resolve.

Q: Are filters charged? The JOB_ANALYZED event fires when the job is fetched. Filters in the 🔬 Advanced filtering section run after the fetch, so they narrow your output but not your bill. If "all rows filtered" is a problem, loosen the filters and post-filter downstream. The two exclusions — Exclude companies and Leave out staffing agencies — are different: they run before the charge, and a job they leave out is never charged.

Q: Can I search several job titles at once? Yes — separate them with commas in the keyword field (e.g. Risk Analyst, Data Analyst, Financial Analyst). The Actor searches for jobs matching any of them. Custom Location works the same way: a comma list resolves to the first place LinkedIn recognises.

Q: Can I sort by posting date? Yes — set searchJobsSortBy to "Most recent" in Mode 1.

Q: How many jobs can I get in one run? The free plan saves up to 20 jobs per run, across all modes. On a paid plan there's no cap — you're limited only by the per-mode cost ceiling you set (maxItemsModeN). If a free run hits the limit, the run's USER_MESSAGE tells you how many more matched, and the jobs that were already fetched but not delivered are appended to the same dataset as preview rows (row_status = "withheld_free_plan") with their titles, companies and locations visible — so you can see what you didn't get without paying for it.

Q: How do I get every open role at a company? Use Mode 3 with the company's LinkedIn URL and leave the keyword field blank — you get the complete listing, all locations. If the run comes back empty, the run report shows the company's live open-roles count: some companies hire on their own careers site and list nothing on LinkedIn.

Q: Can I bulk-enrich a big list of job posting URLs? Yes. Mode 2 takes many job URLs at once and returns all 41 detail columns per posting. For very large lists, split into batches and set maxItemsMode2 to cap spend per run.

Q: Why did my Mode 2 run start a second run? You switched on Read career-site job links and some of your links were on a company career site (Workday, Greenhouse, Lever and the like), which this Actor cannot read. Those links went to our Company Career Page Jobs actor as a second run in your account. That run charged $0.03 per company for them; this run charged nothing for them. The rows have source = "career_site" and the link of the run that charged. Switch the option off and such links are skipped for free again.

Q: Can I schedule this to run daily? Yes — save your filled-in input as an Apify Task and add a schedule to it. Each scheduled run uses your saved input; free-plan scheduled runs are capped at 20 jobs per run like any other run.

Q: Can I get only the jobs I haven't received yet? Yes — set Skip jobs already delivered to you (skipDelivered) to a period or to "Ever delivered". Jobs this Actor already delivered to your account for the same search are skipped before they are fetched, and are never charged; the run returns the next new ones. Works in Mode 1 and Mode 3. See the Only new jobs section.

⏱ Timing & runtime

  • Mode 1: every result is read in full, six at a time — a free-plan run (20 jobs) usually takes 15–60 seconds, 30 jobs about half a minute to a minute.
  • Mode 2: usually 3–15 seconds per job URL, occasionally up to a minute when LinkedIn is slow to load (links are processed one at a time).
  • Mode 3: a little extra time per company URL to look up its LinkedIn id, then same as Mode 1.

Default Apify timeout is fine for runs ≤ a few hundred rows; bump the timeout in Run Options for larger sweeps.

You start with…You want to find…Use this actor
Job criteria / job-posting URLs / company URLsJob listings + full description, salary, hiring teamThis actor
Company names, careers pages or career-site job linksOpen jobs straight from the employer's career site (Workday, Greenhouse, Lever, Ashby and more)Company Career Page Jobs
LinkedIn profile URLs or search criteriaProfiles, companies, decision-makers (70+ fields)LinkedIn Profile Scraper
Job titles, companies or countriesA list of people — employees and decision makers, up to 2,500 per searchLinkedIn People Search
Instagram handlesProfile data + 5 discovery modes (followers, similar, keyword, location)Instagram Profile Scraper
TikTok profile / hashtagProfile data + postsTikTok Profile Scraper
YouTube channel URL / handleChannel + video metadataYouTube Channel Scraper
US ZIP / addressProperty listings + market dataZillow Scraper

🆘 Support

Found a bug, a missing field, or a posting that didn't enrich? Use the Issues tab on this Actor's Apify Store page — it's the fastest path to a fix.

💡 Tips & Best Practices

Getting max results

  • Mode 1: keep keywords to the core job title; comma-separate several titles for an OR search (Risk Analyst, Data Analyst).
  • Geo targeting: the most specific non-empty field wins — Custom Location beats City beats State beats Country.
  • Mode 3: leave the keyword blank to get every open role at the listed companies; add one core keyword only when you need to narrow.

Cost optimization

  • Use maxItemsModeN aggressively when testing — 5-10 rows to validate the output shape, then scale up.
  • Mode 1 and Mode 3 rows already carry every detail column — there is no need to send search results through Mode 2 again.
  • Filters don't reduce billing (they run after the fetch) — narrow with keywords and location first, filter second.

Performance

  • Mode 1: a 20-job run usually takes 15–60 seconds. Mode 2: usually 3–15 seconds per URL (one at a time), occasionally up to a minute. Mode 3: a little extra time per company URL to look up its LinkedIn id.
  • The live status page shows progress in real time; the default Apify timeout is fine for runs up to a few hundred rows.

Data quality

  • salary is empty when the employer did not publish a salary on the posting — that is most postings outside the US.
  • Job postings expire quickly; enrich URLs via Mode 2 soon after collecting them.
  • hiring_team contacts appear when LinkedIn shows them publicly on the posting — not every posting lists its recruiter.

⚖️ Disclaimer

This Actor scrapes only publicly available LinkedIn data. You are responsible for using the data in accordance with LinkedIn's Terms of Service, applicable privacy laws (GDPR, CCPA), and ethical-use guidelines. The Actor does not scrape private data, personal feeds, or content behind login walls.

📋 Changelog

See ./CHANGELOG.md for the version history.