LinkedIn

search-people-advanced

linkedin.com · Cowork
Download for Coworkthe skill folder, zipped
How to use it in Coworkonce per skill
Download the skill (.zip)

One .zip per skill. Inside: SKILL.md with the intent and the frontmatter, and the code that runs in the page.

Open Skills in Cowork

Settings → Personalize → Skills. This is Cowork's own skill library, shared across every chat.

Add → pick the .zip

Click Add and choose the file you just downloaded. Cowork installs it once; no CLAUDE.md, no per-project setup.

Ask in plain language

The skill stays available in every session. Cowork runs it in your own Chrome, riding the session you are already signed into.

Side effectread
Authsession
Needsnavigate · evaluate
Summary

ADVANCED people search on LinkedIn covering ALL the filters of the 'All filters' panel, ONE or MANY searches with AUTO-PAGINATION.

Params
{"queries":"array of objects; each one requires page (pages optional, max 100). Rest of the filters optional: keywords, firstName, lastName, title, company, schoolText, network, geoUrn, currentCompany, pastCompany, school, industry, serviceCategory, profileLanguage, connectionOf, followerOf, openToVolunteer"}
Returns
{"count":"int","results":"array of { query, pagesFetched, count, results:[{ name, degree, slug, profileUrl, headline, location }] }","skipped":"int (if applicable)"}
SKILL.md103 lines

Intent

ADVANCED people search on LinkedIn covering ALL the filters of the 'All filters' panel, ONE or MANY searches with AUTO-PAGINATION. Single PARAM: queries = array of objects. Each query accepts ALL these fields (all optional except page):

  • keywords: general free text.
  • firstName / lastName / title / company / schoolText: EXACT free text (the URL adds quotes). title=Job title, company=Company(text), schoolText=School(text).
  • network: your network degrees, codes 'F'(1st) 'S'(2nd) 'O'(3rd+). Bare ('S' or 'F,S') or array (['F','S']).
  • geoUrn: location ID(s). Resolve the name to an ID with linkedin-geo-typeahead (e.g. Argentina=100446943). Bare or array.
  • currentCompany: CURRENT company ID(s) (e.g. Google=1441). pastCompany: PAST company ID(s) (e.g. IBM=1009).
  • school: school ID(s) (schoolFilter, e.g. ITBA=15090642).
  • industry: industry ID(s) (e.g. Software Development=4). serviceCategory: service category ID(s) (e.g. Consulting=220).
  • profileLanguage: profile language code(s) ('en','es','fr','pt'...).
  • connectionOf: memberId of a person to fetch THEIR connections. followerOf: memberId(s) to fetch a creator's followers (comes from the profileUrl/typeahead, format 'ACoAA...').
  • openToVolunteer: true to filter for interest in volunteering.
  • page: REQUIRED per query. pages: optional, how many pages from page (default 1, max 100). THE IDs ARE PASSED BY HAND: they come from the query-string of the filters panel (select the facet and read the URL) or, for geoUrn, via linkedin-geo-typeahead. The example IDs above are real and verified. FIXED internal RATE-LIMIT (NOT configurable): pool of 20 fetches + ~150ms jittered before each one. Total cap 100 fetches per call (reports skipped). ~10 results per page. Returns { count, skipped?, results:[{ query, pagesFetched, count, results:[{ name, degree, slug, profileUrl, headline, location }] }] }. Requires a session. Regex parser over SSR HTML; if the markup or a param name changes, recapture from the 'All filters' panel.

Preconditions

  • Logged-in session in the browser you drive (Logged-in LinkedIn session (www.linkedin.com cookies)).
  • If not logged in: navigate to https://linkedin.com and let the user log in, then retry.

Execute

  1. navigate → https://www.linkedin.com/feed/
  2. evaluate the Extractor below with params. It does the in-page work and returns the JSON in returns.

Extractor

