Scrape job-posting search
One page of a LinkedIn JOB-POSTING search, addressed EITHER by filters (keywords is REQUIRED here, which no other engine on this surface demands) OR by a pasted /jobs/search-results/ url, which is the only way to hybrid or on-site work, several locations, a radius around one, verified jobs and the other facet segments LinkedIn ships: exactly one of the two. Rows are postings keyed by job_ln_id, with a tracking-free /jobs/view/ URL, title, hiring company, location plus workplace_type, listing age, the salary line as printed, and the Easy Apply / early-applicant / verified flags. Card text is read off an ENGLISH LinkedIn UI: an executor whose interface is another language returns zero rows and still reports success. Page on with has_more, never on paging.total.
Contract:
- MCP tool
scrape_linkedin_search_jobs, registry packagemcp.linkedin/linkedin_scraping, mountlinkedin.scraping. - Operation
action, response envelopeaction. - Flags: none.
Authorizations
Access token issued by gtm.service.id. Its access_identity claim carries team_sid, actor_sid and actor_type, and that team scope is authoritative.
Team scope for tokens that do not carry one. Ignored when the token already names a team.
Body
Request body of scrape_linkedin_search_jobs.
Executor account (ln_ac_...). OPTIONAL everywhere on this surface. Given: the call runs on that account ONLY; a saturated or held scraping bucket refuses 429 bucket_saturated (with retry_after). Omitted: the service auto-picks one of your connected accounts with remaining capacity (SN verbs pick only Sales-Navigator seats; 422 no_connected_accounts when none is ready, 429 when all are at capacity).
18^ln_ac_Ledger replay guard: a repeat call with the same (team, key) returns the stored outcome; no re-execution. Recommended on every search run. The KEY ALONE decides: the probe does not compare arguments, so reusing one key after changing the arguments hands back the FIRST result. On the twelve search verbs that matters twice over, because switching a call from filters to url (or back) under one key replays instead of running the new search. New search, new key.
128EXCLUSIVE with filters: send url OR filters, never both (422) and never neither (422). A search URL built in the LinkedIn UI, and the escape hatch for everything the filter vocabulary cannot express. MUST start with https://www.linkedin.com/jobs/search-results/ : a URL from another LinkedIn search screen is refused here and again by the backend (422 invalid_search_url), because running it would silently scrape the wrong thing. On the url half OUR number wins: the offset or page parameter baked into the pasted URL is overwritten, while every other parameter rides through untouched.
2048^https:\/\/www\.linkedin\.com\/jobs\/search-results\/EXCLUSIVE with url: send filters OR url, never both (422) and never neither (422). LinkedIn jobs-search filters. keywords is REQUIRED; everything else is optional, and a false boolean is the same as an absent one. Anything this vocabulary cannot express (hybrid or on-site work, more than one location, a radius around one, verified-jobs, and the other facet segments LinkedIn ships) is reachable by pasting the UI URL into the url field instead.
LinkedIn page number, default 1; ONE page per call, so re-call with page + 1 while paging.has_more. On the url half OUR number wins: the offset or page parameter baked into the pasted URL is overwritten, while every other parameter rides through untouched.
1 <= x <= 100