( async (root, params) => { let qs = params && params.queries; if (typeof qs === 'string') { try { qs = JSON.parse(qs); } catch(e){ qs = []; } } if (!Array.isArray(qs)) qs = []; const CONC = 20; const DELAY = 150; const MAXTASKS = 100; const enc = encodeURIComponent; const sleep = ms => new Promise(r=>setTimeout(r,ms)); const wrapArr = v => { if (v == null) return ''; if (Array.isArray(v)) v = v.join(','); v = String(v).trim(); if (!v) return ''; if (v.charAt(0) === '[') return v; return '[' + v.split(',').map(s => '"' + s.trim() + '"').join(',') + ']'; }; const buildUrl = (q, pg) => { const parts = ['keywords=' + enc(q.keywords || '')]; const arrKeys = { geoUrn:'geoUrn', currentCompany:'currentCompany', pastCompany:'pastCompany', school:'schoolFilter', industry:'industry', serviceCategory:'serviceCategory', profileLanguage:'profileLanguage', network:'network', followerOf:'followerOf' }; for (const k in arrKeys) { const w = wrapArr(q[k]); if (w) parts.push(arrKeys[k] + '=' + enc(w)); } const strKeys = { firstName:'firstName', lastName:'lastName', title:'title', company:'company', schoolText:'schoolFreetext', connectionOf:'connectionOf' }; for (const k in strKeys) { const v = (q[k] == null ? '' : String(q[k]).trim()); if (v) parts.push(strKeys[k] + '=%22' + enc(v) + '%22'); } if (q.openToVolunteer === true || q.openToVolunteer === 'true' || q.openToVolunteer === 1) parts.push('openToVolunteer=' + enc('["true"]')); parts.push('page=' + enc(String(pg))); parts.push('origin=FACETED_SEARCH'); return 'https://www.linkedin.com/search/results/people/?' + parts.join('&'); }; const parse = (html) => { const re=/href="https://www.linkedin.com/in/([^"]+)"\s+componentkey="[0-9a-f-]+"/g; const starts=[]; let m; while((m=re.exec(html))){ const slug=m[1].split(/[/?#]/)[0]; starts.push({slug, idx:m.index}); } const noise=/^(Connect|Follow|Send message|Message|View profile|Premium|Status)$/i; const out=[]; for(let k=0;k<starts.length;k++){ let chunk=html.slice(starts[k].idx, (starts[k+1]?starts[k+1].idx:starts[k].idx+2500)); chunk=chunk.slice(chunk.indexOf('>')+1); const lines=chunk.split(/<[^>]+>/).map(s=>s.replace(/&/g,'&').replace(/'/g,"'").replace(/"/g,'"').trim()).filter(Boolean); if(!lines.length) continue; let name=lines[0], degree=null; const dm=name.match(/•\s*(1er|2do|3er.|1st|2nd|3rd.|\d+º)/); if(dm){degree=dm[1];name=name.replace(/•./,'').trim();} const di=lines.findIndex(l=>/^•/.test(l)); if(!degree && di>=0) degree=lines[di].replace(/[•\s]/g,''); const rest=lines.slice(1).filter(l=>!noise.test(l) && !/^•/.test(l) && l.length>1 && l!==name); const headline=rest[0]||null; const location=rest.find(l=>/,| y alrededores|Argentina|Spain|Espa|United|Francia|Brasil|Chile|M[eé]xico|Egypt|Egipto/.test(l) && l!==headline) || null; out.push({slug:decodeURIComponent(starts[k].slug), name, degree, headline, location, profileUrl:'https://www.linkedin.com/in/'+starts[k].slug}); } return out; }; let tasks=[]; qs.forEach((q,qi)=>{ if (q.page===undefined || q.page===null || String(q.page).trim()==='') return; const start=parseInt(String(q.page),10)||1; let n=parseInt(String(q.pages||'1'),10)||1; if(n<1)n=1; if(n>100)n=100; for(let pg=start; pg<start+n; pg++) tasks.push({qi,pg,q}); }); let skipped=0; if(tasks.length>MAXTASKS){ skipped=tasks.length-MAXTASKS; tasks=tasks.slice(0,MAXTASKS); } const fetched=new Array(tasks.length); let next=0; const worker = async () => { while(true){ const i=next++; if(i>=tasks.length) break; const t=tasks[i]; if(DELAY>0) await sleep(DELAY0.5 + Math.random()*DELAY); try{ const r=await fetch(buildUrl(t.q,t.pg),{credentials:'include'}); const h=await r.text(); fetched[i]={qi:t.qi,pg:t.pg,people:parse(h)}; } catch(e){ fetched[i]={qi:t.qi,pg:t.pg,people:[],error:String(e)}; } } }; await Promise.all(Array.from({length:Math.min(CONC,tasks.length||1)}, worker)); const results=qs.map((q,qi)=>{ if (q.page===undefined || q.page===null || String(q.page).trim()==='') return {query:q,error:'page is required',count:0,results:[]}; const mine=fetched.filter(f=>f&&f.qi===qi).sort((a,b)=>a.pg-b.pg); const seen=new Set(); const merged=[]; mine.forEach(f=>f.people.forEach(p=>{ if(p.slug && !seen.has(p.slug)){seen.add(p.slug); merged.push(p);} })); return {query:q, pagesFetched:mine.length, count:merged.length, results:merged}; }); const ret={count:results.length, results}; if(skipped) ret.skipped=skipped; return ret; } )

Success assertion

Response text contains "LinkedIn".

search-people-advanced: the LinkedIn skill in Cowork · browser-memory