openapi: 3.2.0 info: title: Gtm Linkedin Scraping API version: '1.0' contact: name: GTM API url: https://gtm-api.com email: support@gtm-api.com license: name: Proprietary url: https://gtm-api.com/license description: 'Operations tagged linkedin_scraping across 2 of this provider''s published API definitions: gtm-api-openapi-openapi.yml, gtm-api-linkedin-openapi.yaml. Each path carries the servers of the definition it was published in.' servers: - url: https://app.gtm-api.com/linkedin/v4 description: Production, through the app.gtm-api.com gateway security: - BearerJwt: [] TeamSid: [] tags: - name: linkedin_scraping description: Registry package `mcp.linkedin/linkedin_scraping`, served on MCP mount `linkedin.scraping`. paths: /api/linkedin-scraping/build-search-url: post: operationId: scrape_linkedin_build_search_url summary: Build LinkedIn search URL description: 'Build the LinkedIn URL of a people or company search, regular or Sales Navigator, from that search''s filters: send exactly ONE of people / sales_nav_people / companies / sales_nav_companies. Facet values take an id or plain text; each text is resolved by the search''s own typeahead (the option labelled exactly so, else the first; a multi-word miss is retried by its first word), at most 10 lookups per call, each a paced scraping read (Sales Navigator ones need an SN seat). A text nothing matches is a 422 naming the closest options, except on Sales Navigator titles and companies, where it stays free text. Returns url (Sales Navigator in its ?query= form, the one imports read), filters in the exact shape the matching search tool takes, and resolved: per text, the id picked and the alternatives. All ids given: no LinkedIn call at all. Recruiter has no build: its URL comes from running scrape_linkedin_search_recruiter_people. Contract: - MCP tool `scrape_linkedin_build_search_url`, registry package `mcp.linkedin/linkedin_scraping`, mount `linkedin.scraping`. - Operation `action`, response envelope `action`. - Flags: paced: a call spends the `scraping` smart-limit bucket of the account it runs on, and calls of one bucket are spaced per account. A short wait is slept by the server; a longer one answers 429 `rate_limited` with `context.retry_after`, and the same call succeeds from that moment. Parallel calls on one account queue behind each other.' tags: - linkedin_scraping requestBody: required: false content: application/json: schema: $ref: '#/components/schemas/ScrapeLinkedinBuildSearchUrlRequest' responses: '200': description: '`action` success envelope.' content: application/json: schema: $ref: '#/components/schemas/ScrapeLinkedinBuildSearchUrlResponse' 4XX: $ref: '#/components/responses/McpClientError' 5XX: $ref: '#/components/responses/McpServerError' x-gtm-mcp-tool: scrape_linkedin_build_search_url x-gtm-mcp-mount: linkedin.scraping x-gtm-entity: linkedin_scraping x-gtm-operation: action x-gtm-envelope: action x-gtm-availability: ga x-gtm-dangerous: false x-gtm-mass-action: false x-gtm-step-eligible: false x-gtm-schedule-required: false x-gtm-tool-class: complex x-gtm-paced-bucket: scraping servers: - url: https://app.gtm-api.com/linkedin/v4 description: Production, through the app.gtm-api.com gateway /api/linkedin-scraping/company-decision-makers: post: operationId: scrape_linkedin_company_decision_makers summary: Scrape company decision makers description: 'Sales Navigator’s decision-makers panel for ONE company: the bounded, seniority-weighted leadership/buying-committee list SN computes. SN-capable executor required. Contract: - MCP tool `scrape_linkedin_company_decision_makers`, registry package `mcp.linkedin/linkedin_scraping`, mount `linkedin.scraping`. - Operation `action`, response envelope `action`. - Flags: paced: a call spends the `scraping` smart-limit bucket of the account it runs on, and calls of one bucket are spaced per account. A short wait is slept by the server; a longer one answers 429 `rate_limited` with `context.retry_after`, and the same call succeeds from that moment. Parallel calls on one account queue behind each other.' tags: - linkedin_scraping requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ScrapeLinkedinCompanyDecisionMakersRequest' responses: '200': description: '`action` success envelope.' content: application/json: schema: $ref: '#/components/schemas/ScrapeLinkedinCompanyDecisionMakersResponse' 4XX: $ref: '#/components/responses/McpClientError' 5XX: $ref: '#/components/responses/McpServerError' x-gtm-mcp-tool: scrape_linkedin_company_decision_makers x-gtm-mcp-mount: linkedin.scraping x-gtm-entity: linkedin_scraping x-gtm-operation: action x-gtm-envelope: action x-gtm-availability: ga x-gtm-dangerous: false x-gtm-mass-action: false x-gtm-step-eligible: false x-gtm-schedule-required: false x-gtm-tool-class: typical x-gtm-paced-bucket: scraping servers: - url: https://app.gtm-api.com/linkedin/v4 description: Production, through the app.gtm-api.com gateway /api/linkedin-scraping/company-employees: post: operationId: scrape_linkedin_company_employees summary: Scrape company employees description: 'List the people currently working at ONE company (the company page’s "employees" people-search, page-numbered). Heavy, page-numbered list. Contract: - MCP tool `scrape_linkedin_company_employees`, registry package `mcp.linkedin/linkedin_scraping`, mount `linkedin.scraping`. - Operation `action`, response envelope `action`. - Flags: paced: a call spends the `scraping` smart-limit bucket of the account it runs on, and calls of one bucket are spaced per account. A short wait is slept by the server; a longer one answers 429 `rate_limited` with `context.retry_after`, and the same call succeeds from that moment. Parallel calls on one account queue behind each other.' tags: - linkedin_scraping requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ScrapeLinkedinCompanyEmployeesRequest' responses: '200': description: '`action` success envelope.' content: application/json: schema: $ref: '#/components/schemas/ScrapeLinkedinCompanyEmployeesResponse' 4XX: $ref: '#/components/responses/McpClientError' 5XX: $ref: '#/components/responses/McpServerError' x-gtm-mcp-tool: scrape_linkedin_company_employees x-gtm-mcp-mount: linkedin.scraping x-gtm-entity: linkedin_scraping x-gtm-operation: action x-gtm-envelope: action x-gtm-availability: ga x-gtm-dangerous: false x-gtm-mass-action: false x-gtm-step-eligible: false x-gtm-schedule-required: false x-gtm-tool-class: typical x-gtm-paced-bucket: scraping servers: - url: https://app.gtm-api.com/linkedin/v4 description: Production, through the app.gtm-api.com gateway /api/linkedin-scraping/get-post-comments: post: operationId: scrape_linkedin_get_post_comments summary: Scrape post comments description: 'Direct LinkedIn read (bypasses our DB): one page of the COMMENTS on ONE post, live. This is the only comments read there is - a row carries the comment AND the person who wrote it (content, posted_at, comment_permalink, reactions_count, replies_count, is_pinned, is_edited + member id, nickname, name, headline), annotated with is_own, so reading "what was written" and "who commented" is a single call and never two. Target any post by URL, activity URN, or the backend urn a company-page post / newsletter issue / group thread carries (share, ugcPost, groupPost) in post. Top-level comments only: replies are not on this wire at all, replies_count is a count. Order with sort_order; one wire page per call, page on with cursor. Contract: - MCP tool `scrape_linkedin_get_post_comments`, registry package `mcp.linkedin/linkedin_scraping`, mount `linkedin.scraping`. - Operation `action`, response envelope `action`. - Flags: paced: a call spends the `scraping` smart-limit bucket of the account it runs on, and calls of one bucket are spaced per account. A short wait is slept by the server; a longer one answers 429 `rate_limited` with `context.retry_after`, and the same call succeeds from that moment. Parallel calls on one account queue behind each other.' tags: - linkedin_scraping requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ScrapeLinkedinGetPostCommentsRequest' responses: '200': description: '`action` success envelope.' content: application/json: schema: $ref: '#/components/schemas/ScrapeLinkedinGetPostCommentsResponse' 4XX: $ref: '#/components/responses/McpClientError' 5XX: $ref: '#/components/responses/McpServerError' x-gtm-mcp-tool: scrape_linkedin_get_post_comments x-gtm-mcp-mount: linkedin.scraping x-gtm-entity: linkedin_scraping x-gtm-operation: action x-gtm-envelope: action x-gtm-availability: ga x-gtm-dangerous: false x-gtm-mass-action: false x-gtm-step-eligible: false x-gtm-schedule-required: false x-gtm-tool-class: complex x-gtm-paced-bucket: scraping servers: - url: https://app.gtm-api.com/linkedin/v4 description: Production, through the app.gtm-api.com gateway /api/linkedin-scraping/get-post-reactors: post: operationId: scrape_linkedin_get_post_reactors summary: Scrape post reactors description: 'Direct LinkedIn read (bypasses our DB): live pull of who is reacting to ONE post - or ONE COMMENT - right now, as transient reactor objects with reaction_type in OUR write vocabulary (interested = the event-post reaction), annotated with is_own. Target a post by URL or urn in any family (activity, share, ugcPost, groupPost) in post, or target a comment by its urn (either form: urn:li:comment:(...) or the fsd urn:li:fsd_comment:(...)) to read the comment''s reactors. LinkedIn files a post''s reactions under its social thread, which is the ugcPost or groupPost urn for those posts and the activity urn for a share post; this tool names the thread for you and result.post.post_ln_id is the urn the feed was read by. One wire page per call; page on with cursor. Contract: - MCP tool `scrape_linkedin_get_post_reactors`, registry package `mcp.linkedin/linkedin_scraping`, mount `linkedin.scraping`. - Operation `action`, response envelope `action`. - Flags: paced: a call spends the `scraping` smart-limit bucket of the account it runs on, and calls of one bucket are spaced per account. A short wait is slept by the server; a longer one answers 429 `rate_limited` with `context.retry_after`, and the same call succeeds from that moment. Parallel calls on one account queue behind each other.' tags: - linkedin_scraping requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ScrapeLinkedinGetPostReactorsRequest' responses: '200': description: '`action` success envelope.' content: application/json: schema: $ref: '#/components/schemas/ScrapeLinkedinGetPostReactorsResponse' 4XX: $ref: '#/components/responses/McpClientError' 5XX: $ref: '#/components/responses/McpServerError' x-gtm-mcp-tool: scrape_linkedin_get_post_reactors x-gtm-mcp-mount: linkedin.scraping x-gtm-entity: linkedin_scraping x-gtm-operation: action x-gtm-envelope: action x-gtm-availability: ga x-gtm-dangerous: false x-gtm-mass-action: false x-gtm-step-eligible: false x-gtm-schedule-required: false x-gtm-tool-class: complex x-gtm-paced-bucket: scraping servers: - url: https://app.gtm-api.com/linkedin/v4 description: Production, through the app.gtm-api.com gateway /api/linkedin-scraping/get-post-resharers: post: operationId: scrape_linkedin_get_post_resharers summary: Scrape post resharers description: 'Direct LinkedIn read (bypasses our DB): live pull of the profiles that RESHARED one post, the third and typically highest-intent leg of the engagement trio. Targeting mirrors the reshare feed''s own grammar (2026-08-21): a post URL, an activity URN, or a backend urn:li:share: / urn:li:ugcPost: urn verbatim - NOT urn:li:groupPost:, which is refused 422 reshare_target_unsupported (a group post has no reshare feed). is_own annotated. When exposed, resharer_commentary carries the added text and reshare_urn is the reshare’s own activity URN (feed it back into get-post-comments / -reactors). One wire page per call; page on with cursor. Contract: - MCP tool `scrape_linkedin_get_post_resharers`, registry package `mcp.linkedin/linkedin_scraping`, mount `linkedin.scraping`. - Operation `action`, response envelope `action`. - Flags: paced: a call spends the `scraping` smart-limit bucket of the account it runs on, and calls of one bucket are spaced per account. A short wait is slept by the server; a longer one answers 429 `rate_limited` with `context.retry_after`, and the same call succeeds from that moment. Parallel calls on one account queue behind each other.' tags: - linkedin_scraping requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ScrapeLinkedinGetPostResharersRequest' responses: '200': description: '`action` success envelope.' content: application/json: schema: $ref: '#/components/schemas/ScrapeLinkedinGetPostResharersResponse' 4XX: $ref: '#/components/responses/McpClientError' 5XX: $ref: '#/components/responses/McpServerError' x-gtm-mcp-tool: scrape_linkedin_get_post_resharers x-gtm-mcp-mount: linkedin.scraping x-gtm-entity: linkedin_scraping x-gtm-operation: action x-gtm-envelope: action x-gtm-availability: ga x-gtm-dangerous: false x-gtm-mass-action: false x-gtm-step-eligible: false x-gtm-schedule-required: false x-gtm-tool-class: complex x-gtm-paced-bucket: scraping servers: - url: https://app.gtm-api.com/linkedin/v4 description: Production, through the app.gtm-api.com gateway /api/linkedin-scraping/param-id-lookup: post: operationId: scrape_linkedin_param_id_lookup summary: Look up LinkedIn facet id description: 'Facet-id typeahead for the REGULAR (non Sales Navigator) search: turn a human term ("United States", "SaaS", "Acme") into the id a filter member needs. One type per call, and the type picks the member: people → followers_of, connections → connections_of, location → locations, company → current_companies, industry → industries, service_category → service_categories. Rows are the dropdown itself, best match first, about ten of them, with no paging and no size knob. An empty rows list means the dropdown offered nothing for that term, which is an answer, not an error. It types into the account''s already-open tab, so no page is loaded, no search is spent and the people-search commercial-use limit is untouched. Cheapest method here. Sales Navigator ids are a different space: use scrape_linkedin_sales_nav_param_id_lookup. Contract: - MCP tool `scrape_linkedin_param_id_lookup`, registry package `mcp.linkedin/linkedin_scraping`, mount `linkedin.scraping`. - Operation `action`, response envelope `action`. - Flags: paced: a call spends the `scraping` smart-limit bucket of the account it runs on, and calls of one bucket are spaced per account. A short wait is slept by the server; a longer one answers 429 `rate_limited` with `context.retry_after`, and the same call succeeds from that moment. Parallel calls on one account queue behind each other.' tags: - linkedin_scraping requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ScrapeLinkedinParamIdLookupRequest' responses: '200': description: '`action` success envelope.' content: application/json: schema: $ref: '#/components/schemas/ScrapeLinkedinParamIdLookupResponse' 4XX: $ref: '#/components/responses/McpClientError' 5XX: $ref: '#/components/responses/McpServerError' x-gtm-mcp-tool: scrape_linkedin_param_id_lookup x-gtm-mcp-mount: linkedin.scraping x-gtm-entity: linkedin_scraping x-gtm-operation: action x-gtm-envelope: action x-gtm-availability: ga x-gtm-dangerous: false x-gtm-mass-action: false x-gtm-step-eligible: false x-gtm-schedule-required: false x-gtm-tool-class: trivial x-gtm-paced-bucket: scraping servers: - url: https://app.gtm-api.com/linkedin/v4 description: Production, through the app.gtm-api.com gateway /api/linkedin-scraping/recruiter-param-id-lookup: post: operationId: scrape_linkedin_recruiter_param_id_lookup summary: Look up LinkedIn Recruiter facet id description: 'LinkedIn Recruiter facet-id typeahead: resolve one Recruiter search facet (type) for a typed query into the ids the scrape_linkedin_search_recruiter_people filters take (urn:li:ts_*:N; the lowercase language name for spoken_languages). A THIRD id space: neither the flagship nor the Sales Navigator lookup ids fit here. Eleven kinds, all text facets (query required); LinkedIn answers a fixed 10. Needs a Recruiter-seat executor. Costs a scraping-bucket slot per call. Contract: - MCP tool `scrape_linkedin_recruiter_param_id_lookup`, registry package `mcp.linkedin/linkedin_scraping`, mount `linkedin.scraping`. - Operation `action`, response envelope `action`. - Flags: paced: a call spends the `scraping` smart-limit bucket of the account it runs on, and calls of one bucket are spaced per account. A short wait is slept by the server; a longer one answers 429 `rate_limited` with `context.retry_after`, and the same call succeeds from that moment. Parallel calls on one account queue behind each other.' tags: - linkedin_scraping requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ScrapeLinkedinRecruiterParamIdLookupRequest' responses: '200': description: '`action` success envelope.' content: application/json: schema: $ref: '#/components/schemas/ScrapeLinkedinRecruiterParamIdLookupResponse' 4XX: $ref: '#/components/responses/McpClientError' 5XX: $ref: '#/components/responses/McpServerError' x-gtm-mcp-tool: scrape_linkedin_recruiter_param_id_lookup x-gtm-mcp-mount: linkedin.scraping x-gtm-entity: linkedin_scraping x-gtm-operation: action x-gtm-envelope: action x-gtm-availability: ga x-gtm-dangerous: false x-gtm-mass-action: false x-gtm-step-eligible: false x-gtm-schedule-required: false x-gtm-tool-class: trivial x-gtm-paced-bucket: scraping servers: - url: https://app.gtm-api.com/linkedin/v4 description: Production, through the app.gtm-api.com gateway /api/linkedin-scraping/sales-nav-param-id-lookup: post: operationId: scrape_linkedin_sales_nav_param_id_lookup summary: Look up Sales Navigator facet id description: 'Sales Navigator facet-id typeahead: resolve one SN facet (type) into the opaque ids the SN search filters need. type is a node salesApiFacetTypeahead kind passed through verbatim; the seven text facets resolve query, the rest return their fixed / account-scoped list. Type → target filter member: TITLE → current_titles/past_titles; BING_GEO → locations/company_headquarters; INDUSTRY → industries; COMPANY_WITH_LIST → current_companies/past_companies; GROUP → groups; SCHOOL → schools; CONNECTION_OF → connections_of; ACCOUNT_LIST → account_lists (company search). Skip it for the static kinds (COMPANY_SIZE/FUNCTION/SENIORITY_V2/RELATIONSHIP/COMPANY_TYPE/TENURE/PROFILE_LANGUAGE): their full id sets are inlined in the search filter schemas. PERSONA/LEAD_LIST/LEAD_INTERACTIONS/SAVED_LEADS_AND_ACCOUNTS have no filters member yet. SN executor required. Contract: - MCP tool `scrape_linkedin_sales_nav_param_id_lookup`, registry package `mcp.linkedin/linkedin_scraping`, mount `linkedin.scraping`. - Operation `action`, response envelope `action`. - Flags: paced: a call spends the `scraping` smart-limit bucket of the account it runs on, and calls of one bucket are spaced per account. A short wait is slept by the server; a longer one answers 429 `rate_limited` with `context.retry_after`, and the same call succeeds from that moment. Parallel calls on one account queue behind each other.' tags: - linkedin_scraping requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ScrapeLinkedinSalesNavParamIdLookupRequest' responses: '200': description: '`action` success envelope.' content: application/json: schema: $ref: '#/components/schemas/ScrapeLinkedinSalesNavParamIdLookupResponse' 4XX: $ref: '#/components/responses/McpClientError' 5XX: $ref: '#/components/responses/McpServerError' x-gtm-mcp-tool: scrape_linkedin_sales_nav_param_id_lookup x-gtm-mcp-mount: linkedin.scraping x-gtm-entity: linkedin_scraping x-gtm-operation: action x-gtm-envelope: action x-gtm-availability: ga x-gtm-dangerous: false x-gtm-mass-action: false x-gtm-step-eligible: false x-gtm-schedule-required: false x-gtm-tool-class: trivial x-gtm-paced-bucket: scraping servers: - url: https://app.gtm-api.com/linkedin/v4 description: Production, through the app.gtm-api.com gateway /api/linkedin-scraping/search-companies: post: operationId: scrape_linkedin_search_companies summary: Scrape LinkedIn company search description: 'One page of a regular LinkedIn company search, addressed EITHER by `filters` (keywords, geo ids, industry ids, headcount buckets) OR by a pasted /search/results/companies/ `url`: exactly one of the two, never both, never neither. Rows are company previews, not people, carrying the card''s industry, followers (LinkedIn''s rounded count) and location next to the identity. Same engine and the same company rows either way; page on with paging.has_more. For revenue, growth, follower, Fortune and buying-signal facets use scrape_linkedin_search_sales_nav_companies instead: this engine has none of them. Contract: - MCP tool `scrape_linkedin_search_companies`, registry package `mcp.linkedin/linkedin_scraping`, mount `linkedin.scraping`. - Operation `action`, response envelope `action`. - Flags: paced: a call spends the `scraping` smart-limit bucket of the account it runs on, and calls of one bucket are spaced per account. A short wait is slept by the server; a longer one answers 429 `rate_limited` with `context.retry_after`, and the same call succeeds from that moment. Parallel calls on one account queue behind each other.' tags: - linkedin_scraping requestBody: required: false content: application/json: schema: $ref: '#/components/schemas/ScrapeLinkedinSearchCompaniesRequest' responses: '200': description: '`action` success envelope.' content: application/json: schema: $ref: '#/components/schemas/ScrapeLinkedinSearchCompaniesResponse' 4XX: $ref: '#/components/responses/McpClientError' 5XX: $ref: '#/components/responses/McpServerError' x-gtm-mcp-tool: scrape_linkedin_search_companies x-gtm-mcp-mount: linkedin.scraping x-gtm-entity: linkedin_scraping x-gtm-operation: action x-gtm-envelope: action x-gtm-availability: ga x-gtm-dangerous: false x-gtm-mass-action: false x-gtm-step-eligible: false x-gtm-schedule-required: false x-gtm-tool-class: typical x-gtm-paced-bucket: scraping servers: - url: https://app.gtm-api.com/linkedin/v4 description: Production, through the app.gtm-api.com gateway /api/linkedin-scraping/search-courses: post: operationId: scrape_linkedin_search_courses summary: Scrape LinkedIn Learning course search description: 'One page of a LinkedIn LEARNING course search, the sixth row kind here: `filters` OR a pasted /search/results/learning/ `url` (that screen, NOT /courses/), exactly one of the two. filters is a REQUIRED keywords plus two closed vocabularies, difficulty and time_to_complete, whose snake_case values the NODE maps onto LinkedIn''s own labels, so never send a label yourself; the screen''s software and subject facets need the url form. Rows are courses keyed by course_slug (this wire has no numeric id), with a tracking-free /learning/ URL, title, instructor, a normalised duration, the release and viewer lines as printed, and artwork. WARNING, the English dependency is TOTAL, as on groups: the title is read out of the "Save the course X" button label and a card without a readable title is DROPPED, so a non-English executor returns ZERO rows at success. Page on has_more, never on paging.total. Contract: - MCP tool `scrape_linkedin_search_courses`, registry package `mcp.linkedin/linkedin_scraping`, mount `linkedin.scraping`. - Operation `action`, response envelope `action`. - Flags: paced: a call spends the `scraping` smart-limit bucket of the account it runs on, and calls of one bucket are spaced per account. A short wait is slept by the server; a longer one answers 429 `rate_limited` with `context.retry_after`, and the same call succeeds from that moment. Parallel calls on one account queue behind each other.' tags: - linkedin_scraping requestBody: required: false content: application/json: schema: $ref: '#/components/schemas/ScrapeLinkedinSearchCoursesRequest' responses: '200': description: '`action` success envelope.' content: application/json: schema: $ref: '#/components/schemas/ScrapeLinkedinSearchCoursesResponse' 4XX: $ref: '#/components/responses/McpClientError' 5XX: $ref: '#/components/responses/McpServerError' x-gtm-mcp-tool: scrape_linkedin_search_courses x-gtm-mcp-mount: linkedin.scraping x-gtm-entity: linkedin_scraping x-gtm-operation: action x-gtm-envelope: action x-gtm-availability: ga x-gtm-dangerous: false x-gtm-mass-action: false x-gtm-step-eligible: false x-gtm-schedule-required: false x-gtm-tool-class: typical x-gtm-paced-bucket: scraping servers: - url: https://app.gtm-api.com/linkedin/v4 description: Production, through the app.gtm-api.com gateway /api/linkedin-scraping/search-events: post: operationId: scrape_linkedin_search_events summary: Scrape event search description: 'One page of a LinkedIn EVENT search. `filters` carries keywords and NOTHING else (this engine has no location, date, type or organizer facet, and any other key is refused by name), so a pasted /search/results/events/ `url` is the only way to any facet the events screen itself offers: send exactly one of the two. Rows are events keyed by event_ln_id, with a tracking-free /events/ URL, title, the date and location lines as printed, the organizer when the card names one, the blurb, the attendee counter (0 is real, null is unread) and a cover image. The English dependency is PARTIAL: rows still come back on another interface language, but date_text and location_text can swap and organizer and attendee_count go null, silently. Page on with has_more, never on paging.total. Contract: - MCP tool `scrape_linkedin_search_events`, registry package `mcp.linkedin/linkedin_scraping`, mount `linkedin.scraping`. - Operation `action`, response envelope `action`. - Flags: paced: a call spends the `scraping` smart-limit bucket of the account it runs on, and calls of one bucket are spaced per account. A short wait is slept by the server; a longer one answers 429 `rate_limited` with `context.retry_after`, and the same call succeeds from that moment. Parallel calls on one account queue behind each other.' tags: - linkedin_scraping requestBody: required: false content: application/json: schema: $ref: '#/components/schemas/ScrapeLinkedinSearchEventsRequest' responses: '200': description: '`action` success envelope.' content: application/json: schema: $ref: '#/components/schemas/ScrapeLinkedinSearchEventsResponse' 4XX: $ref: '#/components/responses/McpClientError' 5XX: $ref: '#/components/responses/McpServerError' x-gtm-mcp-tool: scrape_linkedin_search_events x-gtm-mcp-mount: linkedin.scraping x-gtm-entity: linkedin_scraping x-gtm-operation: action x-gtm-envelope: action x-gtm-availability: ga x-gtm-dangerous: false x-gtm-mass-action: false x-gtm-step-eligible: false x-gtm-schedule-required: false x-gtm-tool-class: typical x-gtm-paced-bucket: scraping servers: - url: https://app.gtm-api.com/linkedin/v4 description: Production, through the app.gtm-api.com gateway /api/linkedin-scraping/search-groups: post: operationId: scrape_linkedin_search_groups summary: Scrape group search description: 'One page of a LinkedIn GROUP search, the fifth row kind on this surface. `filters` carries keywords and nothing else, exactly like the events search, so a pasted /search/results/groups/ `url` is the only way to anything the groups screen offers beyond a keyword: send exactly one of the two. Rows are groups keyed by group_ln_id, with a /groups/ URL, the name, Public or Private, the member counter both as printed and parsed (0 is real, null is unread), a best-effort blurb and a logo. WARNING, the English dependency is TOTAL, worse than the jobs search: the name is read out of English UI copy and a card whose name cannot be read is dropped, so a non-English executor returns ZERO rows and still reports success. Page on with has_more, never on paging.total. Contract: - MCP tool `scrape_linkedin_search_groups`, registry package `mcp.linkedin/linkedin_scraping`, mount `linkedin.scraping`. - Operation `action`, response envelope `action`. - Flags: paced: a call spends the `scraping` smart-limit bucket of the account it runs on, and calls of one bucket are spaced per account. A short wait is slept by the server; a longer one answers 429 `rate_limited` with `context.retry_after`, and the same call succeeds from that moment. Parallel calls on one account queue behind each other.' tags: - linkedin_scraping requestBody: required: false content: application/json: schema: $ref: '#/components/schemas/ScrapeLinkedinSearchGroupsRequest' responses: '200': description: '`action` success envelope.' content: application/json: schema: $ref: '#/components/schemas/ScrapeLinkedinSearchGroupsResponse' 4XX: $ref: '#/components/responses/McpClientError' 5XX: $ref: '#/components/responses/McpServerError' x-gtm-mcp-tool: scrape_linkedin_search_groups x-gtm-mcp-mount: linkedin.scraping x-gtm-entity: linkedin_scraping x-gtm-operation: action x-gtm-envelope: action x-gtm-availability: ga x-gtm-dangerous: false x-gtm-mass-action: false x-gtm-step-eligible: false x-gtm-schedule-required: false x-gtm-tool-class: typical x-gtm-paced-bucket: scraping servers: - url: https://app.gtm-api.com/linkedin/v4 description: Production, through the app.gtm-api.com gateway /api/linkedin-scraping/search-jobs: post: operationId: scrape_linkedin_search_jobs summary: Scrape job-posting search description: '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 package `mcp.linkedin/linkedin_scraping`, mount `linkedin.scraping`. - Operation `action`, response envelope `action`. - Flags: paced: a call spends the `scraping` smart-limit bucket of the account it runs on, and calls of one bucket are spaced per account. A short wait is slept by the server; a longer one answers 429 `rate_limited` with `context.retry_after`, and the same call succeeds from that moment. Parallel calls on one account queue behind each other.' tags: - linkedin_scraping requestBody: required: false content: application/json: schema: $ref: '#/components/schemas/ScrapeLinkedinSearchJobsRequest' responses: '200': description: '`action` success envelope.' content: application/json: schema: $ref: '#/components/schemas/ScrapeLinkedinSearchJobsResponse' 4XX: $ref: '#/components/responses/McpClientError' 5XX: $ref: '#/components/responses/McpServerError' x-gtm-mcp-tool: scrape_linkedin_search_jobs x-gtm-mcp-mount: linkedin.scraping x-gtm-entity: linkedin_scraping x-gtm-operation: action x-gtm-envelope: action x-gtm-availability: ga x-gtm-dangerous: false x-gtm-mass-action: false x-gtm-step-eligible: false x-gtm-schedule-required: false x-gtm-tool-class: typical x-gtm-paced-bucket: scraping servers: - url: https://app.gtm-api.com/linkedin/v4 description: Production, through the app.gtm-api.com gateway /api/linkedin-scraping/search-people: post: operationId: scrape_linkedin_search_people summary: Scrape LinkedIn people search description: 'One page of a regular LinkedIn people search, addressed EITHER by `filters` OR by a pasted search `url`: exactly one of the two, never both, never neither. Use filters when the agent composes the search itself, url for a search already built in the LinkedIn UI or for anything the filter vocabulary cannot express. Same engine and the same person rows either way; page on with paging.has_more. Rows carry the card''s headline, location and degree badge next to the identity (position / company_name are Recruiter-only). commercial_use_limit_hit true means LinkedIn''s monthly search paywall fired and rows may be truncated: switch to the Sales Navigator people tool or wait for the reset. Contract: - MCP tool `scrape_linkedin_search_people`, registry package `mcp.linkedin/linkedin_scraping`, mount `linkedin.scraping`. - Operation `action`, response envelope `action`. - Flags: paced: a call spends the `scraping` smart-limit bucket of the account it runs on, and calls of one bucket are spaced per account. A short wait is slept by the server; a longer one answers 429 `rate_limited` with `context.retry_after`, and the same call succeeds from that moment. Parallel calls on one account queue behind each other.' tags: - linkedin_scraping requestBody: required: false content: application/json: schema: $ref: '#/components/schemas/ScrapeLinkedinSearchPeopleRequest' responses: '200': description: '`action` success envelope.' content: application/json: schema: $ref: '#/components/schemas/ScrapeLinkedinSearchPeopleResponse' 4XX: $ref: '#/components/responses/McpClientError' 5XX: $ref: '#/components/responses/McpServerError' x-gtm-mcp-tool: scrape_linkedin_search_people x-gtm-mcp-mount: linkedin.scraping x-gtm-entity: linkedin_scraping x-gtm-operation: action x-gtm-envelope: action x-gtm-availability: ga x-gtm-dangerous: false x-gtm-mass-action: false x-gtm-step-eligible: false x-gtm-schedule-required: false x-gtm-tool-class: typical x-gtm-paced-bucket: scraping servers: - url: https://app.gtm-api.com/linkedin/v4 description: Production, through the app.gtm-api.com gateway /api/linkedin-scraping/search-posts: post: operationId: scrape_linkedin_search_posts summary: Scrape post search description: 'LinkedIn content search: find POSTS by keywords, recency or author. The discovery front door for post-engagement plays (feed the returned post_ln_id into the commenters / reactors / resharers tools). Addressed EITHER by `filters` (keywords REQUIRED) OR by a pasted /search/results/content/ `url`, which the node parses back into the same filter object: exactly one of the two, never both, never neither. CURSOR paginated, unlike every other search here: feed paging.next_cursor back verbatim and size the page with page_size. A page is the end only when LinkedIn itself answers that the window is empty; an answer the platform cannot read is refused (503 linkedin_page_unreadable, retry in a minute), never returned as an empty page. Contract: - MCP tool `scrape_linkedin_search_posts`, registry package `mcp.linkedin/linkedin_scraping`, mount `linkedin.scraping`. - Operation `action`, response envelope `action`. - Flags: paced: a call spends the `scraping` smart-limit bucket of the account it runs on, and calls of one bucket are spaced per account. A short wait is slept by the server; a longer one answers 429 `rate_limited` with `context.retry_after`, and the same call succeeds from that moment. Parallel calls on one account queue behind each other.' tags: - linkedin_scraping requestBody: required: false content: application/json: schema: $ref: '#/components/schemas/ScrapeLinkedinSearchPostsRequest' responses: '200': description: '`action` success envelope.' content: application/json: schema: $ref: '#/components/schemas/ScrapeLinkedinSearchPostsResponse' 4XX: $ref: '#/components/responses/McpClientError' 5XX: $ref: '#/components/responses/McpServerError' x-gtm-mcp-tool: scrape_linkedin_search_posts x-gtm-mcp-mount: linkedin.scraping x-gtm-entity: linkedin_scraping x-gtm-operation: action x-gtm-envelope: action x-gtm-availability: ga x-gtm-dangerous: false x-gtm-mass-action: false x-gtm-step-eligible: false x-gtm-schedule-required: false x-gtm-tool-class: typical x-gtm-paced-bucket: scraping servers: - url: https://app.gtm-api.com/linkedin/v4 description: Production, through the app.gtm-api.com gateway /api/linkedin-scraping/search-products: post: operationId: scrape_linkedin_search_products summary: Scrape LinkedIn product search description: 'One page of a LinkedIn PRODUCT search, the seventh row kind here: `filters` OR a pasted /search/results/products/ `url`, exactly one of the two. filters is a REQUIRED keywords plus a one-way free_version toggle and two arrays of digit-string ids that come from DIFFERENT places: product_category from param-id-lookup type=product_category, product_company from the ordinary organization id space. Nothing reconciles them, so a swapped id returns an empty page, not an error. Rows are product pages keyed by product_slug (the wire calls it product_id, but it is a slug and must never be sent back as an id), with a tracking-free /products/ URL, name, vendor, tagline, top features and a connections counter. Unlike courses, a non-English executor still returns named rows here, but category_text and tagline can silently carry the WRONG text. Page on has_more, never on paging.total. Contract: - MCP tool `scrape_linkedin_search_products`, registry package `mcp.linkedin/linkedin_scraping`, mount `linkedin.scraping`. - Operation `action`, response envelope `action`. - Flags: paced: a call spends the `scraping` smart-limit bucket of the account it runs on, and calls of one bucket are spaced per account. A short wait is slept by the server; a longer one answers 429 `rate_limited` with `context.retry_after`, and the same call succeeds from that moment. Parallel calls on one account queue behind each other.' tags: - linkedin_scraping requestBody: required: false content: application/json: schema: $ref: '#/components/schemas/ScrapeLinkedinSearchProductsRequest' responses: '200': description: '`action` success envelope.' content: application/json: schema: $ref: '#/components/schemas/ScrapeLinkedinSearchProductsResponse' 4XX: $ref: '#/components/responses/McpClientError' 5XX: $ref: '#/components/responses/McpServerError' x-gtm-mcp-tool: scrape_linkedin_search_products x-gtm-mcp-mount: linkedin.scraping x-gtm-entity: linkedin_scraping x-gtm-operation: action x-gtm-envelope: action x-gtm-availability: ga x-gtm-dangerous: false x-gtm-mass-action: false x-gtm-step-eligible: false x-gtm-schedule-required: false x-gtm-tool-class: typical x-gtm-paced-bucket: scraping servers: - url: https://app.gtm-api.com/linkedin/v4 description: Production, through the app.gtm-api.com gateway /api/linkedin-scraping/search-recruiter-people: post: operationId: scrape_linkedin_search_recruiter_people summary: Scrape LinkedIn Recruiter people search description: 'One page of a LinkedIn Recruiter (talent) people search, by a structured filters object (the Recruiter facets: titles, skills, companies, locations, postal codes, industries, education, languages, seniority, function, company size and type, year sliders, network, open-to-work) OR by a pasted Recruiter search url. A Recruiter URL carries no filters, only the searchHistoryId of the search LinkedIn keeps on the seat, so the url half replays that stored search on your page; every answer returns search_history_id and search_url, so page 2 onwards is addressed by the URL alone. 25 hits a page, page 1..40, a real total; rows carry recruiter_id (for send_linkedin_recruiter_message), headline, location, current position, can_send_inmail. Needs a Recruiter-seat executor with a live session (422 recruiter_required, 409 recruiter_reauth_required); 429 recruiter_search_usage_limit is LinkedIn''s per-seat throttle, wait a minute. Ids via scrape_linkedin_recruiter_param_id_lookup. One scraping-bucket slot per page. Contract: - MCP tool `scrape_linkedin_search_recruiter_people`, registry package `mcp.linkedin/linkedin_scraping`, mount `linkedin.scraping`. - Operation `action`, response envelope `action`. - Flags: paced: a call spends the `scraping` smart-limit bucket of the account it runs on, and calls of one bucket are spaced per account. A short wait is slept by the server; a longer one answers 429 `rate_limited` with `context.retry_after`, and the same call succeeds from that moment. Parallel calls on one account queue behind each other.' tags: - linkedin_scraping requestBody: required: false content: application/json: schema: $ref: '#/components/schemas/ScrapeLinkedinSearchRecruiterPeopleRequest' responses: '200': description: '`action` success envelope.' content: application/json: schema: $ref: '#/components/schemas/ScrapeLinkedinSearchRecruiterPeopleResponse' 4XX: $ref: '#/components/responses/McpClientError' 5XX: $ref: '#/components/responses/McpServerError' x-gtm-mcp-tool: scrape_linkedin_search_recruiter_people x-gtm-mcp-mount: linkedin.scraping x-gtm-entity: linkedin_scraping x-gtm-operation: action x-gtm-envelope: action x-gtm-availability: ga x-gtm-dangerous: false x-gtm-mass-action: false x-gtm-step-eligible: false x-gtm-schedule-required: false x-gtm-tool-class: typical x-gtm-paced-bucket: scraping servers: - url: https://app.gtm-api.com/linkedin/v4 description: Production, through the app.gtm-api.com gateway /api/linkedin-scraping/search-sales-nav-companies: post: operationId: scrape_linkedin_search_sales_nav_companies summary: Scrape Sales Navigator company search description: 'One page of a Sales Navigator ACCOUNT search, the one engine here that filters on annual revenue, company and per-department headcount growth, department headcount, follower buckets, Fortune tier and buying signals (leadership changes, funding events). Addressed EITHER by `filters`, whose typeahead members take ids from scrape_linkedin_sales_nav_param_id_lookup while the static enums are inline, OR by a pasted /sales/search/company `url`: exactly one of the two. SN-capable executor required. Contract: - MCP tool `scrape_linkedin_search_sales_nav_companies`, registry package `mcp.linkedin/linkedin_scraping`, mount `linkedin.scraping`. - Operation `action`, response envelope `action`. - Flags: paced: a call spends the `scraping` smart-limit bucket of the account it runs on, and calls of one bucket are spaced per account. A short wait is slept by the server; a longer one answers 429 `rate_limited` with `context.retry_after`, and the same call succeeds from that moment. Parallel calls on one account queue behind each other.' tags: - linkedin_scraping requestBody: required: false content: application/json: schema: $ref: '#/components/schemas/ScrapeLinkedinSearchSalesNavCompaniesRequest' responses: '200': description: '`action` success envelope.' content: application/json: schema: $ref: '#/components/schemas/ScrapeLinkedinSearchSalesNavCompaniesResponse' 4XX: $ref: '#/components/responses/McpClientError' 5XX: $ref: '#/components/responses/McpServerError' x-gtm-mcp-tool: scrape_linkedin_search_sales_nav_companies x-gtm-mcp-mount: linkedin.scraping x-gtm-entity: linkedin_scraping x-gtm-operation: action x-gtm-envelope: action x-gtm-availability: ga x-gtm-dangerous: false x-gtm-mass-action: false x-gtm-step-eligible: false x-gtm-schedule-required: false x-gtm-tool-class: typical x-gtm-paced-bucket: scraping servers: - url: https://app.gtm-api.com/linkedin/v4 description: Production, through the app.gtm-api.com gateway /api/linkedin-scraping/search-sales-nav-people: post: operationId: scrape_linkedin_search_sales_nav_people summary: Scrape Sales Navigator people search description: 'One page of a Sales Navigator people search: richer facets than the regular engine (seniority, function, tenure, headcount, company type), no commercial-use limit, SN URNs (sn_id) on the rows. Addressed EITHER by `filters`, which carry the COMPLETE SN facet vocabulary, OR by a pasted /sales/search/people `url`: exactly one of the two. Typeahead members take [{id, text, exclude}] values from scrape_linkedin_sales_nav_param_id_lookup; the static-enum members list their ids inline and need no lookup. Needs an SN-capable executor (a non-SN own account gets 422 sales_nav_required; the auto-pick filters to SN seats). Contract: - MCP tool `scrape_linkedin_search_sales_nav_people`, registry package `mcp.linkedin/linkedin_scraping`, mount `linkedin.scraping`. - Operation `action`, response envelope `action`. - Flags: paced: a call spends the `scraping` smart-limit bucket of the account it runs on, and calls of one bucket are spaced per account. A short wait is slept by the server; a longer one answers 429 `rate_limited` with `context.retry_after`, and the same call succeeds from that moment. Parallel calls on one account queue behind each other.' tags: - linkedin_scraping requestBody: required: false content: application/json: schema: $ref: '#/components/schemas/ScrapeLinkedinSearchSalesNavPeopleRequest' responses: '200': description: '`action` success envelope.' content: application/json: schema: $ref: '#/components/schemas/ScrapeLinkedinSearchSalesNavPeopleResponse' 4XX: $ref: '#/components/responses/McpClientError' 5XX: $ref: '#/components/responses/McpServerError' x-gtm-mcp-tool: scrape_linkedin_search_sales_nav_people x-gtm-mcp-mount: linkedin.scraping x-gtm-entity: linkedin_scraping x-gtm-operation: action x-gtm-envelope: action x-gtm-availability: ga x-gtm-dangerous: false x-gtm-mass-action: false x-gtm-step-eligible: false x-gtm-schedule-required: false x-gtm-tool-class: typical x-gtm-paced-bucket: scraping servers: - url: https://app.gtm-api.com/linkedin/v4 description: Production, through the app.gtm-api.com gateway /api/linkedin-scraping/search-schools: post: operationId: scrape_linkedin_search_schools summary: Scrape LinkedIn school search description: 'One page of a LinkedIn SCHOOL search, the eighth row kind here: `filters` OR a pasted /search/results/schools/ `url`, exactly one of the two. filters is a REQUIRED keywords and NOTHING else, the same one-member shape the events search has, so every facet the schools screen offers lives only in the url form. There is NO school-id filter: the ids param_id_lookup returns for type=school fit nothing here. Rows are school pages keyed by school_slug (the wire calls it school_id, it is a slug, it can be URL-encoded, and it is not that lookup id), with a tracking-free /school/ URL, name, location line, a students-and-alumni counter and a blurb. Names and slugs survive a non-English executor, but description can silently carry the students line. Page on has_more, never on paging.total. Contract: - MCP tool `scrape_linkedin_search_schools`, registry package `mcp.linkedin/linkedin_scraping`, mount `linkedin.scraping`. - Operation `action`, response envelope `action`. - Flags: paced: a call spends the `scraping` smart-limit bucket of the account it runs on, and calls of one bucket are spaced per account. A short wait is slept by the server; a longer one answers 429 `rate_limited` with `context.retry_after`, and the same call succeeds from that moment. Parallel calls on one account queue behind each other.' tags: - linkedin_scraping requestBody: required: false content: application/json: schema: $ref: '#/components/schemas/ScrapeLinkedinSearchSchoolsRequest' responses: '200': description: '`action` success envelope.' content: application/json: schema: $ref: '#/components/schemas/ScrapeLinkedinSearchSchoolsResponse' 4XX: $ref: '#/components/responses/McpClientError' 5XX: $ref: '#/components/responses/McpServerError' x-gtm-mcp-tool: scrape_linkedin_search_schools x-gtm-mcp-mount: linkedin.scraping x-gtm-entity: linkedin_scraping x-gtm-operation: action x-gtm-envelope: action x-gtm-availability: ga x-gtm-dangerous: false x-gtm-mass-action: false x-gtm-step-eligible: false x-gtm-schedule-required: false x-gtm-tool-class: typical x-gtm-paced-bucket: scraping servers: - url: https://app.gtm-api.com/linkedin/v4 description: Production, through the app.gtm-api.com gateway /api/linkedin-scraping/search-service-providers: post: operationId: scrape_linkedin_search_service_providers summary: Scrape service-provider search description: 'One page of LinkedIn''s service-provider marketplace (freelancers and agencies), addressed EITHER by `filters` (keywords, LinkedIn''s own numeric service-category and geo ids, connection degree, profile language) OR by a pasted /search/results/services/ `url`, which is how to reach anything those five cannot express: exactly one of the two. Rows are the same person previews the people search returns, because the services screen carries no ratings and no service labels in its results, only provider profiles. Contract: - MCP tool `scrape_linkedin_search_service_providers`, registry package `mcp.linkedin/linkedin_scraping`, mount `linkedin.scraping`. - Operation `action`, response envelope `action`. - Flags: paced: a call spends the `scraping` smart-limit bucket of the account it runs on, and calls of one bucket are spaced per account. A short wait is slept by the server; a longer one answers 429 `rate_limited` with `context.retry_after`, and the same call succeeds from that moment. Parallel calls on one account queue behind each other.' tags: - linkedin_scraping requestBody: required: false content: application/json: schema: $ref: '#/components/schemas/ScrapeLinkedinSearchServiceProvidersRequest' responses: '200': description: '`action` success envelope.' content: application/json: schema: $ref: '#/components/schemas/ScrapeLinkedinSearchServiceProvidersResponse' 4XX: $ref: '#/components/responses/McpClientError' 5XX: $ref: '#/components/responses/McpServerError' x-gtm-mcp-tool: scrape_linkedin_search_service_providers x-gtm-mcp-mount: linkedin.scraping x-gtm-entity: linkedin_scraping x-gtm-operation: action x-gtm-envelope: action x-gtm-availability: ga x-gtm-dangerous: false x-gtm-mass-action: false x-gtm-step-eligible: false x-gtm-schedule-required: false x-gtm-tool-class: typical x-gtm-paced-bucket: scraping servers: - url: https://app.gtm-api.com/linkedin/v4 description: Production, through the app.gtm-api.com gateway /api/linkedin-scraping/similar-companies: post: operationId: scrape_linkedin_similar_companies summary: Scrape similar companies description: 'LinkedIn’s "similar companies / pages people also viewed" list for ONE target company: lookalike account expansion. One-shot list. Contract: - MCP tool `scrape_linkedin_similar_companies`, registry package `mcp.linkedin/linkedin_scraping`, mount `linkedin.scraping`. - Operation `action`, response envelope `action`. - Flags: paced: a call spends the `scraping` smart-limit bucket of the account it runs on, and calls of one bucket are spaced per account. A short wait is slept by the server; a longer one answers 429 `rate_limited` with `context.retry_after`, and the same call succeeds from that moment. Parallel calls on one account queue behind each other.' tags: - linkedin_scraping requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ScrapeLinkedinSimilarCompaniesRequest' responses: '200': description: '`action` success envelope.' content: application/json: schema: $ref: '#/components/schemas/ScrapeLinkedinSimilarCompaniesResponse' 4XX: $ref: '#/components/responses/McpClientError' 5XX: $ref: '#/components/responses/McpServerError' x-gtm-mcp-tool: scrape_linkedin_similar_companies x-gtm-mcp-mount: linkedin.scraping x-gtm-entity: linkedin_scraping x-gtm-operation: action x-gtm-envelope: action x-gtm-availability: ga x-gtm-dangerous: false x-gtm-mass-action: false x-gtm-step-eligible: false x-gtm-schedule-required: false x-gtm-tool-class: typical x-gtm-paced-bucket: scraping servers: - url: https://app.gtm-api.com/linkedin/v4 description: Production, through the app.gtm-api.com gateway /api/linkedin-scraping/similar-profiles: post: operationId: scrape_linkedin_similar_profiles summary: Scrape similar profiles description: 'LinkedIn’s "people also viewed" browsemap for ONE seed profile: lookalike expansion from a known-good persona. IMPORTANT: address by profile.nickname (the vanity slug) ONLY, because the wire navigates by vanityName, so a ln_id/sn_id URN is refused not_dispatchable (resolve it via enrich_linkedin_person_lite_profile first, then pass the nickname). Returns ~20 cards; rows carry ln_member_id + nickname + full_name + headline (no ACoAA id, no avatar, no degree). One-shot list (no pagination); limit caps the cards. Contract: - MCP tool `scrape_linkedin_similar_profiles`, registry package `mcp.linkedin/linkedin_scraping`, mount `linkedin.scraping`. - Operation `action`, response envelope `action`. - Flags: paced: a call spends the `scraping` smart-limit bucket of the account it runs on, and calls of one bucket are spaced per account. A short wait is slept by the server; a longer one answers 429 `rate_limited` with `context.retry_after`, and the same call succeeds from that moment. Parallel calls on one account queue behind each other.' tags: - linkedin_scraping requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ScrapeLinkedinSimilarProfilesRequest' responses: '200': description: '`action` success envelope.' content: application/json: schema: $ref: '#/components/schemas/ScrapeLinkedinSimilarProfilesResponse' 4XX: $ref: '#/components/responses/McpClientError' 5XX: $ref: '#/components/responses/McpServerError' x-gtm-mcp-tool: scrape_linkedin_similar_profiles x-gtm-mcp-mount: linkedin.scraping x-gtm-entity: linkedin_scraping x-gtm-operation: action x-gtm-envelope: action x-gtm-availability: ga x-gtm-dangerous: false x-gtm-mass-action: false x-gtm-step-eligible: false x-gtm-schedule-required: false x-gtm-tool-class: typical x-gtm-paced-bucket: scraping servers: - url: https://app.gtm-api.com/linkedin/v4 description: Production, through the app.gtm-api.com gateway components: schemas: ScrapeLinkedinSimilarProfilesRequest: type: object description: Request body of `scrape_linkedin_similar_profiles`. properties: linkedin_account_sid: type: - string - 'null' minLength: 18 maxLength: 18 pattern: ^ln_ac_ description: '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).' idempotency_key: type: - string - 'null' maxLength: 128 description: '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.' profile: type: object properties: ln_id: type: - string - 'null' maxLength: 128 description: Regular-profile URN (ACoAA…). sn_id: type: - string - 'null' maxLength: 64 description: Sales Navigator URN (ACwAA…). nickname: type: - string - 'null' maxLength: 100 description: Vanity slug. description: 'Target profile: exactly one of ln_id / sn_id / nickname (ln_member_id is not dispatchable, KNOWLEDGE §3d).' limit: type: integer minimum: 1 maximum: 100 description: Default 25. required: - profile ScrapeLinkedinSalesNavParamIdLookupRequest: type: object description: Request body of `scrape_linkedin_sales_nav_param_id_lookup`. properties: linkedin_account_sid: type: - string - 'null' minLength: 18 maxLength: 18 pattern: ^ln_ac_ description: '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).' idempotency_key: type: - string - 'null' maxLength: 128 description: '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.' type: type: string enum: - COMPANY_WITH_LIST - BING_GEO - INDUSTRY - TITLE - GROUP - SCHOOL - CONNECTION_OF - COMPANY_SIZE - FUNCTION - SENIORITY_V2 - RELATIONSHIP - COMPANY_TYPE - TENURE - PROFILE_LANGUAGE - PERSONA - ACCOUNT_LIST - LEAD_LIST - LEAD_INTERACTIONS - SAVED_LEADS_AND_ACCOUNTS description: The Sales Navigator facet kind to resolve (node salesApiFacetTypeahead `type`, passed through verbatim). The first seven are text facets that resolve `query`; the rest return their fixed / account-scoped list and ignore it. query: type: - string - 'null' minLength: 1 maxLength: 100 description: The typed term (text facets); ignored by static-enum / list facets. limit: type: integer minimum: 1 maximum: 25 description: Default 10. required: - type ScrapeLinkedinGetPostReactorsRequest: type: object description: Request body of `scrape_linkedin_get_post_reactors`. properties: linkedin_account_sid: type: - string - 'null' minLength: 18 maxLength: 18 pattern: ^ln_ac_ description: '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).' idempotency_key: type: - string - 'null' maxLength: 128 description: '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.' post: type: string maxLength: 512 description: 'A post URL, a post urn in any family, or a COMMENT urn (public or fsd form - passed to the wire verbatim, no post read: a comment IS the entity). Cheapest is the thread itself: a `urn:li:ugcPost:` or `urn:li:groupPost:-`, or a URL carrying one (a `-ugcPost--` share link, a `/feed/update/urn:li:ugcPost:/` permalink), read directly. An `urn:li:activity:` (bare, in a feed permalink or in a `/posts/-activity--` share link) and a `urn:li:share:` cost one extra post read first, the same read as enrich_linkedin_post_details and cached 7 days, so only the first call on a post pays it: an activity urn does not say whether it wraps a ugcPost or a share post, and the reactions read by the wrong key come back as an empty page. So if you already hold a post''s post_urn and it is a ugcPost, pass that. A repost without commentary reads the original''s reactions. An `lnkd.in` shortlink is expanded server-side at no cost; a linkedin.com link with no id in it (a bare slug URL) is resolved by opening the page through get_activity_urn_by_url, one more page load, cached 7 days. A post LinkedIn returns no post for (deleted, or not visible to the executing account) is refused 422 post_not_resolvable. The post does NOT need to be tracked or owned by you.' page_size: type: integer minimum: 1 maximum: 100 description: Default 50. cursor: type: - string - 'null' description: Opaque resume token; null = first page. required: - post ScrapeLinkedinCompanyDecisionMakersResponse: type: object properties: success: type: boolean enum: - true operation: type: string enum: - action action: type: string description: kebab-case verb; matches the route segment. item: {} result: type: object properties: rows: type: array items: type: object properties: ln_member_id: type: - string - 'null' description: Canonical member id (decoded in-process). ln_id: type: - string - 'null' sn_id: type: - string - 'null' nickname: type: - string - 'null' full_name: type: - string - 'null' headline: type: - string - 'null' description: The card's headline line on the regular and Recruiter engines; null on Sales Navigator people search. position: type: - string - 'null' description: Recruiter search only; the regular card prints no structured position (read the headline). company_name: type: - string - 'null' description: Recruiter search only; the regular card prints no structured company (read the headline). location: type: - string - 'null' description: The card's location line on the regular and Recruiter engines; null on Sales Navigator people search. avatar_url: type: - string - 'null' description: Best effort on the regular engines (a DOM lookup, null when the card image had not rendered). connection_degree: type: - string - 'null' description: 'The degree badge as the card prints it: 1st, 2nd, 3rd or 3rd+ on the regular engines; Recruiter spells 1st, 2nd, 3rd, out_of_network.' required: - ln_member_id - ln_id - sn_id - nickname - full_name - headline - position - company_name - location - avatar_url - connection_degree description: The transient result list, in LinkedIn’s order; NOTHING persisted. paging: {} data_request: type: object properties: {} description: 'The kind="scrape" DataRequest journal row for this call (terminal completed), embedded as result.data_request; served_from_cache always false. A receipt: its result_ref is null here, the page is result.rows next to it; get_data_request by sid reads the stored payload back. Full DataRequestDomain shape owned by ./data_requests.md, so it is left passthrough here.' required: - rows - paging - data_request meta: type: object properties: trace_id: type: string description: UUID v7; same 128-bit value as the X-Trace-Id header. span_id: type: string pattern: ^[0-9a-f]{16}$ description: 16 hex chars, root span of this request. timestamp: type: string description: ISO 8601 UTC (Y-m-dTH:i:sZ), response time. duration_ms: type: integer minimum: 0 description: Server-side wall clock. team_sid: type: - string - 'null' description: The team this call ran in (the token team, or the team_sid override). Null when unauthenticated; absent from pre-2026-08-20 backends. actor_type: type: - string - 'null' description: user | agent | api_key | system. Null when unauthenticated; absent from pre-2026-08-20 backends. required: - trace_id - span_id - timestamp - duration_ms pacing: type: object properties: linkedin_account_sid: type: string description: The account whose bucket the call spent. limit_type: type: string description: The smart-limit bucket the call spent, e.g. send_messages. next_call_after: type: string description: 'ISO 8601, the same form as retry_after: the moment this same call goes through again without waiting. The next pacing slot while the day has budget left, the daily reset when it has none, the end of a LinkedIn lock while one stands.' remaining_today: type: integer minimum: 0 description: daily_limit minus done_today_count of that bucket, after this call. required: - linkedin_account_sid - limit_type - next_call_after - remaining_today required: - success - operation - action - item - result - meta ScrapeLinkedinSearchPeopleResponse: type: object properties: success: type: boolean enum: - true operation: type: string enum: - action action: type: string description: kebab-case verb; matches the route segment. item: {} result: type: object properties: rows: type: array items: type: object properties: ln_member_id: type: - string - 'null' description: Canonical member id (decoded in-process). ln_id: type: - string - 'null' sn_id: type: - string - 'null' nickname: type: - string - 'null' full_name: type: - string - 'null' headline: type: - string - 'null' description: The card's headline line on the regular and Recruiter engines; null on Sales Navigator people search. position: type: - string - 'null' description: Recruiter search only; the regular card prints no structured position (read the headline). company_name: type: - string - 'null' description: Recruiter search only; the regular card prints no structured company (read the headline). location: type: - string - 'null' description: The card's location line on the regular and Recruiter engines; null on Sales Navigator people search. avatar_url: type: - string - 'null' description: Best effort on the regular engines (a DOM lookup, null when the card image had not rendered). connection_degree: type: - string - 'null' description: 'The degree badge as the card prints it: 1st, 2nd, 3rd or 3rd+ on the regular engines; Recruiter spells 1st, 2nd, 3rd, out_of_network.' required: - ln_member_id - ln_id - sn_id - nickname - full_name - headline - position - company_name - location - avatar_url - connection_degree description: The transient result list, in LinkedIn’s order; NOTHING persisted. paging: type: object properties: page: type: integer page_size: type: integer has_more: type: boolean total: type: - integer - 'null' required: - page - page_size - has_more - total description: 'Page-numbered paging: re-call with page + 1 while has_more.' data_request: type: object properties: {} description: 'The kind="scrape" DataRequest journal row for this call (terminal completed), embedded as result.data_request; served_from_cache always false. A receipt: its result_ref is null here, the page is result.rows next to it; get_data_request by sid reads the stored payload back. Full DataRequestDomain shape owned by ./data_requests.md, so it is left passthrough here.' commercial_use_limit_hit: type: boolean description: LinkedIn’s people-search commercial-use-limit upsell fired (wire wasStrike), so rows may be truncated; switch to the SN engine or wait for the monthly reset. required: - rows - paging - data_request - commercial_use_limit_hit meta: type: object properties: trace_id: type: string description: UUID v7; same 128-bit value as the X-Trace-Id header. span_id: type: string pattern: ^[0-9a-f]{16}$ description: 16 hex chars, root span of this request. timestamp: type: string description: ISO 8601 UTC (Y-m-dTH:i:sZ), response time. duration_ms: type: integer minimum: 0 description: Server-side wall clock. team_sid: type: - string - 'null' description: The team this call ran in (the token team, or the team_sid override). Null when unauthenticated; absent from pre-2026-08-20 backends. actor_type: type: - string - 'null' description: user | agent | api_key | system. Null when unauthenticated; absent from pre-2026-08-20 backends. required: - trace_id - span_id - timestamp - duration_ms pacing: type: object properties: linkedin_account_sid: type: string description: The account whose bucket the call spent. limit_type: type: string description: The smart-limit bucket the call spent, e.g. send_messages. next_call_after: type: string description: 'ISO 8601, the same form as retry_after: the moment this same call goes through again without waiting. The next pacing slot while the day has budget left, the daily reset when it has none, the end of a LinkedIn lock while one stands.' remaining_today: type: integer minimum: 0 description: daily_limit minus done_today_count of that bucket, after this call. required: - linkedin_account_sid - limit_type - next_call_after - remaining_today required: - success - operation - action - item - result - meta ScrapeLinkedinSearchCoursesResponse: type: object properties: success: type: boolean enum: - true operation: type: string enum: - action action: type: string description: kebab-case verb; matches the route segment. item: {} result: type: object properties: rows: type: array items: type: object properties: course_slug: type: string description: 'The /learning/{slug} path segment, and the ONLY identity a course row carries: this vertical has no numeric id anywhere on the wire. A card whose slug cannot be read is dropped rather than returned half-formed, and a repeat of a slug already seen on this page is dropped too.' course_url: type: string description: https://www.linkedin.com/learning/{course_slug}/ , composed from the slug, so it is stable and free of tracking parameters. title: type: string description: Course title, read out of the English "Save the course " button label. A card whose title cannot be read is dropped, so this is never null, which is exactly why a non-English executor returns an empty page instead of untitled rows. author: type: - string - 'null' description: 'The instructor, from the card''s "By: <name>" line with the prefix stripped. A bare NAME: no member id, no vanity slug, so joining an instructor back to a LinkedIn profile needs a second search.' duration_text: type: - string - 'null' description: 'Course length, NORMALISED rather than quoted: the parser reads LinkedIn''s accessible form ("6 hours and 14 minutes", "1 day and 10 hours"), folds days into hours and re-prints it compactly ("34h 10m", "45m"). It is not the string on the card and not a number of minutes, so parse it if you need arithmetic.' released_text: type: - string - 'null' description: The release line VERBATIM, prefix included ("Released Jan 2026"). Free text, not a date, and unlike author the "Released " prefix is deliberately not stripped. viewers_text: type: - string - 'null' description: 'The viewer counter exactly as printed ("12,345 viewers", "1.2M viewers"). Kept as text on purpose: LinkedIn rounds the compact form, so there is no honest integer to parse it into.' thumbnail_url: type: - string - 'null' description: 'Course artwork, widest rendition available. Best effort: a card renders several images and the most repeated asset is taken as the thumbnail.' position: type: - integer - 'null' description: The card's slot on THIS page, 0 based and page local, so it is not a rank across pages. Numbering happens before duplicate slugs are dropped, which can leave gaps. The parser always fills it, so null means the key was missing from the wire row. required: - course_slug - course_url - title - author - duration_text - released_text - viewers_text - thumbnail_url - position description: The transient result list, in LinkedIn’s order; NOTHING persisted. paging: type: object properties: page: type: integer page_size: type: integer has_more: type: boolean total: type: - integer - 'null' required: - page - page_size - has_more - total description: 'Page-numbered paging: re-call with page + 1 while has_more. total carries LinkedIn''s own result count when the page printed one, best effort and null otherwise, so drive the loop from has_more.' data_request: type: object properties: {} description: 'The kind="scrape" DataRequest journal row for this call (terminal completed), embedded as result.data_request; served_from_cache always false. A receipt: its result_ref is null here, the page is result.rows next to it; get_data_request by sid reads the stored payload back. Full DataRequestDomain shape owned by ./data_requests.md, so it is left passthrough here.' required: - rows - paging - data_request meta: type: object properties: trace_id: type: string description: UUID v7; same 128-bit value as the X-Trace-Id header. span_id: type: string pattern: ^[0-9a-f]{16}$ description: 16 hex chars, root span of this request. timestamp: type: string description: ISO 8601 UTC (Y-m-dTH:i:sZ), response time. duration_ms: type: integer minimum: 0 description: Server-side wall clock. team_sid: type: - string - 'null' description: The team this call ran in (the token team, or the team_sid override). Null when unauthenticated; absent from pre-2026-08-20 backends. actor_type: type: - string - 'null' description: user | agent | api_key | system. Null when unauthenticated; absent from pre-2026-08-20 backends. required: - trace_id - span_id - timestamp - duration_ms pacing: type: object properties: linkedin_account_sid: type: string description: The account whose bucket the call spent. limit_type: type: string description: The smart-limit bucket the call spent, e.g. send_messages. next_call_after: type: string description: 'ISO 8601, the same form as retry_after: the moment this same call goes through again without waiting. The next pacing slot while the day has budget left, the daily reset when it has none, the end of a LinkedIn lock while one stands.' remaining_today: type: integer minimum: 0 description: daily_limit minus done_today_count of that bucket, after this call. required: - linkedin_account_sid - limit_type - next_call_after - remaining_today required: - success - operation - action - item - result - meta ScrapeLinkedinSearchProductsRequest: type: object description: Request body of `scrape_linkedin_search_products`. properties: linkedin_account_sid: type: - string - 'null' minLength: 18 maxLength: 18 pattern: ^ln_ac_ description: '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).' idempotency_key: type: - string - 'null' maxLength: 128 description: '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.' url: type: - string - 'null' maxLength: 2048 pattern: ^https:\/\/www\.linkedin\.com\/search\/results\/products\/ description: 'EXCLUSIVE 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/search/results/products/ : 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.' filters: type: - object - 'null' properties: keywords: type: string maxLength: 256 description: 'REQUIRED: this engine has no wire form without a query, so a filter object carrying only a category or only the free-version toggle is refused rather than run wide.' free_version: type: - boolean - 'null' description: ONE-WAY toggle. true narrows to products offering a free version; false is identical to omitting it, because the node writes the facet only when the value is truthy. There is no way to ask for products WITHOUT a free version on this wire. product_category: type: - array - 'null' items: type: string pattern: ^\d+$ minItems: 1 maxItems: 10 description: LinkedIn product-category ids, digit strings. Resolve them with param-id-lookup type=product_category, which is this filter's only source; this search is that lookup type's first consumer. Not interchangeable with product_company. product_company: type: - array - 'null' items: type: string pattern: ^\d+$ minItems: 1 maxItems: 10 description: 'Vendor organization ids, digit strings. NOT a param-id-lookup type of its own: resolve them with type=company, whose ids this filter accepts (verified live: type=company "Salesforce" gives 3185, and that id narrows a crm search from 1600 results to 3, all published by Salesforce). A category id used here matches nothing and returns an ordinary empty page.' required: - keywords additionalProperties: false description: 'EXCLUSIVE with `url`: send filters OR url, never both (422) and never neither (422). LinkedIn product-search filters. keywords is REQUIRED; free_version is a one-way toggle; product_category and product_company are arrays of digit-string ids from two DIFFERENT id spaces, and nothing validates one against the other, so a category id in the company slot returns an ordinary empty page. Any other key is REFUSED rather than ignored.' page: type: integer minimum: 1 maximum: 100 description: '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.' ScrapeLinkedinSimilarCompaniesResponse: type: object properties: success: type: boolean enum: - true operation: type: string enum: - action action: type: string description: kebab-case verb; matches the route segment. item: {} result: type: object properties: rows: type: array items: type: object properties: company_ln_id: type: - string - 'null' nickname: type: - string - 'null' description: 'Vanity slug; regular and similar-companies engines only (Sales Navigator: null, resolve via get-company-public-identifier).' name: type: - string - 'null' industry: type: - string - 'null' employees_size: type: - string - 'null' description: Sales Navigator size bucket ("11-50"); null on the regular engine. followers: type: - integer - 'null' description: Regular and similar-companies engines; the regular card's rounded "4K followers" reads 4000. location: type: - string - 'null' description: 'Regular engine: the card''s location line; many cards print none.' tagline: type: - string - 'null' description: Sales Navigator only. logo_url: type: - string - 'null' website: type: - string - 'null' description: On no search wire; always null. required: - company_ln_id - nickname - name - industry - employees_size - followers - location - tagline - logo_url - website description: The transient result list, in LinkedIn’s order; NOTHING persisted. paging: {} data_request: type: object properties: {} description: 'The kind="scrape" DataRequest journal row for this call (terminal completed), embedded as result.data_request; served_from_cache always false. A receipt: its result_ref is null here, the page is result.rows next to it; get_data_request by sid reads the stored payload back. Full DataRequestDomain shape owned by ./data_requests.md, so it is left passthrough here.' required: - rows - paging - data_request meta: type: object properties: trace_id: type: string description: UUID v7; same 128-bit value as the X-Trace-Id header. span_id: type: string pattern: ^[0-9a-f]{16}$ description: 16 hex chars, root span of this request. timestamp: type: string description: ISO 8601 UTC (Y-m-dTH:i:sZ), response time. duration_ms: type: integer minimum: 0 description: Server-side wall clock. team_sid: type: - string - 'null' description: The team this call ran in (the token team, or the team_sid override). Null when unauthenticated; absent from pre-2026-08-20 backends. actor_type: type: - string - 'null' description: user | agent | api_key | system. Null when unauthenticated; absent from pre-2026-08-20 backends. required: - trace_id - span_id - timestamp - duration_ms pacing: type: object properties: linkedin_account_sid: type: string description: The account whose bucket the call spent. limit_type: type: string description: The smart-limit bucket the call spent, e.g. send_messages. next_call_after: type: string description: 'ISO 8601, the same form as retry_after: the moment this same call goes through again without waiting. The next pacing slot while the day has budget left, the daily reset when it has none, the end of a LinkedIn lock while one stands.' remaining_today: type: integer minimum: 0 description: daily_limit minus done_today_count of that bucket, after this call. required: - linkedin_account_sid - limit_type - next_call_after - remaining_today required: - success - operation - action - item - result - meta ScrapeLinkedinCompanyEmployeesResponse: type: object properties: success: type: boolean enum: - true operation: type: string enum: - action action: type: string description: kebab-case verb; matches the route segment. item: {} result: type: object properties: rows: type: array items: type: object properties: ln_member_id: type: - string - 'null' description: Canonical member id (decoded in-process). ln_id: type: - string - 'null' sn_id: type: - string - 'null' nickname: type: - string - 'null' full_name: type: - string - 'null' headline: type: - string - 'null' description: The card's headline line on the regular and Recruiter engines; null on Sales Navigator people search. position: type: - string - 'null' description: Recruiter search only; the regular card prints no structured position (read the headline). company_name: type: - string - 'null' description: Recruiter search only; the regular card prints no structured company (read the headline). location: type: - string - 'null' description: The card's location line on the regular and Recruiter engines; null on Sales Navigator people search. avatar_url: type: - string - 'null' description: Best effort on the regular engines (a DOM lookup, null when the card image had not rendered). connection_degree: type: - string - 'null' description: 'The degree badge as the card prints it: 1st, 2nd, 3rd or 3rd+ on the regular engines; Recruiter spells 1st, 2nd, 3rd, out_of_network.' required: - ln_member_id - ln_id - sn_id - nickname - full_name - headline - position - company_name - location - avatar_url - connection_degree description: The transient result list, in LinkedIn’s order; NOTHING persisted. paging: type: object properties: page: type: integer page_size: type: integer has_more: type: boolean total: type: - integer - 'null' required: - page - page_size - has_more - total description: 'Page-numbered paging: re-call with page + 1 while has_more.' data_request: type: object properties: {} description: 'The kind="scrape" DataRequest journal row for this call (terminal completed), embedded as result.data_request; served_from_cache always false. A receipt: its result_ref is null here, the page is result.rows next to it; get_data_request by sid reads the stored payload back. Full DataRequestDomain shape owned by ./data_requests.md, so it is left passthrough here.' required: - rows - paging - data_request meta: type: object properties: trace_id: type: string description: UUID v7; same 128-bit value as the X-Trace-Id header. span_id: type: string pattern: ^[0-9a-f]{16}$ description: 16 hex chars, root span of this request. timestamp: type: string description: ISO 8601 UTC (Y-m-dTH:i:sZ), response time. duration_ms: type: integer minimum: 0 description: Server-side wall clock. team_sid: type: - string - 'null' description: The team this call ran in (the token team, or the team_sid override). Null when unauthenticated; absent from pre-2026-08-20 backends. actor_type: type: - string - 'null' description: user | agent | api_key | system. Null when unauthenticated; absent from pre-2026-08-20 backends. required: - trace_id - span_id - timestamp - duration_ms pacing: type: object properties: linkedin_account_sid: type: string description: The account whose bucket the call spent. limit_type: type: string description: The smart-limit bucket the call spent, e.g. send_messages. next_call_after: type: string description: 'ISO 8601, the same form as retry_after: the moment this same call goes through again without waiting. The next pacing slot while the day has budget left, the daily reset when it has none, the end of a LinkedIn lock while one stands.' remaining_today: type: integer minimum: 0 description: daily_limit minus done_today_count of that bucket, after this call. required: - linkedin_account_sid - limit_type - next_call_after - remaining_today required: - success - operation - action - item - result - meta ScrapeLinkedinSearchRecruiterPeopleResponse: type: object properties: success: type: boolean enum: - true operation: type: string enum: - action action: type: string description: kebab-case verb; matches the route segment. item: {} result: type: object properties: rows: type: array items: type: object properties: ln_member_id: type: - string - 'null' description: Canonical member id (decoded in-process). ln_id: type: - string - 'null' sn_id: type: - string - 'null' nickname: type: - string - 'null' full_name: type: - string - 'null' headline: type: - string - 'null' description: The card's headline line on the regular and Recruiter engines; null on Sales Navigator people search. position: type: - string - 'null' description: Recruiter search only; the regular card prints no structured position (read the headline). company_name: type: - string - 'null' description: Recruiter search only; the regular card prints no structured company (read the headline). location: type: - string - 'null' description: The card's location line on the regular and Recruiter engines; null on Sales Navigator people search. avatar_url: type: - string - 'null' description: Best effort on the regular engines (a DOM lookup, null when the card image had not rendered). connection_degree: type: - string - 'null' description: 'The degree badge as the card prints it: 1st, 2nd, 3rd or 3rd+ on the regular engines; Recruiter spells 1st, 2nd, 3rd, out_of_network.' recruiter_id: type: string description: 'The Recruiter (talent) profile id, AEMAA…: what send_linkedin_recruiter_message addresses as recipient_profile_id. Same member as ln_member_id.' industry: type: - string - 'null' can_send_inmail: type: - boolean - 'null' description: LinkedIn says this seat may InMail the member right now. open_to_work: type: - boolean - 'null' description: The member signalled openness to new opportunities. connections_count: type: - integer - 'null' anonymized: type: boolean description: LinkedIn hid the name (out-of-network privacy); the names are what it showed. required: - ln_member_id - ln_id - sn_id - nickname - full_name - headline - position - company_name - location - avatar_url - connection_degree - recruiter_id - industry - can_send_inmail - open_to_work - connections_count - anonymized description: The transient result list, in LinkedIn’s order; NOTHING persisted. paging: type: object properties: page: type: integer page_size: type: integer has_more: type: boolean total: type: - integer - 'null' required: - page - page_size - has_more - total description: Page-numbered paging with LinkedIn Recruiter's own total; has_more is false at the 40-page ceiling even when total says more. data_request: type: object properties: {} description: 'The kind="scrape" DataRequest journal row for this call (terminal completed), embedded as result.data_request; served_from_cache always false. A receipt: its result_ref is null here, the page is result.rows next to it; get_data_request by sid reads the stored payload back. Full DataRequestDomain shape owned by ./data_requests.md, so it is left passthrough here.' search_history_id: type: - string - 'null' description: The searchHistoryId LinkedIn Recruiter gave this search (the seat's "Recent searches" entry); null only when the wire returned none. search_url: type: - string - 'null' description: 'https://www.linkedin.com/talent/search?searchHistoryId=N&start=0: pass it as `url` (with page) to page on, or to re-run this exact search later, without re-sending the filters.' required: - rows - paging - data_request - search_history_id - search_url meta: type: object properties: trace_id: type: string description: UUID v7; same 128-bit value as the X-Trace-Id header. span_id: type: string pattern: ^[0-9a-f]{16}$ description: 16 hex chars, root span of this request. timestamp: type: string description: ISO 8601 UTC (Y-m-dTH:i:sZ), response time. duration_ms: type: integer minimum: 0 description: Server-side wall clock. team_sid: type: - string - 'null' description: The team this call ran in (the token team, or the team_sid override). Null when unauthenticated; absent from pre-2026-08-20 backends. actor_type: type: - string - 'null' description: user | agent | api_key | system. Null when unauthenticated; absent from pre-2026-08-20 backends. required: - trace_id - span_id - timestamp - duration_ms pacing: type: object properties: linkedin_account_sid: type: string description: The account whose bucket the call spent. limit_type: type: string description: The smart-limit bucket the call spent, e.g. send_messages. next_call_after: type: string description: 'ISO 8601, the same form as retry_after: the moment this same call goes through again without waiting. The next pacing slot while the day has budget left, the daily reset when it has none, the end of a LinkedIn lock while one stands.' remaining_today: type: integer minimum: 0 description: daily_limit minus done_today_count of that bucket, after this call. required: - linkedin_account_sid - limit_type - next_call_after - remaining_today required: - success - operation - action - item - result - meta ScrapeLinkedinGetPostResharersResponse: type: object properties: success: type: boolean enum: - true operation: type: string enum: - action action: type: string description: kebab-case verb; matches the route segment. item: {} result: type: object properties: rows: type: array items: type: object properties: resharer_ln_member_id: type: string resharer_ln_id: type: - string - 'null' resharer_nickname: type: - string - 'null' resharer_full_name: type: - string - 'null' resharer_headline: type: - string - 'null' resharer_picture_url: type: - string - 'null' resharer_commentary: type: - string - 'null' description: Text added when resharing (empty for a plain reshare). reshare_urn: type: - string - 'null' description: The reshare’s own activity URN. Feed it back into get-post-comments/-reactors to scrape the reshare itself. is_own: type: boolean required: - resharer_ln_member_id - resharer_ln_id - resharer_nickname - resharer_full_name - resharer_headline - resharer_picture_url - resharer_commentary - reshare_urn - is_own description: The transient result list, in LinkedIn’s order; NOTHING persisted. paging: type: object properties: page_size: type: integer next_cursor: type: - string - 'null' has_more: type: boolean total: type: - integer - 'null' required: - page_size - next_cursor - has_more - total description: 'Cursor paging: feed next_cursor back verbatim; null = terminal page.' data_request: type: object properties: {} description: 'The kind="scrape" DataRequest journal row for this call (terminal completed), embedded as result.data_request; served_from_cache always false. A receipt: its result_ref is null here, the page is result.rows next to it; get_data_request by sid reads the stored payload back. Full DataRequestDomain shape owned by ./data_requests.md, so it is left passthrough here.' post: type: object properties: post_ln_id: type: string required: - post_ln_id description: The resolved post target the getter ran against. required: - rows - paging - data_request - post meta: type: object properties: trace_id: type: string description: UUID v7; same 128-bit value as the X-Trace-Id header. span_id: type: string pattern: ^[0-9a-f]{16}$ description: 16 hex chars, root span of this request. timestamp: type: string description: ISO 8601 UTC (Y-m-dTH:i:sZ), response time. duration_ms: type: integer minimum: 0 description: Server-side wall clock. team_sid: type: - string - 'null' description: The team this call ran in (the token team, or the team_sid override). Null when unauthenticated; absent from pre-2026-08-20 backends. actor_type: type: - string - 'null' description: user | agent | api_key | system. Null when unauthenticated; absent from pre-2026-08-20 backends. required: - trace_id - span_id - timestamp - duration_ms pacing: type: object properties: linkedin_account_sid: type: string description: The account whose bucket the call spent. limit_type: type: string description: The smart-limit bucket the call spent, e.g. send_messages. next_call_after: type: string description: 'ISO 8601, the same form as retry_after: the moment this same call goes through again without waiting. The next pacing slot while the day has budget left, the daily reset when it has none, the end of a LinkedIn lock while one stands.' remaining_today: type: integer minimum: 0 description: daily_limit minus done_today_count of that bucket, after this call. required: - linkedin_account_sid - limit_type - next_call_after - remaining_today required: - success - operation - action - item - result - meta ScrapeLinkedinRecruiterParamIdLookupRequest: type: object description: Request body of `scrape_linkedin_recruiter_param_id_lookup`. properties: linkedin_account_sid: type: - string - 'null' minLength: 18 maxLength: 18 pattern: ^ln_ac_ description: '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).' idempotency_key: type: - string - 'null' maxLength: 128 description: '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.' type: type: string enum: - occupation - skill - company - geo - zip - industry - school - fieldOfStudy - degree - language - group description: 'The Recruiter search-filter typeahead kind (node talentTypeaheads `q`, passed through verbatim). Every kind is a text facet, so `query` is required. Where each id lands in the search_recruiter_people filters: occupation → job_titles / occupations, skill → skills, company → companies / current_companies, geo → locations, zip → postal_codes, industry → industries, school → schools (the row id is the school''s organization urn, what the facet matches on), fieldOfStudy → fields_of_study, degree → degrees, language → spoken_languages (the id is the lowercase language name), group → no search member yet. Pass the row''s display_name as the chip text on job_titles / occupations / skills / companies / postal_codes. The closed enums of the search (seniority, function, company size, …) have no typeahead: their code sets are inline in the filters.' query: type: string minLength: 1 maxLength: 100 description: The typed term - required, every kind is a text facet. required: - type - query ScrapeLinkedinCompanyDecisionMakersRequest: type: object description: Request body of `scrape_linkedin_company_decision_makers`. properties: linkedin_account_sid: type: - string - 'null' minLength: 18 maxLength: 18 pattern: ^ln_ac_ description: '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).' idempotency_key: type: - string - 'null' maxLength: 128 description: '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.' company: type: object properties: company_url: type: - string - 'null' maxLength: 512 description: https://www.linkedin.com/company/{slug}/ company_ln_id: type: - string - 'null' maxLength: 64 description: Numeric LinkedIn company id. description: 'Target company: exactly one of company_url / company_ln_id.' limit: type: integer minimum: 1 maximum: 50 description: Default 25. required: - company ScrapeLinkedinSimilarCompaniesRequest: type: object description: Request body of `scrape_linkedin_similar_companies`. properties: linkedin_account_sid: type: - string - 'null' minLength: 18 maxLength: 18 pattern: ^ln_ac_ description: '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).' idempotency_key: type: - string - 'null' maxLength: 128 description: '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.' company: type: object properties: company_url: type: - string - 'null' maxLength: 512 description: https://www.linkedin.com/company/{slug}/ company_ln_id: type: - string - 'null' maxLength: 64 description: Numeric LinkedIn company id. description: 'Target company: exactly one of company_url / company_ln_id.' limit: type: integer minimum: 1 maximum: 50 description: Default 10. required: - company ScrapeLinkedinBuildSearchUrlRequest: type: object description: Request body of `scrape_linkedin_build_search_url`. properties: linkedin_account_sid: type: - string - 'null' minLength: 18 maxLength: 18 pattern: ^ln_ac_ description: '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).' idempotency_key: type: - string - 'null' maxLength: 128 description: '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.' people: type: - object - 'null' properties: keywords: type: - string - 'null' maxLength: 256 description: Free-text query. first_name: type: - string - 'null' maxLength: 100 last_name: type: - string - 'null' maxLength: 100 title: type: - string - 'null' maxLength: 256 description: Current-title keywords. company: type: - string - 'null' maxLength: 256 description: Current-company keywords (free text; prefer current_companies). school: type: - string - 'null' maxLength: 256 description: 'School free-text: this engine has no school-id facet, so lookup(type: "school") ids fit nowhere here.' network: type: - array - 'null' items: type: string enum: - 1st - 2nd - 3rd_plus maxItems: 3 description: Relationship-degree facet; the node maps each degree onto the LinkedIn wire code (F/S/O) itself. locations: type: - array - 'null' items: type: object properties: id: type: - string - 'null' maxLength: 64 pattern: ^\d+$ description: A numeric id, as the search takes it; rides as it is. text: type: - string - 'null' maxLength: 100 description: 'Human text to resolve ("Berlin", "Acme"): typed into the typeahead, which picks the option labelled exactly so, else LinkedIn''s first.' description: 'One value: an id, or a text to resolve (at least one of the two).' maxItems: 10 description: Geography (location typeahead). industries: type: - array - 'null' items: type: object properties: id: type: - string - 'null' maxLength: 64 pattern: ^\d+$ description: A numeric id, as the search takes it; rides as it is. text: type: - string - 'null' maxLength: 100 description: 'Human text to resolve ("Berlin", "Acme"): typed into the typeahead, which picks the option labelled exactly so, else LinkedIn''s first.' description: 'One value: an id, or a text to resolve (at least one of the two).' maxItems: 10 description: Industries (industry typeahead). current_companies: type: - array - 'null' items: type: object properties: id: type: - string - 'null' maxLength: 64 pattern: ^\d+$ description: A numeric id, as the search takes it; rides as it is. text: type: - string - 'null' maxLength: 100 description: 'Human text to resolve ("Berlin", "Acme"): typed into the typeahead, which picks the option labelled exactly so, else LinkedIn''s first.' description: 'One value: an id, or a text to resolve (at least one of the two).' maxItems: 10 description: Current employer (company typeahead). past_companies: type: - array - 'null' items: type: object properties: id: type: - string - 'null' maxLength: 64 pattern: ^\d+$ description: A numeric id, as the search takes it; rides as it is. text: type: - string - 'null' maxLength: 100 description: 'Human text to resolve ("Berlin", "Acme"): typed into the typeahead, which picks the option labelled exactly so, else LinkedIn''s first.' description: 'One value: an id, or a text to resolve (at least one of the two).' maxItems: 10 description: Past employer (company typeahead). service_categories: type: - array - 'null' items: type: object properties: id: type: - string - 'null' maxLength: 64 pattern: ^\d+$ description: A numeric id, as the search takes it; rides as it is. text: type: - string - 'null' maxLength: 100 description: 'Human text to resolve ("Berlin", "Acme"): typed into the typeahead, which picks the option labelled exactly so, else LinkedIn''s first.' description: 'One value: an id, or a text to resolve (at least one of the two).' maxItems: 10 description: Service categories (service_category typeahead). connections_of: type: - array - 'null' items: type: object properties: id: type: - string - 'null' maxLength: 128 pattern: ^[\w-]+$ description: A profile id (ACoA…), as the search takes it; rides as it is. text: type: - string - 'null' maxLength: 100 description: 'Human text to resolve ("Berlin", "Acme"): typed into the typeahead, which picks the option labelled exactly so, else LinkedIn''s first.' description: 'One value: an id, or a text to resolve (at least one of the two).' maxItems: 10 description: 'People connected to these members: a name resolves through the connections typeahead.' followers_of: type: - array - 'null' items: type: object properties: id: type: - string - 'null' maxLength: 128 pattern: ^[\w-]+$ description: A profile id (ACoA…), as the search takes it; rides as it is. text: type: - string - 'null' maxLength: 100 description: 'Human text to resolve ("Berlin", "Acme"): typed into the typeahead, which picks the option labelled exactly so, else LinkedIn''s first.' description: 'One value: an id, or a text to resolve (at least one of the two).' maxItems: 10 description: 'People following these members: a name resolves through the people typeahead.' profile_languages: type: - array - 'null' items: type: string maxItems: 10 description: ISO 639-1 language codes. open_to_volunteer: type: - boolean - 'null' description: 'The scrape_linkedin_search_people filters. A text the typeahead offers nothing for is a 422 naming it: these members take ids only.' sales_nav_people: type: - object - 'null' properties: keywords: type: - string - 'null' maxLength: 256 first_name: type: - string - 'null' maxLength: 100 description: Text-only wire facet (no id space). last_name: type: - string - 'null' maxLength: 100 description: Text-only wire facet (no id space). current_titles: type: - array - 'null' items: type: object properties: id: type: - string - 'null' maxLength: 128 description: Opaque SN facet id, VERBATIM from scrape_linkedin_sales_nav_param_id_lookup. text: type: - string - 'null' maxLength: 256 description: Text to resolve into an id plus LinkedIn's label; one the typeahead offers nothing for stays a free-text chip, which SN matches on this facet. exclude: type: - boolean - 'null' description: true → EXCLUDED (negative filter); omitted/false → INCLUDED. free_text: type: - boolean - 'null' description: true → send the text as a free-text chip, unresolved. description: 'One chip: an id, or a text (at least one of the two).' maxItems: 10 description: Current job titles (TITLE). past_titles: type: - array - 'null' items: type: object properties: id: type: - string - 'null' maxLength: 128 description: Opaque SN facet id, VERBATIM from scrape_linkedin_sales_nav_param_id_lookup. text: type: - string - 'null' maxLength: 256 description: Text to resolve into an id plus LinkedIn's label; one the typeahead offers nothing for stays a free-text chip, which SN matches on this facet. exclude: type: - boolean - 'null' description: true → EXCLUDED (negative filter); omitted/false → INCLUDED. free_text: type: - boolean - 'null' description: true → send the text as a free-text chip, unresolved. description: 'One chip: an id, or a text (at least one of the two).' maxItems: 10 description: Past job titles (TITLE). locations: type: - array - 'null' items: type: object properties: id: type: - string - 'null' maxLength: 128 description: Opaque SN facet id, VERBATIM from scrape_linkedin_sales_nav_param_id_lookup. text: type: - string - 'null' maxLength: 256 description: Text to resolve into an id plus LinkedIn's label; one the typeahead offers nothing for is a 422 naming the closest options. exclude: type: - boolean - 'null' description: true → EXCLUDED (negative filter); omitted/false → INCLUDED. description: 'One chip: an id, or a text (at least one of the two).' maxItems: 10 description: Person geography (BING_GEO). company_headquarters: type: - array - 'null' items: type: object properties: id: type: - string - 'null' maxLength: 128 description: Opaque SN facet id, VERBATIM from scrape_linkedin_sales_nav_param_id_lookup. text: type: - string - 'null' maxLength: 256 description: Text to resolve into an id plus LinkedIn's label; one the typeahead offers nothing for is a 422 naming the closest options. exclude: type: - boolean - 'null' description: true → EXCLUDED (negative filter); omitted/false → INCLUDED. description: 'One chip: an id, or a text (at least one of the two).' maxItems: 10 description: Current company HQ region (BING_GEO). industries: type: - array - 'null' items: type: object properties: id: type: - string - 'null' maxLength: 128 description: Opaque SN facet id, VERBATIM from scrape_linkedin_sales_nav_param_id_lookup. text: type: - string - 'null' maxLength: 256 description: Text to resolve into an id plus LinkedIn's label; one the typeahead offers nothing for is a 422 naming the closest options. exclude: type: - boolean - 'null' description: true → EXCLUDED (negative filter); omitted/false → INCLUDED. description: 'One chip: an id, or a text (at least one of the two).' maxItems: 10 description: Industries (INDUSTRY). current_companies: type: - array - 'null' items: type: object properties: id: type: - string - 'null' maxLength: 128 description: Opaque SN facet id, VERBATIM from scrape_linkedin_sales_nav_param_id_lookup. text: type: - string - 'null' maxLength: 256 description: Text to resolve into an id plus LinkedIn's label; one the typeahead offers nothing for stays a free-text chip, which SN matches on this facet. exclude: type: - boolean - 'null' description: true → EXCLUDED (negative filter); omitted/false → INCLUDED. free_text: type: - boolean - 'null' description: true → send the text as a free-text chip, unresolved. description: 'One chip: an id, or a text (at least one of the two).' maxItems: 10 description: Current employer (COMPANY_WITH_LIST). past_companies: type: - array - 'null' items: type: object properties: id: type: - string - 'null' maxLength: 128 description: Opaque SN facet id, VERBATIM from scrape_linkedin_sales_nav_param_id_lookup. text: type: - string - 'null' maxLength: 256 description: Text to resolve into an id plus LinkedIn's label; one the typeahead offers nothing for stays a free-text chip, which SN matches on this facet. exclude: type: - boolean - 'null' description: true → EXCLUDED (negative filter); omitted/false → INCLUDED. free_text: type: - boolean - 'null' description: true → send the text as a free-text chip, unresolved. description: 'One chip: an id, or a text (at least one of the two).' maxItems: 10 description: Past employer (COMPANY_WITH_LIST). groups: type: - array - 'null' items: type: object properties: id: type: - string - 'null' maxLength: 128 description: Opaque SN facet id, VERBATIM from scrape_linkedin_sales_nav_param_id_lookup. text: type: - string - 'null' maxLength: 256 description: Text to resolve into an id plus LinkedIn's label; one the typeahead offers nothing for is a 422 naming the closest options. exclude: type: - boolean - 'null' description: true → EXCLUDED (negative filter); omitted/false → INCLUDED. description: 'One chip: an id, or a text (at least one of the two).' maxItems: 10 description: Group membership (GROUP). schools: type: - array - 'null' items: type: object properties: id: type: - string - 'null' maxLength: 128 description: Opaque SN facet id, VERBATIM from scrape_linkedin_sales_nav_param_id_lookup. text: type: - string - 'null' maxLength: 256 description: Text to resolve into an id plus LinkedIn's label; one the typeahead offers nothing for is a 422 naming the closest options. exclude: type: - boolean - 'null' description: true → EXCLUDED (negative filter); omitted/false → INCLUDED. description: 'One chip: an id, or a text (at least one of the two).' maxItems: 10 description: Schools (SCHOOL). seniority_levels: type: - array - 'null' items: type: string enum: - '100' - '110' - '120' - '130' - '200' - '210' - '220' - '300' - '310' - '320' maxItems: 10 description: 'SENIORITY_V2: ''100'' In Training, ''110'' Entry Level, ''120'' Senior, ''130'' Strategic, ''200'' Entry Level Manager, ''210'' Experienced Manager, ''220'' Director, ''300'' Vice President, ''310'' CXO, ''320'' Owner/Partner.' functions: type: - array - 'null' items: type: string enum: - '1' - '2' - '3' - '4' - '5' - '6' - '7' - '8' - '9' - '10' - '11' - '12' - '13' - '14' - '15' - '16' - '17' - '18' - '19' - '20' - '21' - '22' - '23' - '24' - '25' - '26' maxItems: 10 description: 'Job function: ''1'' Accounting, ''2'' Administrative, ''3'' Arts and Design, ''4'' Business Development, ''5'' Community and Social Services, ''6'' Consulting, ''7'' Education, ''8'' Engineering, ''9'' Entrepreneurship, ''10'' Finance, ''11'' Healthcare Services, ''12'' Human Resources, ''13'' Information Technology, ''14'' Legal, ''15'' Marketing, ''16'' Media and Communication, ''17'' Military and Protective Services, ''18'' Operations, ''19'' Product Management, ''20'' Program and Project Management, ''21'' Purchasing, ''22'' Quality Assurance, ''23'' Real Estate, ''24'' Research, ''25'' Sales, ''26'' Customer Success and Support.' years_in_current_company: type: - array - 'null' items: type: string enum: - '1' - '2' - '3' - '4' - '5' maxItems: 5 description: 'Tenure at current company: ''1'' <1 year, ''2'' 1-2, ''3'' 3-5, ''4'' 6-10, ''5'' 10+ years.' years_in_current_position: type: - array - 'null' items: type: string enum: - '1' - '2' - '3' - '4' - '5' maxItems: 5 description: 'Tenure in current position: ''1'' <1 year, ''2'' 1-2, ''3'' 3-5, ''4'' 6-10, ''5'' 10+ years.' years_of_experience: type: - array - 'null' items: type: string enum: - '1' - '2' - '3' - '4' - '5' maxItems: 5 description: 'Total career length: ''1'' <1 year, ''2'' 1-2, ''3'' 3-5, ''4'' 6-10, ''5'' 10+ years.' company_headcounts: type: - array - 'null' items: type: string enum: - A - B - C - D - E - F - G - H - I maxItems: 10 description: 'Company size: ''A'' Self-employed, ''B'' 1-10, ''C'' 11-50, ''D'' 51-200, ''E'' 201-500, ''F'' 501-1000, ''G'' 1001-5000, ''H'' 5001-10000, ''I'' 10001+.' company_types: type: - array - 'null' items: type: string enum: - C - P - N - D - S - E - O - G maxItems: 10 description: 'Company type: ''C'' Public, ''P'' Privately Held, ''N'' Non-profit, ''D'' Educational, ''S'' Partnership, ''E'' Self-Employed, ''O'' Self-Owned, ''G'' Government.' profile_languages: type: - array - 'null' items: type: string enum: - ar - en - es - pt - zh - fr - it - ru - de - nl - tr - tl - pl - ko - ja - ms - 'no' - da - ro - sv - in - cs maxItems: 10 description: Profile language, ISO 639-1. network: type: - array - 'null' items: type: string enum: - F - S - A - O maxItems: 4 description: 'Relationship degree: ''F'' 1st, ''S'' 2nd, ''A'' group members, ''O'' 3rd+.' connections_of: type: - array - 'null' items: type: object properties: id: type: - string - 'null' maxLength: 64 description: A member token (ACwA…), as the search takes it. text: type: - string - 'null' maxLength: 100 description: 'Human text to resolve ("Berlin", "Acme"): typed into the typeahead, which picks the option labelled exactly so, else LinkedIn''s first.' description: 'One value: an id, or a text to resolve (at least one of the two).' maxItems: 10 description: 'People connected to these members: a name resolves through CONNECTION_OF; a miss is a 422.' description: The scrape_linkedin_search_sales_nav_people filters; resolution through the Sales Navigator typeahead, which needs an SN seat. companies: type: - object - 'null' properties: keywords: type: - string - 'null' maxLength: 256 geo_ids: type: - array - 'null' items: type: object properties: id: type: - string - 'null' maxLength: 64 pattern: ^\d+$ description: A numeric id, as the search takes it; rides as it is. text: type: - string - 'null' maxLength: 100 description: 'Human text to resolve ("Berlin", "Acme"): typed into the typeahead, which picks the option labelled exactly so, else LinkedIn''s first.' description: 'One value: an id, or a text to resolve (at least one of the two).' maxItems: 10 description: HQ geography (location typeahead). industry_ids: type: - array - 'null' items: type: object properties: id: type: - string - 'null' maxLength: 64 pattern: ^\d+$ description: A numeric id, as the search takes it; rides as it is. text: type: - string - 'null' maxLength: 100 description: 'Human text to resolve ("Berlin", "Acme"): typed into the typeahead, which picks the option labelled exactly so, else LinkedIn''s first.' description: 'One value: an id, or a text to resolve (at least one of the two).' maxItems: 10 description: Industries (industry typeahead). company_sizes: type: - array - 'null' items: type: string enum: - 1_to_10 - 11_to_50 - 51_to_200 - 201_to_500 - 501_to_1000 - 1001_to_5000 - 5001_to_10000 - 10001_plus maxItems: 8 description: Headcount ranges; the node maps each range onto the LinkedIn size-bucket id itself. network: type: - array - 'null' items: type: string enum: - 1st maxItems: 1 description: Companies where you have 1st-degree connections; the company search takes the first degree only. has_jobs: type: - boolean - 'null' description: true → only companies hiring on LinkedIn right now; false sends nothing. description: The scrape_linkedin_search_companies filters. A text the typeahead offers nothing for is a 422 naming it. sales_nav_companies: type: - object - 'null' properties: keywords: type: - string - 'null' maxLength: 256 company_headquarters: type: - array - 'null' items: type: object properties: id: type: - string - 'null' maxLength: 128 description: Opaque SN facet id, VERBATIM from scrape_linkedin_sales_nav_param_id_lookup. text: type: - string - 'null' maxLength: 256 description: Text to resolve into an id plus LinkedIn's label; one the typeahead offers nothing for is a 422 naming the closest options. exclude: type: - boolean - 'null' description: true → EXCLUDED (negative filter); omitted/false → INCLUDED. description: 'One chip: an id, or a text (at least one of the two).' maxItems: 10 description: HQ region (BING_GEO). industries: type: - array - 'null' items: type: object properties: id: type: - string - 'null' maxLength: 128 description: Opaque SN facet id, VERBATIM from scrape_linkedin_sales_nav_param_id_lookup. text: type: - string - 'null' maxLength: 256 description: Text to resolve into an id plus LinkedIn's label; one the typeahead offers nothing for is a 422 naming the closest options. exclude: type: - boolean - 'null' description: true → EXCLUDED (negative filter); omitted/false → INCLUDED. description: 'One chip: an id, or a text (at least one of the two).' maxItems: 10 description: Industries (INDUSTRY). account_lists: type: - array - 'null' items: type: object properties: id: type: - string - 'null' maxLength: 128 description: Opaque SN facet id, VERBATIM from scrape_linkedin_sales_nav_param_id_lookup. text: type: - string - 'null' maxLength: 256 description: 'A list NAME: matched against the seat''s own lists, exactly or as the one name containing it; no match is a 422.' exclude: type: - boolean - 'null' description: true → EXCLUDED (negative filter); omitted/false → INCLUDED. description: 'One facet value: at least one of id / text.' maxItems: 10 description: Your SN account lists, by id or by name (ACCOUNT_LIST). company_headcounts: type: - array - 'null' items: type: string enum: - B - C - D - E - F - G - H - I maxItems: 10 description: 'Company size: ''B'' 1-10, ''C'' 11-50, ''D'' 51-200, ''E'' 201-500, ''F'' 501-1000, ''G'' 1001-5000, ''H'' 5001-10000, ''I'' 10001+ (account search has no ''A'').' num_of_followers: type: - array - 'null' items: type: string enum: - NFR1 - NFR2 - NFR3 - NFR4 - NFR5 maxItems: 5 description: 'LinkedIn page followers: NFR1 1-50, NFR2 51-100, NFR3 101-1000, NFR4 1001-5000, NFR5 5001+.' fortune: type: - array - 'null' items: type: string enum: - '1' - '2' - '3' - '4' maxItems: 4 description: 'Fortune 500 tier: ''1'' Fortune 50, ''2'' 51-100, ''3'' 101-250, ''4'' 251-500.' account_activities: type: - array - 'null' items: type: string enum: - SLC - RFE maxItems: 2 description: 'Buying signals: ''SLC'' senior-leadership changes in the last 3 months, ''RFE'' funding event in the past 12 months.' annual_revenue: type: - object - 'null' properties: min: type: integer minimum: 0 maximum: 100000000 max: type: integer minimum: 0 maximum: 100000000 currency: type: - string - 'null' pattern: ^[A-Z]{3}$ description: ISO-4217 code; default USD. required: - min - max description: 'Annual revenue range in MILLIONS (e.g. {min: 10, max: 100} = $10M-$100M).' company_headcount_growth: type: - object - 'null' properties: min: type: integer minimum: -100 maximum: 1000 max: type: integer minimum: -100 maximum: 1000 required: - min - max description: Company-wide headcount growth range, percent (negative = shrinking). department_headcount: type: - object - 'null' properties: department: type: string enum: - '1' - '2' - '3' - '4' - '5' - '6' - '7' - '8' - '9' - '10' - '11' - '12' - '13' - '14' - '15' - '16' - '17' - '18' - '19' - '20' - '21' - '22' - '23' - '24' - '25' - '26' description: 'Numeric SN department id - the same taxonomy as the people-search functions facet (see its legend, or lookup(type: "FUNCTION")).' min: type: integer minimum: 0 maximum: 1000000 max: type: integer minimum: 0 maximum: 1000000 required: - department - min - max description: 'Headcount of ONE department (e.g. department "25" Sales, {min: 20, max: 99999}).' department_headcount_growth: type: - object - 'null' properties: department: type: string enum: - '1' - '2' - '3' - '4' - '5' - '6' - '7' - '8' - '9' - '10' - '11' - '12' - '13' - '14' - '15' - '16' - '17' - '18' - '19' - '20' - '21' - '22' - '23' - '24' - '25' - '26' description: 'Numeric SN department id - the same taxonomy as the people-search functions facet (see its legend, or lookup(type: "FUNCTION")).' min: type: integer minimum: -100 maximum: 1000 max: type: integer minimum: -100 maximum: 1000 required: - department - min - max description: Growth of ONE department, percent - e.g. engineering team growing 10%+. hiring_on_linkedin: type: - boolean - 'null' description: true → only accounts currently hiring on LinkedIn. first_degree_connection: type: - boolean - 'null' description: true → only accounts where you have a 1st-degree connection. saved_accounts_only: type: - boolean - 'null' description: true → only your saved SN accounts. description: The scrape_linkedin_search_sales_nav_companies filters; resolution through the Sales Navigator typeahead, which needs an SN seat. ScrapeLinkedinRecruiterParamIdLookupResponse: type: object properties: success: type: boolean enum: - true operation: type: string enum: - action action: type: string description: kebab-case verb; matches the route segment. item: {} result: type: object properties: rows: type: array items: type: object properties: type: type: string id: type: string description: Opaque SN facet id. Feed it VERBATIM into the SN search filters (company "urn:li:organization:1441", geo "100506914", …). display_name: type: - string - 'null' headline: type: - string - 'null' entity_urn: type: - string - 'null' image_url: type: - string - 'null' required: - type - id - display_name - headline - entity_urn - image_url description: 'Same shape as the Sales Navigator lookup rows: id is what the Recruiter filter takes; entity_urn the typeahead entity urn; headline the ISO country code for geo / zip, the organization urn for school; image_url the company logo.' description: The transient result list, in LinkedIn’s order; NOTHING persisted. paging: {} data_request: type: object properties: {} description: 'The kind="scrape" DataRequest journal row for this call (terminal completed), embedded as result.data_request; served_from_cache always false. A receipt: its result_ref is null here, the page is result.rows next to it; get_data_request by sid reads the stored payload back. Full DataRequestDomain shape owned by ./data_requests.md, so it is left passthrough here.' required: - rows - paging - data_request meta: type: object properties: trace_id: type: string description: UUID v7; same 128-bit value as the X-Trace-Id header. span_id: type: string pattern: ^[0-9a-f]{16}$ description: 16 hex chars, root span of this request. timestamp: type: string description: ISO 8601 UTC (Y-m-dTH:i:sZ), response time. duration_ms: type: integer minimum: 0 description: Server-side wall clock. team_sid: type: - string - 'null' description: The team this call ran in (the token team, or the team_sid override). Null when unauthenticated; absent from pre-2026-08-20 backends. actor_type: type: - string - 'null' description: user | agent | api_key | system. Null when unauthenticated; absent from pre-2026-08-20 backends. required: - trace_id - span_id - timestamp - duration_ms pacing: type: object properties: linkedin_account_sid: type: string description: The account whose bucket the call spent. limit_type: type: string description: The smart-limit bucket the call spent, e.g. send_messages. next_call_after: type: string description: 'ISO 8601, the same form as retry_after: the moment this same call goes through again without waiting. The next pacing slot while the day has budget left, the daily reset when it has none, the end of a LinkedIn lock while one stands.' remaining_today: type: integer minimum: 0 description: daily_limit minus done_today_count of that bucket, after this call. required: - linkedin_account_sid - limit_type - next_call_after - remaining_today required: - success - operation - action - item - result - meta ScrapeLinkedinParamIdLookupRequest: type: object description: Request body of `scrape_linkedin_param_id_lookup`. properties: linkedin_account_sid: type: - string - 'null' minLength: 18 maxLength: 18 pattern: ^ln_ac_ description: '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).' idempotency_key: type: - string - 'null' maxLength: 128 description: '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.' type: type: string enum: - people - connections - location - company - school - industry - service_category - product_category - job_location description: 'Which dropdown to type into. Where each id lands in the search_people filters: people → followers_of, connections → connections_of, location → locations, company → current_companies, industry → industries, service_category → service_categories. search_service_providers takes the same location and service_category ids; search_companies takes location as geo_ids and industry as industry_ids (matched on the people search, not yet verified there). job_location is the JOBS search box and its ids feed the search_jobs geo_id member ONLY: a different family from the location ids above, so do not cross the two. product_category feeds the search_products product_category member, and ONLY that one: its sibling product_company takes ordinary organization ids, which this endpoint does not resolve under that name (use type=company). school is the one type with no filter member on this surface.' query: type: string minLength: 1 maxLength: 100 description: 'The human term to resolve. Always required: a typeahead has nothing to offer without typed text, and every one of the nine types is a text box.' required: - type - query ScrapeLinkedinSearchSalesNavCompaniesRequest: type: object description: Request body of `scrape_linkedin_search_sales_nav_companies`. properties: linkedin_account_sid: type: - string - 'null' minLength: 18 maxLength: 18 pattern: ^ln_ac_ description: '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).' idempotency_key: type: - string - 'null' maxLength: 128 description: '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.' url: type: - string - 'null' maxLength: 2048 pattern: ^https:\/\/www\.linkedin\.com\/sales\/search\/company description: 'EXCLUSIVE 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/sales/search/company : 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.' filters: type: - object - 'null' properties: keywords: type: - string - 'null' maxLength: 256 company_headquarters: type: - array - 'null' items: type: object properties: id: type: string maxLength: 128 description: 'Opaque SN facet id, VERBATIM from scrape_linkedin_sales_nav_param_id_lookup. REQUIRED: LinkedIn ignores a value without its id on this facet.' text: type: - string - 'null' maxLength: 256 description: The id's label, optional. exclude: type: - boolean - 'null' description: true → EXCLUDED (negative filter); omitted/false → INCLUDED. required: - id description: One facet value, by id. maxItems: 10 description: 'HQ region, by id via lookup(type: "BING_GEO"); exclude supported (e.g. exclude APAC).' industries: type: - array - 'null' items: type: object properties: id: type: string maxLength: 128 description: 'Opaque SN facet id, VERBATIM from scrape_linkedin_sales_nav_param_id_lookup. REQUIRED: LinkedIn ignores a value without its id on this facet.' text: type: - string - 'null' maxLength: 256 description: The id's label, optional. exclude: type: - boolean - 'null' description: true → EXCLUDED (negative filter); omitted/false → INCLUDED. required: - id description: One facet value, by id. maxItems: 10 description: 'Industries, by id via lookup(type: "INDUSTRY").' account_lists: type: - array - 'null' items: type: object properties: id: type: string maxLength: 128 description: 'Opaque SN facet id, VERBATIM from scrape_linkedin_sales_nav_param_id_lookup. REQUIRED: LinkedIn ignores a value without its id on this facet.' text: type: - string - 'null' maxLength: 256 description: The id's label, optional. exclude: type: - boolean - 'null' description: true → EXCLUDED (negative filter); omitted/false → INCLUDED. required: - id description: One facet value, by id. maxItems: 10 description: 'Your SN account lists, by id via lookup(type: "ACCOUNT_LIST") - numeric list ids or the "ALL" sentinel; exclude: true skips a list (e.g. current book of business).' company_headcounts: type: - array - 'null' items: type: string enum: - B - C - D - E - F - G - H - I maxItems: 10 description: 'Company size: ''B'' 1-10, ''C'' 11-50, ''D'' 51-200, ''E'' 201-500, ''F'' 501-1000, ''G'' 1001-5000, ''H'' 5001-10000, ''I'' 10001+ (account search has no ''A'').' num_of_followers: type: - array - 'null' items: type: string enum: - NFR1 - NFR2 - NFR3 - NFR4 - NFR5 maxItems: 5 description: 'LinkedIn page followers: NFR1 1-50, NFR2 51-100, NFR3 101-1000, NFR4 1001-5000, NFR5 5001+.' fortune: type: - array - 'null' items: type: string enum: - '1' - '2' - '3' - '4' maxItems: 4 description: 'Fortune 500 tier: ''1'' Fortune 50, ''2'' 51-100, ''3'' 101-250, ''4'' 251-500.' account_activities: type: - array - 'null' items: type: string enum: - SLC - RFE maxItems: 2 description: 'Buying signals: ''SLC'' senior-leadership changes in the last 3 months, ''RFE'' funding event in the past 12 months.' annual_revenue: type: - object - 'null' properties: min: type: integer minimum: 0 maximum: 100000000 max: type: integer minimum: 0 maximum: 100000000 currency: type: - string - 'null' pattern: ^[A-Z]{3}$ description: ISO-4217 code; default USD. required: - min - max description: 'Annual revenue range in MILLIONS (e.g. {min: 10, max: 100} = $10M-$100M).' company_headcount_growth: type: - object - 'null' properties: min: type: integer minimum: -100 maximum: 1000 max: type: integer minimum: -100 maximum: 1000 required: - min - max description: Company-wide headcount growth range, percent (negative = shrinking). department_headcount: type: - object - 'null' properties: department: type: string enum: - '1' - '2' - '3' - '4' - '5' - '6' - '7' - '8' - '9' - '10' - '11' - '12' - '13' - '14' - '15' - '16' - '17' - '18' - '19' - '20' - '21' - '22' - '23' - '24' - '25' - '26' description: 'Numeric SN department id - the same taxonomy as the people-search functions facet (see its legend, or lookup(type: "FUNCTION")).' min: type: integer minimum: 0 maximum: 1000000 max: type: integer minimum: 0 maximum: 1000000 required: - department - min - max description: 'Headcount of ONE department (e.g. department "25" Sales, {min: 20, max: 99999}).' department_headcount_growth: type: - object - 'null' properties: department: type: string enum: - '1' - '2' - '3' - '4' - '5' - '6' - '7' - '8' - '9' - '10' - '11' - '12' - '13' - '14' - '15' - '16' - '17' - '18' - '19' - '20' - '21' - '22' - '23' - '24' - '25' - '26' description: 'Numeric SN department id - the same taxonomy as the people-search functions facet (see its legend, or lookup(type: "FUNCTION")).' min: type: integer minimum: -100 maximum: 1000 max: type: integer minimum: -100 maximum: 1000 required: - department - min - max description: Growth of ONE department, percent - e.g. engineering team growing 10%+. hiring_on_linkedin: type: - boolean - 'null' description: true → only accounts currently hiring on LinkedIn. first_degree_connection: type: - boolean - 'null' description: true → only accounts where you have a 1st-degree connection. saved_accounts_only: type: - boolean - 'null' description: true → only your saved SN accounts. description: 'EXCLUSIVE with `url`: send filters OR url, never both (422) and never neither (422). Sales Navigator account-search filters, the COMPLETE SN vocabulary. At least one member must be non-empty (a false toggle counts as empty). lookup = scrape_linkedin_sales_nav_param_id_lookup.' page: type: integer minimum: 1 maximum: 100 description: LinkedIn page number, default 1; ONE page per call, so re-call with page + 1 while paging.has_more. ScrapeLinkedinSearchSalesNavPeopleResponse: type: object properties: success: type: boolean enum: - true operation: type: string enum: - action action: type: string description: kebab-case verb; matches the route segment. item: {} result: type: object properties: rows: type: array items: type: object properties: ln_member_id: type: - string - 'null' description: Canonical member id (decoded in-process). ln_id: type: - string - 'null' sn_id: type: - string - 'null' nickname: type: - string - 'null' full_name: type: - string - 'null' headline: type: - string - 'null' description: The card's headline line on the regular and Recruiter engines; null on Sales Navigator people search. position: type: - string - 'null' description: Recruiter search only; the regular card prints no structured position (read the headline). company_name: type: - string - 'null' description: Recruiter search only; the regular card prints no structured company (read the headline). location: type: - string - 'null' description: The card's location line on the regular and Recruiter engines; null on Sales Navigator people search. avatar_url: type: - string - 'null' description: Best effort on the regular engines (a DOM lookup, null when the card image had not rendered). connection_degree: type: - string - 'null' description: 'The degree badge as the card prints it: 1st, 2nd, 3rd or 3rd+ on the regular engines; Recruiter spells 1st, 2nd, 3rd, out_of_network.' required: - ln_member_id - ln_id - sn_id - nickname - full_name - headline - position - company_name - location - avatar_url - connection_degree description: The transient result list, in LinkedIn’s order; NOTHING persisted. paging: type: object properties: page: type: integer page_size: type: integer has_more: type: boolean total: type: - integer - 'null' required: - page - page_size - has_more - total description: 'Page-numbered paging: re-call with page + 1 while has_more.' data_request: type: object properties: {} description: 'The kind="scrape" DataRequest journal row for this call (terminal completed), embedded as result.data_request; served_from_cache always false. A receipt: its result_ref is null here, the page is result.rows next to it; get_data_request by sid reads the stored payload back. Full DataRequestDomain shape owned by ./data_requests.md, so it is left passthrough here.' required: - rows - paging - data_request meta: type: object properties: trace_id: type: string description: UUID v7; same 128-bit value as the X-Trace-Id header. span_id: type: string pattern: ^[0-9a-f]{16}$ description: 16 hex chars, root span of this request. timestamp: type: string description: ISO 8601 UTC (Y-m-dTH:i:sZ), response time. duration_ms: type: integer minimum: 0 description: Server-side wall clock. team_sid: type: - string - 'null' description: The team this call ran in (the token team, or the team_sid override). Null when unauthenticated; absent from pre-2026-08-20 backends. actor_type: type: - string - 'null' description: user | agent | api_key | system. Null when unauthenticated; absent from pre-2026-08-20 backends. required: - trace_id - span_id - timestamp - duration_ms pacing: type: object properties: linkedin_account_sid: type: string description: The account whose bucket the call spent. limit_type: type: string description: The smart-limit bucket the call spent, e.g. send_messages. next_call_after: type: string description: 'ISO 8601, the same form as retry_after: the moment this same call goes through again without waiting. The next pacing slot while the day has budget left, the daily reset when it has none, the end of a LinkedIn lock while one stands.' remaining_today: type: integer minimum: 0 description: daily_limit minus done_today_count of that bucket, after this call. required: - linkedin_account_sid - limit_type - next_call_after - remaining_today required: - success - operation - action - item - result - meta ScrapeLinkedinSearchGroupsRequest: type: object description: Request body of `scrape_linkedin_search_groups`. properties: linkedin_account_sid: type: - string - 'null' minLength: 18 maxLength: 18 pattern: ^ln_ac_ description: '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).' idempotency_key: type: - string - 'null' maxLength: 128 description: '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.' url: type: - string - 'null' maxLength: 2048 pattern: ^https:\/\/www\.linkedin\.com\/search\/results\/groups\/ description: 'EXCLUSIVE 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/search/results/groups/ : 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.' filters: type: - object - 'null' properties: keywords: type: string maxLength: 256 description: 'REQUIRED, and the WHOLE vocabulary: the groups search has no wire form without a query and no second axis to combine with it. Passed to LinkedIn verbatim.' required: - keywords additionalProperties: false description: 'EXCLUSIVE with `url`: send filters OR url, never both (422) and never neither (422). LinkedIn groups-search filters. keywords is the only member there is, and any other key is REFUSED rather than ignored. Anything else the groups screen offers is reachable only by building the search in the LinkedIn UI and pasting the URL into the url field.' page: type: integer minimum: 1 maximum: 100 description: '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.' ScrapeLinkedinSearchCompaniesResponse: type: object properties: success: type: boolean enum: - true operation: type: string enum: - action action: type: string description: kebab-case verb; matches the route segment. item: {} result: type: object properties: rows: type: array items: type: object properties: company_ln_id: type: - string - 'null' nickname: type: - string - 'null' description: 'Vanity slug; regular and similar-companies engines only (Sales Navigator: null, resolve via get-company-public-identifier).' name: type: - string - 'null' industry: type: - string - 'null' employees_size: type: - string - 'null' description: Sales Navigator size bucket ("11-50"); null on the regular engine. followers: type: - integer - 'null' description: Regular and similar-companies engines; the regular card's rounded "4K followers" reads 4000. location: type: - string - 'null' description: 'Regular engine: the card''s location line; many cards print none.' tagline: type: - string - 'null' description: Sales Navigator only. logo_url: type: - string - 'null' website: type: - string - 'null' description: On no search wire; always null. required: - company_ln_id - nickname - name - industry - employees_size - followers - location - tagline - logo_url - website description: The transient result list, in LinkedIn’s order; NOTHING persisted. paging: type: object properties: page: type: integer page_size: type: integer has_more: type: boolean total: type: - integer - 'null' required: - page - page_size - has_more - total description: 'Page-numbered paging: re-call with page + 1 while has_more.' data_request: type: object properties: {} description: 'The kind="scrape" DataRequest journal row for this call (terminal completed), embedded as result.data_request; served_from_cache always false. A receipt: its result_ref is null here, the page is result.rows next to it; get_data_request by sid reads the stored payload back. Full DataRequestDomain shape owned by ./data_requests.md, so it is left passthrough here.' required: - rows - paging - data_request meta: type: object properties: trace_id: type: string description: UUID v7; same 128-bit value as the X-Trace-Id header. span_id: type: string pattern: ^[0-9a-f]{16}$ description: 16 hex chars, root span of this request. timestamp: type: string description: ISO 8601 UTC (Y-m-dTH:i:sZ), response time. duration_ms: type: integer minimum: 0 description: Server-side wall clock. team_sid: type: - string - 'null' description: The team this call ran in (the token team, or the team_sid override). Null when unauthenticated; absent from pre-2026-08-20 backends. actor_type: type: - string - 'null' description: user | agent | api_key | system. Null when unauthenticated; absent from pre-2026-08-20 backends. required: - trace_id - span_id - timestamp - duration_ms pacing: type: object properties: linkedin_account_sid: type: string description: The account whose bucket the call spent. limit_type: type: string description: The smart-limit bucket the call spent, e.g. send_messages. next_call_after: type: string description: 'ISO 8601, the same form as retry_after: the moment this same call goes through again without waiting. The next pacing slot while the day has budget left, the daily reset when it has none, the end of a LinkedIn lock while one stands.' remaining_today: type: integer minimum: 0 description: daily_limit minus done_today_count of that bucket, after this call. required: - linkedin_account_sid - limit_type - next_call_after - remaining_today required: - success - operation - action - item - result - meta ScrapeLinkedinSearchCoursesRequest: type: object description: Request body of `scrape_linkedin_search_courses`. properties: linkedin_account_sid: type: - string - 'null' minLength: 18 maxLength: 18 pattern: ^ln_ac_ description: '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).' idempotency_key: type: - string - 'null' maxLength: 128 description: '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.' url: type: - string - 'null' maxLength: 2048 pattern: ^https:\/\/www\.linkedin\.com\/search\/results\/learning\/ description: 'EXCLUSIVE 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/search/results/learning/ : 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.' filters: type: - object - 'null' properties: keywords: type: string maxLength: 256 description: 'REQUIRED: this engine has no wire form without a query, so a filter object carrying only a difficulty or only a length band is refused rather than run wide.' difficulty: type: - array - 'null' items: type: string enum: - beginner - intermediate - advanced maxItems: 3 description: 'Course level, LinkedIn Learning''s own three. Send these readable names: the node maps each onto LinkedIn''s own label ("Beginner") while it composes the URL, so a label sent from here is refused by its validator.' time_to_complete: type: - array - 'null' items: type: string enum: - under_10_mins - 10_to_30_mins - 30_to_60_mins - 1_to_2_hours - 2_to_3_hours - 3_plus_hours maxItems: 6 description: Course-length bands, in LinkedIn's own dropdown order. Readable names only, mapped node-side exactly like difficulty ("< 10 mins", "3+ hours"); the bands are what the UI offers, so an arbitrary minute range cannot be asked for. required: - keywords additionalProperties: false description: 'EXCLUSIVE with `url`: send filters OR url, never both (422) and never neither (422). LinkedIn LEARNING course-search filters. keywords is REQUIRED; difficulty and time_to_complete are closed vocabularies whose values the NODE translates into LinkedIn''s own labels, so send the names spelled here and never a LinkedIn label. Any other key is REFUSED rather than ignored, which matters because the learning screen also ships software and subject facets this vocabulary does not carry: those are reachable only by building the search in the LinkedIn UI and pasting the URL into the url field.' page: type: integer minimum: 1 maximum: 100 description: '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.' ScrapeLinkedinSearchServiceProvidersResponse: type: object properties: success: type: boolean enum: - true operation: type: string enum: - action action: type: string description: kebab-case verb; matches the route segment. item: {} result: type: object properties: rows: type: array items: type: object properties: ln_member_id: type: - string - 'null' description: Canonical member id (decoded in-process). ln_id: type: - string - 'null' sn_id: type: - string - 'null' nickname: type: - string - 'null' full_name: type: - string - 'null' headline: type: - string - 'null' description: The card's headline line on the regular and Recruiter engines; null on Sales Navigator people search. position: type: - string - 'null' description: Recruiter search only; the regular card prints no structured position (read the headline). company_name: type: - string - 'null' description: Recruiter search only; the regular card prints no structured company (read the headline). location: type: - string - 'null' description: The card's location line on the regular and Recruiter engines; null on Sales Navigator people search. avatar_url: type: - string - 'null' description: Best effort on the regular engines (a DOM lookup, null when the card image had not rendered). connection_degree: type: - string - 'null' description: 'The degree badge as the card prints it: 1st, 2nd, 3rd or 3rd+ on the regular engines; Recruiter spells 1st, 2nd, 3rd, out_of_network.' required: - ln_member_id - ln_id - sn_id - nickname - full_name - headline - position - company_name - location - avatar_url - connection_degree description: The transient result list, in LinkedIn’s order; NOTHING persisted. paging: type: object properties: page: type: integer page_size: type: integer has_more: type: boolean total: type: - integer - 'null' required: - page - page_size - has_more - total description: 'Page-numbered paging: re-call with page + 1 while has_more.' data_request: type: object properties: {} description: 'The kind="scrape" DataRequest journal row for this call (terminal completed), embedded as result.data_request; served_from_cache always false. A receipt: its result_ref is null here, the page is result.rows next to it; get_data_request by sid reads the stored payload back. Full DataRequestDomain shape owned by ./data_requests.md, so it is left passthrough here.' required: - rows - paging - data_request meta: type: object properties: trace_id: type: string description: UUID v7; same 128-bit value as the X-Trace-Id header. span_id: type: string pattern: ^[0-9a-f]{16}$ description: 16 hex chars, root span of this request. timestamp: type: string description: ISO 8601 UTC (Y-m-dTH:i:sZ), response time. duration_ms: type: integer minimum: 0 description: Server-side wall clock. team_sid: type: - string - 'null' description: The team this call ran in (the token team, or the team_sid override). Null when unauthenticated; absent from pre-2026-08-20 backends. actor_type: type: - string - 'null' description: user | agent | api_key | system. Null when unauthenticated; absent from pre-2026-08-20 backends. required: - trace_id - span_id - timestamp - duration_ms pacing: type: object properties: linkedin_account_sid: type: string description: The account whose bucket the call spent. limit_type: type: string description: The smart-limit bucket the call spent, e.g. send_messages. next_call_after: type: string description: 'ISO 8601, the same form as retry_after: the moment this same call goes through again without waiting. The next pacing slot while the day has budget left, the daily reset when it has none, the end of a LinkedIn lock while one stands.' remaining_today: type: integer minimum: 0 description: daily_limit minus done_today_count of that bucket, after this call. required: - linkedin_account_sid - limit_type - next_call_after - remaining_today required: - success - operation - action - item - result - meta ScrapeLinkedinGetPostCommentsResponse: type: object properties: success: type: boolean enum: - true operation: type: string enum: - action action: type: string description: kebab-case verb; matches the route segment. item: {} result: type: object properties: rows: type: array items: type: object properties: commenter_ln_member_id: type: string commenter_ln_id: type: - string - 'null' commenter_nickname: type: - string - 'null' commenter_full_name: type: - string - 'null' commenter_headline: type: - string - 'null' commenter_picture_url: type: - string - 'null' comment_ln_id: type: string content: type: string posted_at: type: - string - 'null' reactions_count: type: integer description: SUM of every reaction bucket on the comment, not a like count. comment_permalink: type: - string - 'null' description: Public deep link to this comment. replies_count: type: integer description: 'How many replies the comment has. A COUNT ONLY: this verb queries the post''s comment list and fetches no replies, so there is no way to read them and no replies array to ask for.' is_pinned: type: boolean is_edited: type: boolean is_own: type: boolean description: Commenter matches one of the team’s managed accounts (same rule as sync). required: - commenter_ln_member_id - commenter_ln_id - commenter_nickname - commenter_full_name - commenter_headline - commenter_picture_url - comment_ln_id - content - posted_at - reactions_count - comment_permalink - replies_count - is_pinned - is_edited - is_own description: The transient result list, in LinkedIn’s order; NOTHING persisted. paging: type: object properties: page_size: type: integer next_cursor: type: - string - 'null' has_more: type: boolean total: type: - integer - 'null' required: - page_size - next_cursor - has_more - total description: 'Cursor paging: feed next_cursor back verbatim; null = terminal page.' data_request: type: object properties: {} description: 'The kind="scrape" DataRequest journal row for this call (terminal completed), embedded as result.data_request; served_from_cache always false. A receipt: its result_ref is null here, the page is result.rows next to it; get_data_request by sid reads the stored payload back. Full DataRequestDomain shape owned by ./data_requests.md, so it is left passthrough here.' post: type: object properties: post_ln_id: type: string required: - post_ln_id description: The resolved post target the getter ran against. skipped_invalid_rows: type: integer description: Wire comments with urn=null / no decodable author skipped. required: - rows - paging - data_request - post - skipped_invalid_rows meta: type: object properties: trace_id: type: string description: UUID v7; same 128-bit value as the X-Trace-Id header. span_id: type: string pattern: ^[0-9a-f]{16}$ description: 16 hex chars, root span of this request. timestamp: type: string description: ISO 8601 UTC (Y-m-dTH:i:sZ), response time. duration_ms: type: integer minimum: 0 description: Server-side wall clock. team_sid: type: - string - 'null' description: The team this call ran in (the token team, or the team_sid override). Null when unauthenticated; absent from pre-2026-08-20 backends. actor_type: type: - string - 'null' description: user | agent | api_key | system. Null when unauthenticated; absent from pre-2026-08-20 backends. required: - trace_id - span_id - timestamp - duration_ms pacing: type: object properties: linkedin_account_sid: type: string description: The account whose bucket the call spent. limit_type: type: string description: The smart-limit bucket the call spent, e.g. send_messages. next_call_after: type: string description: 'ISO 8601, the same form as retry_after: the moment this same call goes through again without waiting. The next pacing slot while the day has budget left, the daily reset when it has none, the end of a LinkedIn lock while one stands.' remaining_today: type: integer minimum: 0 description: daily_limit minus done_today_count of that bucket, after this call. required: - linkedin_account_sid - limit_type - next_call_after - remaining_today required: - success - operation - action - item - result - meta ScrapeLinkedinSalesNavParamIdLookupResponse: type: object properties: success: type: boolean enum: - true operation: type: string enum: - action action: type: string description: kebab-case verb; matches the route segment. item: {} result: type: object properties: rows: type: array items: type: object properties: type: type: string id: type: string description: Opaque SN facet id. Feed it VERBATIM into the SN search filters (company "urn:li:organization:1441", geo "100506914", …). display_name: type: - string - 'null' headline: type: - string - 'null' entity_urn: type: - string - 'null' image_url: type: - string - 'null' required: - type - id - display_name - headline - entity_urn - image_url description: The transient result list, in LinkedIn’s order; NOTHING persisted. paging: {} data_request: type: object properties: {} description: 'The kind="scrape" DataRequest journal row for this call (terminal completed), embedded as result.data_request; served_from_cache always false. A receipt: its result_ref is null here, the page is result.rows next to it; get_data_request by sid reads the stored payload back. Full DataRequestDomain shape owned by ./data_requests.md, so it is left passthrough here.' required: - rows - paging - data_request meta: type: object properties: trace_id: type: string description: UUID v7; same 128-bit value as the X-Trace-Id header. span_id: type: string pattern: ^[0-9a-f]{16}$ description: 16 hex chars, root span of this request. timestamp: type: string description: ISO 8601 UTC (Y-m-dTH:i:sZ), response time. duration_ms: type: integer minimum: 0 description: Server-side wall clock. team_sid: type: - string - 'null' description: The team this call ran in (the token team, or the team_sid override). Null when unauthenticated; absent from pre-2026-08-20 backends. actor_type: type: - string - 'null' description: user | agent | api_key | system. Null when unauthenticated; absent from pre-2026-08-20 backends. required: - trace_id - span_id - timestamp - duration_ms pacing: type: object properties: linkedin_account_sid: type: string description: The account whose bucket the call spent. limit_type: type: string description: The smart-limit bucket the call spent, e.g. send_messages. next_call_after: type: string description: 'ISO 8601, the same form as retry_after: the moment this same call goes through again without waiting. The next pacing slot while the day has budget left, the daily reset when it has none, the end of a LinkedIn lock while one stands.' remaining_today: type: integer minimum: 0 description: daily_limit minus done_today_count of that bucket, after this call. required: - linkedin_account_sid - limit_type - next_call_after - remaining_today required: - success - operation - action - item - result - meta ScrapeLinkedinSearchPostsResponse: type: object properties: success: type: boolean enum: - true operation: type: string enum: - action action: type: string description: kebab-case verb; matches the route segment. item: {} result: type: object properties: rows: type: array items: type: object properties: post_ln_id: type: - string - 'null' author_nickname: type: - string - 'null' description: 'The /in/ slug. Null for an ORGANIZATION author: the parser detects the company case and skips member parsing.' author_full_name: type: - string - 'null' author_headline: type: - string - 'null' author_profile_url: type: - string - 'null' description: Clean /in/ URL, no query string. Null for an organization author, exactly like author_nickname - a filled name with a null profile_url is today the only signal that a post was published by a company page. author_picture_url: type: - string - 'null' description: Author avatar, filled for members and organizations alike. content: type: - string - 'null' posted_at: type: - string - 'null' reactions_count: type: - integer - 'null' comments_count: type: - integer - 'null' reshares_count: type: - integer - 'null' required: - post_ln_id - author_nickname - author_full_name - author_headline - author_profile_url - author_picture_url - content - posted_at - reactions_count - comments_count - reshares_count description: The transient result list, in LinkedIn’s order; NOTHING persisted. paging: type: object properties: page_size: type: integer next_cursor: type: - string - 'null' has_more: type: boolean total: type: - integer - 'null' required: - page_size - next_cursor - has_more - total description: 'Cursor paging: feed next_cursor back verbatim; null = terminal page.' data_request: type: object properties: {} description: 'The kind="scrape" DataRequest journal row for this call (terminal completed), embedded as result.data_request; served_from_cache always false. A receipt: its result_ref is null here, the page is result.rows next to it; get_data_request by sid reads the stored payload back. Full DataRequestDomain shape owned by ./data_requests.md, so it is left passthrough here.' required: - rows - paging - data_request meta: type: object properties: trace_id: type: string description: UUID v7; same 128-bit value as the X-Trace-Id header. span_id: type: string pattern: ^[0-9a-f]{16}$ description: 16 hex chars, root span of this request. timestamp: type: string description: ISO 8601 UTC (Y-m-dTH:i:sZ), response time. duration_ms: type: integer minimum: 0 description: Server-side wall clock. team_sid: type: - string - 'null' description: The team this call ran in (the token team, or the team_sid override). Null when unauthenticated; absent from pre-2026-08-20 backends. actor_type: type: - string - 'null' description: user | agent | api_key | system. Null when unauthenticated; absent from pre-2026-08-20 backends. required: - trace_id - span_id - timestamp - duration_ms pacing: type: object properties: linkedin_account_sid: type: string description: The account whose bucket the call spent. limit_type: type: string description: The smart-limit bucket the call spent, e.g. send_messages. next_call_after: type: string description: 'ISO 8601, the same form as retry_after: the moment this same call goes through again without waiting. The next pacing slot while the day has budget left, the daily reset when it has none, the end of a LinkedIn lock while one stands.' remaining_today: type: integer minimum: 0 description: daily_limit minus done_today_count of that bucket, after this call. required: - linkedin_account_sid - limit_type - next_call_after - remaining_today required: - success - operation - action - item - result - meta ScrapeLinkedinParamIdLookupResponse: type: object properties: success: type: boolean enum: - true operation: type: string enum: - action action: type: string description: kebab-case verb; matches the route segment. item: {} result: type: object properties: rows: type: array items: type: object properties: type: type: string enum: - people - connections - location - company - school - industry - service_category - product_category - job_location description: Echo of the requested type, so a caller batching several lookups can tell the rows apart. id: type: string description: The raw filter value the picked option feeds, e.g. location "104001442", company "167560", people "ACoAAADnFr8Brc7l...". Not a urn. Goes into the filter member verbatim. display_name: type: string description: The option label, LinkedIn's first visible line (wire display_value). subtitle: type: - string - 'null' description: 'The option''s second line: member headline for people/connections, industry for company. null for the other six types, which render one line.' required: - type - id - display_name - subtitle description: The transient result list, in LinkedIn’s order; NOTHING persisted. paging: {} data_request: type: object properties: {} description: 'The kind="scrape" DataRequest journal row for this call (terminal completed), embedded as result.data_request; served_from_cache always false. A receipt: its result_ref is null here, the page is result.rows next to it; get_data_request by sid reads the stored payload back. Full DataRequestDomain shape owned by ./data_requests.md, so it is left passthrough here.' required: - rows - paging - data_request meta: type: object properties: trace_id: type: string description: UUID v7; same 128-bit value as the X-Trace-Id header. span_id: type: string pattern: ^[0-9a-f]{16}$ description: 16 hex chars, root span of this request. timestamp: type: string description: ISO 8601 UTC (Y-m-dTH:i:sZ), response time. duration_ms: type: integer minimum: 0 description: Server-side wall clock. team_sid: type: - string - 'null' description: The team this call ran in (the token team, or the team_sid override). Null when unauthenticated; absent from pre-2026-08-20 backends. actor_type: type: - string - 'null' description: user | agent | api_key | system. Null when unauthenticated; absent from pre-2026-08-20 backends. required: - trace_id - span_id - timestamp - duration_ms pacing: type: object properties: linkedin_account_sid: type: string description: The account whose bucket the call spent. limit_type: type: string description: The smart-limit bucket the call spent, e.g. send_messages. next_call_after: type: string description: 'ISO 8601, the same form as retry_after: the moment this same call goes through again without waiting. The next pacing slot while the day has budget left, the daily reset when it has none, the end of a LinkedIn lock while one stands.' remaining_today: type: integer minimum: 0 description: daily_limit minus done_today_count of that bucket, after this call. required: - linkedin_account_sid - limit_type - next_call_after - remaining_today required: - success - operation - action - item - result - meta ScrapeLinkedinSearchCompaniesRequest: type: object description: Request body of `scrape_linkedin_search_companies`. properties: linkedin_account_sid: type: - string - 'null' minLength: 18 maxLength: 18 pattern: ^ln_ac_ description: '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).' idempotency_key: type: - string - 'null' maxLength: 128 description: '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.' url: type: - string - 'null' maxLength: 2048 pattern: ^https:\/\/www\.linkedin\.com\/search\/results\/companies\/ description: 'EXCLUSIVE 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/search/results/companies/ : 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.' filters: type: - object - 'null' properties: keywords: type: - string - 'null' maxLength: 256 geo_ids: type: - array - 'null' items: type: string maxItems: 10 description: 'Numeric LinkedIn geo ids. scrape_linkedin_param_id_lookup(type: "location") answers in this id family, but the mapping was matched on the people search and this engine sends the ids under its own URL param, so check the first page before a long run.' industry_ids: type: - array - 'null' items: type: string maxItems: 10 description: 'Numeric LinkedIn industry ids, from scrape_linkedin_param_id_lookup(type: "industry"). Same caveat as geo_ids: the mapping is verified on the people search, not on this one.' company_sizes: type: - array - 'null' items: type: string enum: - 1_to_10 - 11_to_50 - 51_to_200 - 201_to_500 - 501_to_1000 - 1001_to_5000 - 5001_to_10000 - 10001_plus maxItems: 8 description: Headcount ranges; the node maps each range onto the LinkedIn size-bucket id itself. network: type: - array - 'null' items: type: string enum: - 1st maxItems: 1 description: Companies where you have 1st-degree connections; the company search takes the first degree only. has_jobs: type: - boolean - 'null' description: true → only companies hiring on LinkedIn right now; false sends nothing. description: 'EXCLUSIVE with `url`: send filters OR url, never both (422) and never neither (422). Regular company-search filters. At least one member must be non-empty.' page: type: integer minimum: 1 maximum: 100 description: LinkedIn page number, default 1; ONE page per call, so re-call with page + 1 while paging.has_more. McpError: type: object properties: success: type: boolean enum: - false error: type: object properties: code: type: string enum: - validation_failed - nothing_to_update - not_found - relation_not_found - invalid_transition - limit_exceeded - payment_required - duplicate_rejected - conflict - delete_blocked - unauthorized - forbidden - rate_limited - internal_error - service_unavailable - not_implemented message: type: string recoverable: type: boolean suggestion: type: string field_errors: type: object additionalProperties: type: array items: type: object properties: rule: type: string message: type: string required: - rule - message blockers: type: array items: type: object properties: type: type: string description: Machine-readable blocker type (active_flow, pending_tasks, …). severity: type: string enum: - hard - soft description: hard = external action required; soft = acknowledge is enough. description: type: string entity_sid: type: - string - 'null' count: type: integer resolution: type: string description: 'Hard: tool name to call. Soft: code for acknowledge[].' resolution_hint: type: string required: - type - severity - description - entity_sid - resolution - resolution_hint context: type: object additionalProperties: {} required: - code - message - recoverable meta: type: object properties: trace_id: type: string description: UUID v7; same 128-bit value as the X-Trace-Id header. span_id: type: string pattern: ^[0-9a-f]{16}$ description: 16 hex chars, root span of this request. timestamp: type: string description: ISO 8601 UTC (Y-m-dTH:i:sZ), response time. duration_ms: type: integer minimum: 0 description: Server-side wall clock. team_sid: type: - string - 'null' description: The team this call ran in (the token team, or the team_sid override). Null when unauthenticated; absent from pre-2026-08-20 backends. actor_type: type: - string - 'null' description: user | agent | api_key | system. Null when unauthenticated; absent from pre-2026-08-20 backends. required: - trace_id - span_id - timestamp - duration_ms required: - success - error ScrapeLinkedinSearchSalesNavCompaniesResponse: type: object properties: success: type: boolean enum: - true operation: type: string enum: - action action: type: string description: kebab-case verb; matches the route segment. item: {} result: type: object properties: rows: type: array items: type: object properties: company_ln_id: type: - string - 'null' nickname: type: - string - 'null' description: 'Vanity slug; regular and similar-companies engines only (Sales Navigator: null, resolve via get-company-public-identifier).' name: type: - string - 'null' industry: type: - string - 'null' employees_size: type: - string - 'null' description: Sales Navigator size bucket ("11-50"); null on the regular engine. followers: type: - integer - 'null' description: Regular and similar-companies engines; the regular card's rounded "4K followers" reads 4000. location: type: - string - 'null' description: 'Regular engine: the card''s location line; many cards print none.' tagline: type: - string - 'null' description: Sales Navigator only. logo_url: type: - string - 'null' website: type: - string - 'null' description: On no search wire; always null. required: - company_ln_id - nickname - name - industry - employees_size - followers - location - tagline - logo_url - website description: The transient result list, in LinkedIn’s order; NOTHING persisted. paging: type: object properties: page: type: integer page_size: type: integer has_more: type: boolean total: type: - integer - 'null' required: - page - page_size - has_more - total description: 'Page-numbered paging: re-call with page + 1 while has_more.' data_request: type: object properties: {} description: 'The kind="scrape" DataRequest journal row for this call (terminal completed), embedded as result.data_request; served_from_cache always false. A receipt: its result_ref is null here, the page is result.rows next to it; get_data_request by sid reads the stored payload back. Full DataRequestDomain shape owned by ./data_requests.md, so it is left passthrough here.' required: - rows - paging - data_request meta: type: object properties: trace_id: type: string description: UUID v7; same 128-bit value as the X-Trace-Id header. span_id: type: string pattern: ^[0-9a-f]{16}$ description: 16 hex chars, root span of this request. timestamp: type: string description: ISO 8601 UTC (Y-m-dTH:i:sZ), response time. duration_ms: type: integer minimum: 0 description: Server-side wall clock. team_sid: type: - string - 'null' description: The team this call ran in (the token team, or the team_sid override). Null when unauthenticated; absent from pre-2026-08-20 backends. actor_type: type: - string - 'null' description: user | agent | api_key | system. Null when unauthenticated; absent from pre-2026-08-20 backends. required: - trace_id - span_id - timestamp - duration_ms pacing: type: object properties: linkedin_account_sid: type: string description: The account whose bucket the call spent. limit_type: type: string description: The smart-limit bucket the call spent, e.g. send_messages. next_call_after: type: string description: 'ISO 8601, the same form as retry_after: the moment this same call goes through again without waiting. The next pacing slot while the day has budget left, the daily reset when it has none, the end of a LinkedIn lock while one stands.' remaining_today: type: integer minimum: 0 description: daily_limit minus done_today_count of that bucket, after this call. required: - linkedin_account_sid - limit_type - next_call_after - remaining_today required: - success - operation - action - item - result - meta ScrapeLinkedinSearchEventsResponse: type: object properties: success: type: boolean enum: - true operation: type: string enum: - action action: type: string description: kebab-case verb; matches the route segment. item: {} result: type: object properties: rows: type: array items: type: object properties: event_ln_id: type: string description: LinkedIn's numeric event id (the wire's event_id), the only stable identity an event row carries. A card whose id cannot be read is dropped rather than returned half-formed, and a repeat of an id already seen on this page is dropped too. event_url: type: string description: https://www.linkedin.com/events/{event_ln_id}/ , composed from the id, so it is stable and free of tracking parameters. title: type: string description: Event title. A card whose title cannot be read is dropped, so this is never null. date_text: type: - string - 'null' description: The date line as LinkedIn printed it ("Wed, Sep 16 - Fri, Sep 18", "Wed, Jul 29, 11:00 AM", "Jul 29, 2026"). Free text in the viewing account's own timezone, not a timestamp, and start and end are not split out. location_text: type: - string - 'null' description: 'Where it happens, as printed: "Online", "In person", "Hybrid" or a venue. When the card packs location and organizer into one bulleted line, this holds the left half only.' organizer: type: - string - 'null' description: The "By <name>" half of that same line, prefix stripped. null whenever the card printed no bullet, which is the common case, so null here means the card did not name an organizer rather than that the event has none. description: type: - string - 'null' description: 'The card''s blurb: the longest small-print line that is not the attendee counter. LinkedIn truncates it on the card, so it is a snippet, not the event description.' attendee_count: type: - integer - 'null' description: Attendees, parsed out of attendees_text. 0 is a REAL value and means the card said "Be the first attendee"; null means no counter was read at all. Do not conflate the two. A compact counter is rounded on the way in ("2.3K attendees" becomes 2300), so a large number is an approximation. attendees_text: type: - string - 'null' description: The counter exactly as printed ("4 attendees", "2.3K attendees", "Be the first attendee"). Read this whenever the rounding in attendee_count would matter. thumbnail_url: type: - string - 'null' description: 'Event cover image, widest rendition available. Best effort: a card renders several images and the most repeated asset is taken as the cover.' position: type: - integer - 'null' description: The card's slot on THIS page, 0 based and page local, so it is not a rank across pages. Numbering happens before duplicate ids are dropped, which can leave gaps. The parser always fills it, so null means the key was missing from the wire row. required: - event_ln_id - event_url - title - date_text - location_text - organizer - description - attendee_count - attendees_text - thumbnail_url - position description: The transient result list, in LinkedIn’s order; NOTHING persisted. paging: type: object properties: page: type: integer page_size: type: integer has_more: type: boolean total: type: - integer - 'null' required: - page - page_size - has_more - total description: 'Page-numbered paging: re-call with page + 1 while has_more. total carries LinkedIn''s own result count when the page printed one, best effort and null otherwise, so drive the loop from has_more.' data_request: type: object properties: {} description: 'The kind="scrape" DataRequest journal row for this call (terminal completed), embedded as result.data_request; served_from_cache always false. A receipt: its result_ref is null here, the page is result.rows next to it; get_data_request by sid reads the stored payload back. Full DataRequestDomain shape owned by ./data_requests.md, so it is left passthrough here.' required: - rows - paging - data_request meta: type: object properties: trace_id: type: string description: UUID v7; same 128-bit value as the X-Trace-Id header. span_id: type: string pattern: ^[0-9a-f]{16}$ description: 16 hex chars, root span of this request. timestamp: type: string description: ISO 8601 UTC (Y-m-dTH:i:sZ), response time. duration_ms: type: integer minimum: 0 description: Server-side wall clock. team_sid: type: - string - 'null' description: The team this call ran in (the token team, or the team_sid override). Null when unauthenticated; absent from pre-2026-08-20 backends. actor_type: type: - string - 'null' description: user | agent | api_key | system. Null when unauthenticated; absent from pre-2026-08-20 backends. required: - trace_id - span_id - timestamp - duration_ms pacing: type: object properties: linkedin_account_sid: type: string description: The account whose bucket the call spent. limit_type: type: string description: The smart-limit bucket the call spent, e.g. send_messages. next_call_after: type: string description: 'ISO 8601, the same form as retry_after: the moment this same call goes through again without waiting. The next pacing slot while the day has budget left, the daily reset when it has none, the end of a LinkedIn lock while one stands.' remaining_today: type: integer minimum: 0 description: daily_limit minus done_today_count of that bucket, after this call. required: - linkedin_account_sid - limit_type - next_call_after - remaining_today required: - success - operation - action - item - result - meta ScrapeLinkedinSearchPostsRequest: type: object description: Request body of `scrape_linkedin_search_posts`. properties: linkedin_account_sid: type: - string - 'null' minLength: 18 maxLength: 18 pattern: ^ln_ac_ description: '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).' idempotency_key: type: - string - 'null' maxLength: 128 description: '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.' url: type: - string - 'null' maxLength: 2048 pattern: ^https:\/\/www\.linkedin\.com\/search\/results\/content\/ description: 'EXCLUSIVE 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/search/results/content/ : 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.' filters: type: - object - 'null' properties: keywords: type: string maxLength: 256 description: 'REQUIRED: content search needs a query.' date_posted: type: - string - 'null' enum: - past_24h - past_week - past_month sort_by: type: - string - 'null' enum: - relevance - date_posted description: Default relevance. content_type: type: - array - 'null' items: type: string enum: - videos - photos - jobs - liveVideos - documents maxItems: 5 description: Post media/format facet; wire-ready content-type tokens. posted_by: type: - array - 'null' items: type: string enum: - following - me - first maxItems: 3 description: 'Author-relationship facet: following / me / first (1st-degree).' author_job_title: type: - string - 'null' maxLength: 256 description: Author current-title keywords. from_member: type: - array - 'null' items: type: string maxItems: 10 description: Posts authored by these members (facet ids/urns). This is the real author-by-member facet. from_organization: type: - array - 'null' items: type: string maxItems: 10 description: Posts authored by these organizations (facet ids/urns). author_company: type: - array - 'null' items: type: string maxItems: 10 description: Author-company facet ids. author_industry: type: - array - 'null' items: type: string maxItems: 10 description: Author-industry facet ids. mentions_member: type: - array - 'null' items: type: string maxItems: 10 description: Posts mentioning these members (facet ids/urns). mentions_organization: type: - array - 'null' items: type: string maxItems: 10 description: Posts mentioning these organizations (facet ids/urns). required: - keywords description: 'EXCLUSIVE with `url`: send filters OR url, never both (422) and never neither (422). Content-search filters; keywords required.' page_size: type: - integer - 'null' minimum: 1 maximum: 50 description: 'Rows per page (1..50); default 10. The cap is LinkedIn''s: its content search serves at most 50 posts per page and answers a larger request with an empty page, so 51+ is refused (422 page_size_above_linkedin_max) rather than returned as 3 rows with has_more false. Ask for 50 or fewer and follow paging.next_cursor. The first page may carry up to 3 rows when you ask for fewer: every content search opens with a cluster of 3.' cursor: type: - string - 'null' maxLength: 2048 description: Opaque resume token from paging.next_cursor; null/omitted = first page. ScrapeLinkedinSearchSalesNavPeopleRequest: type: object description: Request body of `scrape_linkedin_search_sales_nav_people`. properties: linkedin_account_sid: type: - string - 'null' minLength: 18 maxLength: 18 pattern: ^ln_ac_ description: '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).' idempotency_key: type: - string - 'null' maxLength: 128 description: '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.' url: type: - string - 'null' maxLength: 2048 pattern: ^https:\/\/www\.linkedin\.com\/sales\/search\/people description: 'EXCLUSIVE 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/sales/search/people : 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.' filters: type: - object - 'null' properties: keywords: type: - string - 'null' maxLength: 256 first_name: type: - string - 'null' maxLength: 100 description: Text-only wire facet (no id space). last_name: type: - string - 'null' maxLength: 100 description: Text-only wire facet (no id space). current_titles: type: - array - 'null' items: type: object properties: id: type: - string - 'null' maxLength: 128 description: Opaque SN facet id, VERBATIM from scrape_linkedin_sales_nav_param_id_lookup. text: type: - string - 'null' maxLength: 256 description: Free text when no id is given (SN matches it on titles and companies), else the id's label. exclude: type: - boolean - 'null' description: true → EXCLUDED (negative filter); omitted/false → INCLUDED. description: 'One facet value: at least one of id / text.' maxItems: 10 description: 'Current job titles. Ids via lookup(type: "TITLE") (numeric, e.g. "5" Director) - or just free text: [{text: "VP Marketing"}].' past_titles: type: - array - 'null' items: type: object properties: id: type: - string - 'null' maxLength: 128 description: Opaque SN facet id, VERBATIM from scrape_linkedin_sales_nav_param_id_lookup. text: type: - string - 'null' maxLength: 256 description: Free text when no id is given (SN matches it on titles and companies), else the id's label. exclude: type: - boolean - 'null' description: true → EXCLUDED (negative filter); omitted/false → INCLUDED. description: 'One facet value: at least one of id / text.' maxItems: 10 description: 'Past job titles. Ids via lookup(type: "TITLE") or free text.' locations: type: - array - 'null' items: type: object properties: id: type: string maxLength: 128 description: 'Opaque SN facet id, VERBATIM from scrape_linkedin_sales_nav_param_id_lookup. REQUIRED: LinkedIn ignores a value without its id on this facet.' text: type: - string - 'null' maxLength: 256 description: The id's label, optional. exclude: type: - boolean - 'null' description: true → EXCLUDED (negative filter); omitted/false → INCLUDED. required: - id description: One facet value, by id. maxItems: 10 description: 'Person geography, by id via lookup(type: "BING_GEO") (e.g. "103644278" United States); regions like DACH/EMEA exist too.' company_headquarters: type: - array - 'null' items: type: object properties: id: type: string maxLength: 128 description: 'Opaque SN facet id, VERBATIM from scrape_linkedin_sales_nav_param_id_lookup. REQUIRED: LinkedIn ignores a value without its id on this facet.' text: type: - string - 'null' maxLength: 256 description: The id's label, optional. exclude: type: - boolean - 'null' description: true → EXCLUDED (negative filter); omitted/false → INCLUDED. required: - id description: One facet value, by id. maxItems: 10 description: 'CURRENT COMPANY HQ region (not the person’s own location), by id via lookup(type: "BING_GEO").' industries: type: - array - 'null' items: type: object properties: id: type: string maxLength: 128 description: 'Opaque SN facet id, VERBATIM from scrape_linkedin_sales_nav_param_id_lookup. REQUIRED: LinkedIn ignores a value without its id on this facet.' text: type: - string - 'null' maxLength: 256 description: The id's label, optional. exclude: type: - boolean - 'null' description: true → EXCLUDED (negative filter); omitted/false → INCLUDED. required: - id description: One facet value, by id. maxItems: 10 description: 'Industries, by id via lookup(type: "INDUSTRY") (numeric).' current_companies: type: - array - 'null' items: type: object properties: id: type: - string - 'null' maxLength: 128 description: Opaque SN facet id, VERBATIM from scrape_linkedin_sales_nav_param_id_lookup. text: type: - string - 'null' maxLength: 256 description: Free text when no id is given (SN matches it on titles and companies), else the id's label. exclude: type: - boolean - 'null' description: true → EXCLUDED (negative filter); omitted/false → INCLUDED. description: 'One facet value: at least one of id / text.' maxItems: 10 description: 'Current employer. Ids via lookup(type: "COMPANY_WITH_LIST") - id shape urn:li:organization:N - or free text. exclude: true is the classic "not my customers" move.' past_companies: type: - array - 'null' items: type: object properties: id: type: - string - 'null' maxLength: 128 description: Opaque SN facet id, VERBATIM from scrape_linkedin_sales_nav_param_id_lookup. text: type: - string - 'null' maxLength: 256 description: Free text when no id is given (SN matches it on titles and companies), else the id's label. exclude: type: - boolean - 'null' description: true → EXCLUDED (negative filter); omitted/false → INCLUDED. description: 'One facet value: at least one of id / text.' maxItems: 10 description: 'Past employer. Ids via lookup(type: "COMPANY_WITH_LIST") or free text.' groups: type: - array - 'null' items: type: object properties: id: type: string maxLength: 128 description: 'Opaque SN facet id, VERBATIM from scrape_linkedin_sales_nav_param_id_lookup. REQUIRED: LinkedIn ignores a value without its id on this facet.' text: type: - string - 'null' maxLength: 256 description: The id's label, optional. exclude: type: - boolean - 'null' description: true → EXCLUDED (negative filter); omitted/false → INCLUDED. required: - id description: One facet value, by id. maxItems: 10 description: 'LinkedIn group membership, by id via lookup(type: "GROUP").' schools: type: - array - 'null' items: type: object properties: id: type: string maxLength: 128 description: 'Opaque SN facet id, VERBATIM from scrape_linkedin_sales_nav_param_id_lookup. REQUIRED: LinkedIn ignores a value without its id on this facet.' text: type: - string - 'null' maxLength: 256 description: The id's label, optional. exclude: type: - boolean - 'null' description: true → EXCLUDED (negative filter); omitted/false → INCLUDED. required: - id description: One facet value, by id. maxItems: 10 description: 'Schools attended, by id via lookup(type: "SCHOOL").' seniority_levels: type: - array - 'null' items: type: string enum: - '100' - '110' - '120' - '130' - '200' - '210' - '220' - '300' - '310' - '320' maxItems: 10 description: 'SENIORITY_V2: ''100'' In Training, ''110'' Entry Level, ''120'' Senior, ''130'' Strategic, ''200'' Entry Level Manager, ''210'' Experienced Manager, ''220'' Director, ''300'' Vice President, ''310'' CXO, ''320'' Owner/Partner.' functions: type: - array - 'null' items: type: string enum: - '1' - '2' - '3' - '4' - '5' - '6' - '7' - '8' - '9' - '10' - '11' - '12' - '13' - '14' - '15' - '16' - '17' - '18' - '19' - '20' - '21' - '22' - '23' - '24' - '25' - '26' maxItems: 10 description: 'Job function: ''1'' Accounting, ''2'' Administrative, ''3'' Arts and Design, ''4'' Business Development, ''5'' Community and Social Services, ''6'' Consulting, ''7'' Education, ''8'' Engineering, ''9'' Entrepreneurship, ''10'' Finance, ''11'' Healthcare Services, ''12'' Human Resources, ''13'' Information Technology, ''14'' Legal, ''15'' Marketing, ''16'' Media and Communication, ''17'' Military and Protective Services, ''18'' Operations, ''19'' Product Management, ''20'' Program and Project Management, ''21'' Purchasing, ''22'' Quality Assurance, ''23'' Real Estate, ''24'' Research, ''25'' Sales, ''26'' Customer Success and Support.' years_in_current_company: type: - array - 'null' items: type: string enum: - '1' - '2' - '3' - '4' - '5' maxItems: 5 description: 'Tenure at current company: ''1'' <1 year, ''2'' 1-2, ''3'' 3-5, ''4'' 6-10, ''5'' 10+ years.' years_in_current_position: type: - array - 'null' items: type: string enum: - '1' - '2' - '3' - '4' - '5' maxItems: 5 description: 'Tenure in current position: ''1'' <1 year, ''2'' 1-2, ''3'' 3-5, ''4'' 6-10, ''5'' 10+ years.' years_of_experience: type: - array - 'null' items: type: string enum: - '1' - '2' - '3' - '4' - '5' maxItems: 5 description: 'Total career length: ''1'' <1 year, ''2'' 1-2, ''3'' 3-5, ''4'' 6-10, ''5'' 10+ years.' company_headcounts: type: - array - 'null' items: type: string enum: - A - B - C - D - E - F - G - H - I maxItems: 10 description: 'Company size: ''A'' Self-employed, ''B'' 1-10, ''C'' 11-50, ''D'' 51-200, ''E'' 201-500, ''F'' 501-1000, ''G'' 1001-5000, ''H'' 5001-10000, ''I'' 10001+.' company_types: type: - array - 'null' items: type: string enum: - C - P - N - D - S - E - O - G maxItems: 10 description: 'Company type: ''C'' Public, ''P'' Privately Held, ''N'' Non-profit, ''D'' Educational, ''S'' Partnership, ''E'' Self-Employed, ''O'' Self-Owned, ''G'' Government.' profile_languages: type: - array - 'null' items: type: string enum: - ar - en - es - pt - zh - fr - it - ru - de - nl - tr - tl - pl - ko - ja - ms - 'no' - da - ro - sv - in - cs maxItems: 10 description: Profile language, ISO 639-1. network: type: - array - 'null' items: type: string enum: - F - S - A - O maxItems: 4 description: 'Relationship degree: ''F'' 1st, ''S'' 2nd, ''A'' group members, ''O'' 3rd+.' connections_of: type: - array - 'null' items: type: string maxLength: 64 maxItems: 10 description: 'People connected to these members. Tokens (ACwA…) via lookup(type: "CONNECTION_OF").' description: 'EXCLUSIVE with `url`: send filters OR url, never both (422) and never neither (422). Sales Navigator people-search filters, the COMPLETE SN facet vocabulary. At least one member must be non-empty. lookup = scrape_linkedin_sales_nav_param_id_lookup.' page: type: integer minimum: 1 maximum: 100 description: LinkedIn page number, default 1; ONE page per call, so re-call with page + 1 while paging.has_more. ScrapeLinkedinSearchProductsResponse: type: object properties: success: type: boolean enum: - true operation: type: string enum: - action action: type: string description: kebab-case verb; matches the route segment. item: {} result: type: object properties: rows: type: array items: type: object properties: product_slug: type: string description: 'The /products/{slug} path segment, and the ONLY identity a product row carries: this wire has no numeric id anywhere. ⚠️ The node calls this field product_id; it is NOT an id and must never be sent as one into product_category or product_company. A card whose slug cannot be read is dropped, and a repeat of a slug already seen on this page is dropped too.' product_url: type: string description: 'https://www.linkedin.com/products/{product_slug}/ , composed from the slug, so it is stable and free of tracking parameters. Pasting it back into this tool''s url field is refused: it is one product''s page, not a search.' name: type: string description: Product name, read off the card's single bold text run rather than out of English copy, so it survives a non-English interface. A card without one is dropped, so this is never null. category_text: type: - string - 'null' description: 'The category line as printed ("CRM Software"). ⚠️ Derived by elimination: it is the first small text run that is NOT the "By:" line, so on a non-English interface, where the By: test stops matching, this field can carry the COMPANY name instead. Unreliable rather than absent off English.' company: type: - string - 'null' description: 'The vendor, from the card''s "By: <name>" line with the prefix stripped. A bare NAME: no organization id and no link, so it cannot be fed into product_company without a separate lookup. Null on a non-English interface.' tagline: type: - string - 'null' description: The product's one-line pitch. ⚠️ Picked as the longest remaining small-print run after the Top-Features and connections lines are excluded by their English prefixes. On a non-English interface both exclusions stop working, so this most often holds the TOP-FEATURES list (usually the longest run) and sometimes the connections line. top_features: type: - string - 'null' description: The "Top Features:" line with the prefix stripped, a single comma-joined string rather than a list. Null when the card does not print one, and null on a non-English interface. connections_count: type: - integer - 'null' description: 'How many of your connections can be asked about this product, parsed from the text below, so "1.2K" becomes 1200. ROUNDED, because the string it came from was: LinkedIn never printed an exact number.' connections_text: type: - string - 'null' description: That same counter exactly as printed ("1.2K connections to ask about this product"). Kept alongside the number because the number is a rounding of it. logo_url: type: - string - 'null' description: 'Product artwork, widest rendition available. Best effort: a card renders several images and the most repeated asset is taken as the logo.' position: type: - integer - 'null' description: The card's slot on THIS page, 0 based and page local, so it is not a rank across pages. Numbering happens before dropped and duplicate cards are removed, which can leave gaps. required: - product_slug - product_url - name - category_text - company - tagline - top_features - connections_count - connections_text - logo_url - position description: The transient result list, in LinkedIn’s order; NOTHING persisted. paging: type: object properties: page: type: integer page_size: type: integer has_more: type: boolean total: type: - integer - 'null' required: - page - page_size - has_more - total description: 'Page-numbered paging: re-call with page + 1 while has_more. total carries LinkedIn''s own result count when the page printed one, best effort and null otherwise, so drive the loop from has_more.' data_request: type: object properties: {} description: 'The kind="scrape" DataRequest journal row for this call (terminal completed), embedded as result.data_request; served_from_cache always false. A receipt: its result_ref is null here, the page is result.rows next to it; get_data_request by sid reads the stored payload back. Full DataRequestDomain shape owned by ./data_requests.md, so it is left passthrough here.' required: - rows - paging - data_request meta: type: object properties: trace_id: type: string description: UUID v7; same 128-bit value as the X-Trace-Id header. span_id: type: string pattern: ^[0-9a-f]{16}$ description: 16 hex chars, root span of this request. timestamp: type: string description: ISO 8601 UTC (Y-m-dTH:i:sZ), response time. duration_ms: type: integer minimum: 0 description: Server-side wall clock. team_sid: type: - string - 'null' description: The team this call ran in (the token team, or the team_sid override). Null when unauthenticated; absent from pre-2026-08-20 backends. actor_type: type: - string - 'null' description: user | agent | api_key | system. Null when unauthenticated; absent from pre-2026-08-20 backends. required: - trace_id - span_id - timestamp - duration_ms pacing: type: object properties: linkedin_account_sid: type: string description: The account whose bucket the call spent. limit_type: type: string description: The smart-limit bucket the call spent, e.g. send_messages. next_call_after: type: string description: 'ISO 8601, the same form as retry_after: the moment this same call goes through again without waiting. The next pacing slot while the day has budget left, the daily reset when it has none, the end of a LinkedIn lock while one stands.' remaining_today: type: integer minimum: 0 description: daily_limit minus done_today_count of that bucket, after this call. required: - linkedin_account_sid - limit_type - next_call_after - remaining_today required: - success - operation - action - item - result - meta ScrapeLinkedinBuildSearchUrlResponse: type: object properties: success: type: boolean enum: - true operation: type: string enum: - action action: type: string description: kebab-case verb; matches the route segment. item: {} result: type: object properties: url: type: string description: 'The search URL: open it, paste it into the matching search tool''s url, or hand it to an import.' filters: type: object properties: {} description: 'The filters in the exact shape the matching search tool takes as `filters`: texts replaced by the ids they resolved to.' resolved: type: array items: type: object properties: member: type: string description: The filter member the value sits in. query: type: string description: The text as given (trimmed). lookup_type: type: string description: The typeahead type it went through (location, company, TITLE, BING_GEO, ACCOUNT_LIST, ...). id: type: - string - 'null' description: The id picked; null when the typeahead offered nothing and the text rides as a Sales Navigator free-text chip. display_name: type: - string - 'null' description: LinkedIn's label of the picked option, also the chip text on Sales Navigator. alternatives: type: array items: type: object properties: id: type: string display_name: type: - string - 'null' required: - id - display_name description: 'The other options LinkedIn offered, best first: resend the value with one of these ids to choose another.' required: - member - query - lookup_type - id - display_name - alternatives description: One entry per text value that went through a typeahead, in filter order; empty when every value carried an id. data_requests: type: array items: type: object properties: {} description: 'The kind="scrape" DataRequest journal row for this call (terminal completed), embedded as result.data_request; served_from_cache always false. A receipt: its result_ref is null here, the page is result.rows next to it; get_data_request by sid reads the stored payload back. Full DataRequestDomain shape owned by ./data_requests.md, so it is left passthrough here.' description: The kind="scrape" ledger rows of the typeahead lookups this call ran, one per distinct text; empty when nothing was resolved. required: - url - filters - resolved - data_requests meta: type: object properties: trace_id: type: string description: UUID v7; same 128-bit value as the X-Trace-Id header. span_id: type: string pattern: ^[0-9a-f]{16}$ description: 16 hex chars, root span of this request. timestamp: type: string description: ISO 8601 UTC (Y-m-dTH:i:sZ), response time. duration_ms: type: integer minimum: 0 description: Server-side wall clock. team_sid: type: - string - 'null' description: The team this call ran in (the token team, or the team_sid override). Null when unauthenticated; absent from pre-2026-08-20 backends. actor_type: type: - string - 'null' description: user | agent | api_key | system. Null when unauthenticated; absent from pre-2026-08-20 backends. required: - trace_id - span_id - timestamp - duration_ms pacing: type: object properties: linkedin_account_sid: type: string description: The account whose bucket the call spent. limit_type: type: string description: The smart-limit bucket the call spent, e.g. send_messages. next_call_after: type: string description: 'ISO 8601, the same form as retry_after: the moment this same call goes through again without waiting. The next pacing slot while the day has budget left, the daily reset when it has none, the end of a LinkedIn lock while one stands.' remaining_today: type: integer minimum: 0 description: daily_limit minus done_today_count of that bucket, after this call. required: - linkedin_account_sid - limit_type - next_call_after - remaining_today required: - success - operation - action - item - result - meta ScrapeLinkedinSearchEventsRequest: type: object description: Request body of `scrape_linkedin_search_events`. properties: linkedin_account_sid: type: - string - 'null' minLength: 18 maxLength: 18 pattern: ^ln_ac_ description: '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).' idempotency_key: type: - string - 'null' maxLength: 128 description: '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.' url: type: - string - 'null' maxLength: 2048 pattern: ^https:\/\/www\.linkedin\.com\/search\/results\/events\/ description: 'EXCLUSIVE 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/search/results/events/ : 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.' filters: type: - object - 'null' properties: keywords: type: string maxLength: 256 description: 'REQUIRED, and the WHOLE vocabulary: the events search has no wire form without a query and no second axis to combine with it. Passed to LinkedIn verbatim.' required: - keywords additionalProperties: false description: 'EXCLUSIVE with `url`: send filters OR url, never both (422) and never neither (422). LinkedIn events-search filters. keywords is the only member there is, because it is the only one the engine accepts, and any other key is REFUSED rather than ignored. Every facet the LinkedIn events screen itself offers is reachable ONLY by building the search in that UI and pasting the URL into the url field.' page: type: integer minimum: 1 maximum: 100 description: '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.' ScrapeLinkedinGetPostResharersRequest: type: object description: Request body of `scrape_linkedin_get_post_resharers`. properties: linkedin_account_sid: type: - string - 'null' minLength: 18 maxLength: 18 pattern: ^ln_ac_ description: '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).' idempotency_key: type: - string - 'null' maxLength: 128 description: '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.' post: type: string maxLength: 512 description: A post URL, an activity URN, or a backend post urn (`urn:li:share:<id>` / `urn:li:ugcPost:<id>`, passed verbatim). NOT `urn:li:groupPost:` - the reshare feed refuses it, so we refuse it locally by name. URL forms are parsed locally at no extra cost; a link with no id in it (a shortlink, a bare slug URL) is resolved by opening the page through get_activity_urn_by_url, which adds one extra page load to this call and is cached 7 days. The post does NOT need to be tracked or owned by you. page_size: type: integer minimum: 1 maximum: 100 description: Default 50. cursor: type: - string - 'null' description: Opaque resume token; null = first page. required: - post ScrapeLinkedinSearchPeopleRequest: type: object description: Request body of `scrape_linkedin_search_people`. properties: linkedin_account_sid: type: - string - 'null' minLength: 18 maxLength: 18 pattern: ^ln_ac_ description: '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).' idempotency_key: type: - string - 'null' maxLength: 128 description: '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.' url: type: - string - 'null' maxLength: 2048 pattern: ^https:\/\/www\.linkedin\.com\/search\/results\/people\/ description: 'EXCLUSIVE 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/search/results/people/ : 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.' filters: type: - object - 'null' properties: keywords: type: - string - 'null' maxLength: 256 description: Free-text query. first_name: type: - string - 'null' maxLength: 100 last_name: type: - string - 'null' maxLength: 100 title: type: - string - 'null' maxLength: 256 description: Current-title keywords. company: type: - string - 'null' maxLength: 256 description: Current-company keywords (free text; prefer current_companies). school: type: - string - 'null' maxLength: 256 description: 'School free-text: this engine has no school-id facet, so lookup(type: "school") ids fit nowhere here.' network: type: - array - 'null' items: type: string enum: - 1st - 2nd - 3rd_plus maxItems: 3 description: Relationship-degree facet; the node maps each degree onto the LinkedIn wire code (F/S/O) itself. locations: type: - array - 'null' items: type: string maxItems: 10 description: 'Numeric geo ids, via lookup(type: "location").' industries: type: - array - 'null' items: type: string maxItems: 10 description: 'Numeric industry ids, via lookup(type: "industry").' current_companies: type: - array - 'null' items: type: string maxItems: 10 description: 'Numeric company ids, via lookup(type: "company").' past_companies: type: - array - 'null' items: type: string maxItems: 10 description: 'Numeric company ids. The typeahead has no past-company axis; lookup(type: "company") answers in what looks like the same org-id space, which is our reading of the wire rather than a verified mapping.' service_categories: type: - array - 'null' items: type: string maxItems: 10 description: 'Numeric service-category ids, via lookup(type: "service_category").' connections_of: type: - array - 'null' items: type: string maxItems: 10 description: 'Profile ids (ACoA…): people in these profiles’ connections. Via lookup(type: "connections").' followers_of: type: - array - 'null' items: type: string maxItems: 10 description: 'Profile ids (ACoA…): people following these profiles. Via lookup(type: "people").' profile_languages: type: - array - 'null' items: type: string maxItems: 10 description: ISO 639-1 language codes. open_to_volunteer: type: - boolean - 'null' description: 'EXCLUSIVE with `url`: send filters OR url, never both (422) and never neither (422). Regular people-search filters. At least one member must be non-empty. Members are LinkedIn-ready wire values (passed through untranslated), except network, whose degrees the node maps onto LinkedIn codes. lookup = scrape_linkedin_param_id_lookup, and each id-bearing member names the type it takes.' page: type: integer minimum: 1 maximum: 100 description: LinkedIn page number, default 1; ONE page per call, so re-call with page + 1 while paging.has_more. ScrapeLinkedinSearchSchoolsResponse: type: object properties: success: type: boolean enum: - true operation: type: string enum: - action action: type: string description: kebab-case verb; matches the route segment. item: {} result: type: object properties: rows: type: array items: type: object properties: school_slug: type: string description: The /school/{slug} path segment, and the ONLY identity a school row carries. ⚠️ The node calls this field school_id; it is a vanity slug, NOT the numeric id param_id_lookup returns for type=school, and the two are unrelated. ⚠️ It can be URL-ENCODED (an apostrophe arrives as %27) because the parser never decodes it; pass it through as-is or school_url stops matching. school_url: type: string description: 'https://www.linkedin.com/school/{school_slug}/ , composed from the slug, so it is stable and free of tracking parameters. Pasting it back into this tool''s url field is refused: it is one school''s page, not a search.' name: type: string description: School name, read off the card's single bold text run rather than out of English copy, so it survives a non-English interface. A card without one is dropped, so this is never null. location_text: type: - string - 'null' description: The location line as printed ("Stanford, California"). Free text, never parsed into city / country. Positional rather than prefix-matched, so unlike the products category line it stays correct on a non-English interface; simply absent on schools that print none. students_alumni_count: type: - integer - 'null' description: 'How many students and alumni LinkedIn reports, parsed from the text below, so "448K" becomes 448000. ROUNDED, because the string it came from was: LinkedIn never printed an exact number. Not an employee count and not comparable to a company headcount.' students_alumni_text: type: - string - 'null' description: That same counter exactly as printed ("448K students and alumni on LinkedIn"). ⚠️ Read through the one English pattern on this card, so it goes null on a non-English interface, and see the description warning below. description: type: - string - 'null' description: The school blurb. ⚠️ Picked as the longest small-print run once the students-and-alumni line is excluded by its English wording. On a non-English interface that exclusion stops working, so this field can silently hold the STUDENTS line instead of a description. logo_url: type: - string - 'null' description: 'School logo, widest rendition available. Best effort: a card renders several images and the most repeated asset is taken as the logo.' position: type: - integer - 'null' description: The card's slot on THIS page, 0 based and page local, so it is not a rank across pages. Numbering happens before dropped cards are removed, which can leave gaps. required: - school_slug - school_url - name - location_text - students_alumni_count - students_alumni_text - description - logo_url - position description: The transient result list, in LinkedIn’s order; NOTHING persisted. paging: type: object properties: page: type: integer page_size: type: integer has_more: type: boolean total: type: - integer - 'null' required: - page - page_size - has_more - total description: 'Page-numbered paging: re-call with page + 1 while has_more. total carries LinkedIn''s own result count when the page printed one, best effort and null otherwise, so drive the loop from has_more.' data_request: type: object properties: {} description: 'The kind="scrape" DataRequest journal row for this call (terminal completed), embedded as result.data_request; served_from_cache always false. A receipt: its result_ref is null here, the page is result.rows next to it; get_data_request by sid reads the stored payload back. Full DataRequestDomain shape owned by ./data_requests.md, so it is left passthrough here.' required: - rows - paging - data_request meta: type: object properties: trace_id: type: string description: UUID v7; same 128-bit value as the X-Trace-Id header. span_id: type: string pattern: ^[0-9a-f]{16}$ description: 16 hex chars, root span of this request. timestamp: type: string description: ISO 8601 UTC (Y-m-dTH:i:sZ), response time. duration_ms: type: integer minimum: 0 description: Server-side wall clock. team_sid: type: - string - 'null' description: The team this call ran in (the token team, or the team_sid override). Null when unauthenticated; absent from pre-2026-08-20 backends. actor_type: type: - string - 'null' description: user | agent | api_key | system. Null when unauthenticated; absent from pre-2026-08-20 backends. required: - trace_id - span_id - timestamp - duration_ms pacing: type: object properties: linkedin_account_sid: type: string description: The account whose bucket the call spent. limit_type: type: string description: The smart-limit bucket the call spent, e.g. send_messages. next_call_after: type: string description: 'ISO 8601, the same form as retry_after: the moment this same call goes through again without waiting. The next pacing slot while the day has budget left, the daily reset when it has none, the end of a LinkedIn lock while one stands.' remaining_today: type: integer minimum: 0 description: daily_limit minus done_today_count of that bucket, after this call. required: - linkedin_account_sid - limit_type - next_call_after - remaining_today required: - success - operation - action - item - result - meta ScrapeLinkedinSearchRecruiterPeopleRequest: type: object description: Request body of `scrape_linkedin_search_recruiter_people`. properties: linkedin_account_sid: type: - string - 'null' minLength: 18 maxLength: 18 pattern: ^ln_ac_ description: '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).' idempotency_key: type: - string - 'null' maxLength: 128 description: '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.' url: type: - string - 'null' maxLength: 2048 pattern: ^https:\/\/www\.linkedin\.com\/talent\/search description: 'EXCLUSIVE 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/talent/search : 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. The URL LinkedIn Recruiter shows after a search (/talent/search?...searchHistoryId=N...): it names the search by its searchHistoryId only, so a URL without one is refused (422) rather than run as a keywords-only search. Use the search_url a previous answer returned to page on.' filters: type: - object - 'null' properties: keywords: type: - string - 'null' maxLength: 256 first_name: type: - string - 'null' maxLength: 100 last_name: type: - string - 'null' maxLength: 100 job_titles: type: - array - 'null' items: type: object properties: id: type: - string - 'null' maxLength: 128 description: The typeahead row id VERBATIM (urn:li:ts_*:N; the lowercase language name for spoken_languages). A bare numeric id is accepted for the urn kinds. text: type: - string - 'null' maxLength: 256 description: 'The chip label (the typeahead row''s display_name). REQUIRED on job_titles / occupations / skills / companies / postal_codes, id or no id: LinkedIn refuses an entity chip without its label. On job_titles / skills / companies a text-only chip is a free-text search term.' exclude: type: - boolean - 'null' description: true → the chip is a negative filter. required: type: - boolean - 'null' description: true → a must-have chip (the Recruiter UI "required" toggle); omitted → nice-to-have. scope: type: - string - 'null' enum: - CURRENT - PAST - CURRENT_OR_PAST - PAST_NOT_CURRENT description: Where in the career the chip must sit; omitted = CURRENT_OR_PAST. description: 'One Recruiter facet chip: at least one of id / text.' maxItems: 20 description: 'Job titles. Ids via lookup(type: "occupation") (urn:li:ts_title:N) or free text: [{text: "Head of Growth"}]. scope: ''CURRENT'' | ''PAST'' | ''CURRENT_OR_PAST'' (default) | ''PAST_NOT_CURRENT''.' occupations: type: - array - 'null' items: type: object properties: id: type: - string - 'null' maxLength: 128 description: The typeahead row id VERBATIM (urn:li:ts_*:N; the lowercase language name for spoken_languages). A bare numeric id is accepted for the urn kinds. text: type: - string - 'null' maxLength: 256 description: 'The chip label (the typeahead row''s display_name). REQUIRED on job_titles / occupations / skills / companies / postal_codes, id or no id: LinkedIn refuses an entity chip without its label. On job_titles / skills / companies a text-only chip is a free-text search term.' exclude: type: - boolean - 'null' description: true → the chip is a negative filter. required: type: - boolean - 'null' description: true → a must-have chip (the Recruiter UI "required" toggle); omitted → nice-to-have. scope: type: - string - 'null' enum: - CURRENT - PAST - CURRENT_OR_PAST - PAST_NOT_CURRENT description: Where in the career the chip must sit; omitted = CURRENT_OR_PAST. description: 'One Recruiter facet chip: at least one of id / text.' maxItems: 20 description: 'Standardized titles (LinkedIn''s title taxonomy), id REQUIRED via lookup(type: "occupation"). scope: ''CURRENT'' | ''PAST'' | ''CURRENT_OR_PAST'' (default) | ''PAST_NOT_CURRENT''.' skills: type: - array - 'null' items: type: object properties: id: type: - string - 'null' maxLength: 128 description: The typeahead row id VERBATIM (urn:li:ts_*:N; the lowercase language name for spoken_languages). A bare numeric id is accepted for the urn kinds. text: type: - string - 'null' maxLength: 256 description: 'The chip label (the typeahead row''s display_name). REQUIRED on job_titles / occupations / skills / companies / postal_codes, id or no id: LinkedIn refuses an entity chip without its label. On job_titles / skills / companies a text-only chip is a free-text search term.' exclude: type: - boolean - 'null' description: true → the chip is a negative filter. required: type: - boolean - 'null' description: true → a must-have chip (the Recruiter UI "required" toggle); omitted → nice-to-have. description: 'One Recruiter facet chip: at least one of id / text.' maxItems: 20 description: 'Skills. Ids via lookup(type: "skill") (urn:li:ts_skill:N) or free text.' companies: type: - array - 'null' items: type: object properties: id: type: - string - 'null' maxLength: 128 description: The typeahead row id VERBATIM (urn:li:ts_*:N; the lowercase language name for spoken_languages). A bare numeric id is accepted for the urn kinds. text: type: - string - 'null' maxLength: 256 description: 'The chip label (the typeahead row''s display_name). REQUIRED on job_titles / occupations / skills / companies / postal_codes, id or no id: LinkedIn refuses an entity chip without its label. On job_titles / skills / companies a text-only chip is a free-text search term.' exclude: type: - boolean - 'null' description: true → the chip is a negative filter. required: type: - boolean - 'null' description: true → a must-have chip (the Recruiter UI "required" toggle); omitted → nice-to-have. scope: type: - string - 'null' enum: - CURRENT - PAST - CURRENT_OR_PAST - PAST_NOT_CURRENT description: Where in the career the chip must sit; omitted = CURRENT_OR_PAST. description: 'One Recruiter facet chip: at least one of id / text.' maxItems: 20 description: 'Employers, current or past. Ids via lookup(type: "company") (urn:li:ts_company:N) or free text; exclude: true is the classic "not my own company". scope: ''CURRENT'' | ''PAST'' | ''CURRENT_OR_PAST'' (default) | ''PAST_NOT_CURRENT''.' current_companies: type: - array - 'null' items: type: object properties: id: type: - string - 'null' maxLength: 128 description: The typeahead row id VERBATIM (urn:li:ts_*:N; the lowercase language name for spoken_languages). A bare numeric id is accepted for the urn kinds. text: type: - string - 'null' maxLength: 256 description: 'The chip label (the typeahead row''s display_name). REQUIRED on job_titles / occupations / skills / companies / postal_codes, id or no id: LinkedIn refuses an entity chip without its label. On job_titles / skills / companies a text-only chip is a free-text search term.' exclude: type: - boolean - 'null' description: true → the chip is a negative filter. required: type: - boolean - 'null' description: true → a must-have chip (the Recruiter UI "required" toggle); omitted → nice-to-have. description: 'One Recruiter facet chip: at least one of id / text.' maxItems: 20 description: 'The employer right now (the CURRENT_COMPANY facet). Ids via lookup(type: "company").' locations: type: - array - 'null' items: type: object properties: id: type: - string - 'null' maxLength: 128 description: The typeahead row id VERBATIM (urn:li:ts_*:N; the lowercase language name for spoken_languages). A bare numeric id is accepted for the urn kinds. text: type: - string - 'null' maxLength: 256 description: 'The chip label (the typeahead row''s display_name). REQUIRED on job_titles / occupations / skills / companies / postal_codes, id or no id: LinkedIn refuses an entity chip without its label. On job_titles / skills / companies a text-only chip is a free-text search term.' exclude: type: - boolean - 'null' description: true → the chip is a negative filter. required: type: - boolean - 'null' description: true → a must-have chip (the Recruiter UI "required" toggle); omitted → nice-to-have. scope: type: - string - 'null' enum: - CURRENT - PREFERRED_NOT_CURRENT - CURRENT_OR_PREFERRED description: '''CURRENT'' lives there, ''PREFERRED_NOT_CURRENT'' would move there, ''CURRENT_OR_PREFERRED'' either.' description: 'One Recruiter facet chip: at least one of id / text.' maxItems: 20 description: 'Geography. Ids via lookup(type: "geo") (urn:li:ts_geo:N). scope: ''CURRENT'' (lives there) | ''PREFERRED_NOT_CURRENT'' (would move there) | ''CURRENT_OR_PREFERRED''.' postal_codes: type: - array - 'null' items: type: object properties: id: type: - string - 'null' maxLength: 128 description: The typeahead row id VERBATIM (urn:li:ts_*:N; the lowercase language name for spoken_languages). A bare numeric id is accepted for the urn kinds. text: type: - string - 'null' maxLength: 256 description: 'The chip label (the typeahead row''s display_name). REQUIRED on job_titles / occupations / skills / companies / postal_codes, id or no id: LinkedIn refuses an entity chip without its label. On job_titles / skills / companies a text-only chip is a free-text search term.' exclude: type: - boolean - 'null' description: true → the chip is a negative filter. required: type: - boolean - 'null' description: true → a must-have chip (the Recruiter UI "required" toggle); omitted → nice-to-have. description: 'One Recruiter facet chip: at least one of id / text.' maxItems: 20 description: 'Postal codes. id via lookup(type: "zip") (urn:li:ts_geo:N) REQUIRED, plus its text; a text-only postal code is refused. Pair with postal_code_distance.' postal_code_distance: type: - integer - 'null' minimum: 1 maximum: 500 description: Radius around every postal code, miles. Default 50. industries: type: - array - 'null' items: type: object properties: id: type: - string - 'null' maxLength: 128 description: The typeahead row id VERBATIM (urn:li:ts_*:N; the lowercase language name for spoken_languages). A bare numeric id is accepted for the urn kinds. text: type: - string - 'null' maxLength: 256 description: 'The chip label (the typeahead row''s display_name). REQUIRED on job_titles / occupations / skills / companies / postal_codes, id or no id: LinkedIn refuses an entity chip without its label. On job_titles / skills / companies a text-only chip is a free-text search term.' exclude: type: - boolean - 'null' description: true → the chip is a negative filter. required: type: - boolean - 'null' description: true → a must-have chip (the Recruiter UI "required" toggle); omitted → nice-to-have. description: 'One Recruiter facet chip: at least one of id / text.' maxItems: 20 description: 'Industries. Ids via lookup(type: "industry") (urn:li:ts_industry:N).' schools: type: - array - 'null' items: type: object properties: id: type: - string - 'null' maxLength: 128 description: The typeahead row id VERBATIM (urn:li:ts_*:N; the lowercase language name for spoken_languages). A bare numeric id is accepted for the urn kinds. text: type: - string - 'null' maxLength: 256 description: 'The chip label (the typeahead row''s display_name). REQUIRED on job_titles / occupations / skills / companies / postal_codes, id or no id: LinkedIn refuses an entity chip without its label. On job_titles / skills / companies a text-only chip is a free-text search term.' exclude: type: - boolean - 'null' description: true → the chip is a negative filter. required: type: - boolean - 'null' description: true → a must-have chip (the Recruiter UI "required" toggle); omitted → nice-to-have. description: 'One Recruiter facet chip: at least one of id / text.' maxItems: 20 description: 'Schools attended. Ids via lookup(type: "school"): the row id is the school''s urn:li:ts_organization:N, which is what this facet matches on (a ts_school id answers zero).' fields_of_study: type: - array - 'null' items: type: object properties: id: type: - string - 'null' maxLength: 128 description: The typeahead row id VERBATIM (urn:li:ts_*:N; the lowercase language name for spoken_languages). A bare numeric id is accepted for the urn kinds. text: type: - string - 'null' maxLength: 256 description: 'The chip label (the typeahead row''s display_name). REQUIRED on job_titles / occupations / skills / companies / postal_codes, id or no id: LinkedIn refuses an entity chip without its label. On job_titles / skills / companies a text-only chip is a free-text search term.' exclude: type: - boolean - 'null' description: true → the chip is a negative filter. required: type: - boolean - 'null' description: true → a must-have chip (the Recruiter UI "required" toggle); omitted → nice-to-have. description: 'One Recruiter facet chip: at least one of id / text.' maxItems: 20 description: 'Fields of study. Ids via lookup(type: "fieldOfStudy") (urn:li:ts_field_of_study:N).' degrees: type: - array - 'null' items: type: object properties: id: type: - string - 'null' maxLength: 128 description: The typeahead row id VERBATIM (urn:li:ts_*:N; the lowercase language name for spoken_languages). A bare numeric id is accepted for the urn kinds. text: type: - string - 'null' maxLength: 256 description: 'The chip label (the typeahead row''s display_name). REQUIRED on job_titles / occupations / skills / companies / postal_codes, id or no id: LinkedIn refuses an entity chip without its label. On job_titles / skills / companies a text-only chip is a free-text search term.' exclude: type: - boolean - 'null' description: true → the chip is a negative filter. required: type: - boolean - 'null' description: true → a must-have chip (the Recruiter UI "required" toggle); omitted → nice-to-have. description: 'One Recruiter facet chip: at least one of id / text.' maxItems: 20 description: 'Degrees. Ids via lookup(type: "degree") (urn:li:ts_degree:N).' spoken_languages: type: - array - 'null' items: type: object properties: id: type: - string - 'null' maxLength: 128 description: The typeahead row id VERBATIM (urn:li:ts_*:N; the lowercase language name for spoken_languages). A bare numeric id is accepted for the urn kinds. text: type: - string - 'null' maxLength: 256 description: 'The chip label (the typeahead row''s display_name). REQUIRED on job_titles / occupations / skills / companies / postal_codes, id or no id: LinkedIn refuses an entity chip without its label. On job_titles / skills / companies a text-only chip is a free-text search term.' exclude: type: - boolean - 'null' description: true → the chip is a negative filter. required: type: - boolean - 'null' description: true → a must-have chip (the Recruiter UI "required" toggle); omitted → nice-to-have. scope: type: - string - 'null' enum: - ELEMENTARY - LIMITED_WORKING - PROFESSIONAL_WORKING - FULL_PROFESSIONAL - NATIVE_OR_BILINGUAL description: Minimum proficiency of the spoken language. description: 'One Recruiter facet chip: at least one of id / text.' maxItems: 20 description: 'Languages the member speaks. Ids via lookup(type: "language") - the lowercase language name, e.g. "danish". scope = minimum proficiency: ''ELEMENTARY'' | ''LIMITED_WORKING'' | ''PROFESSIONAL_WORKING'' | ''FULL_PROFESSIONAL'' | ''NATIVE_OR_BILINGUAL''.' network: type: - array - 'null' items: type: string enum: - F - S - A - O maxItems: 4 description: 'Relationship: ''F'' 1st, ''S'' 2nd, ''A'' group members, ''O'' 3rd+ and everyone else.' seniority_levels: type: - array - 'null' items: type: string enum: - '1' - '2' - '3' - '4' - '5' - '6' - '7' - '8' - '9' - '10' maxItems: 10 description: 'Seniority: ''1'' Unpaid, ''2'' Training, ''3'' Entry, ''4'' Senior, ''5'' Manager, ''6'' Director, ''7'' VP, ''8'' CXO, ''9'' Partner, ''10'' Owner. NOT the Sales Navigator codes.' functions: type: - array - 'null' items: type: string enum: - '1' - '2' - '3' - '4' - '5' - '6' - '7' - '8' - '9' - '10' - '11' - '12' - '13' - '14' - '15' - '16' - '17' - '18' - '19' - '20' - '21' - '22' - '23' - '24' - '25' - '26' maxItems: 26 description: 'Job function: ''1'' Accounting, ''2'' Administrative, ''3'' Arts and Design, ''4'' Business Development, ''5'' Community and Social Services, ''6'' Consulting, ''7'' Education, ''8'' Engineering, ''9'' Entrepreneurship, ''10'' Finance, ''11'' Healthcare Services, ''12'' Human Resources, ''13'' Information Technology, ''14'' Legal, ''15'' Marketing, ''16'' Media and Communication, ''17'' Military and Protective Services, ''18'' Operations, ''19'' Product Management, ''20'' Program and Project Management, ''21'' Purchasing, ''22'' Quality Assurance, ''23'' Real Estate, ''24'' Research, ''25'' Sales, ''26'' Customer Success and Support.' company_sizes: type: - array - 'null' items: type: string enum: - A - B - C - D - E - F - G - H - I maxItems: 9 description: 'Company size: ''A'' Self-employed, ''B'' 1-10, ''C'' 11-50, ''D'' 51-200, ''E'' 201-500, ''F'' 501-1000, ''G'' 1001-5000, ''H'' 5001-10000, ''I'' 10001+.' company_types: type: - array - 'null' items: type: string enum: - P - C - D - N - G - S - O - E - A maxItems: 9 description: 'Company type: ''P'' Privately Held, ''C'' Public Company, ''D'' Educational Institution, ''N'' Non Profit, ''G'' Government Agency, ''S'' Partnership, ''O'' Self Owned, ''E'' Self Employed, ''A'' College Page.' profile_languages: type: - array - 'null' items: type: string enum: - en - es - fr - pt - zh - de - it - in - tr - pl - nl - ru - ar - cs - sv - ro - ko - da - 'no' - uk - ms - ja - _o maxItems: 23 description: Profile language, ISO 639-1 ('_o' = others). recently_joined: type: - array - 'null' items: type: string enum: - '1' - '2' - '3' - '4' - '5' maxItems: 5 description: 'Joined LinkedIn: ''1'' 1 day ago, ''2'' 2-7 days, ''3'' 8-14 days, ''4'' 15-30 days, ''5'' 1-3 months ago.' workplace_preferences: type: - array - 'null' items: type: string enum: - O - R - H maxItems: 3 description: 'Workplace: ''O'' on-site, ''R'' remote, ''H'' hybrid.' employment_types: type: - array - 'null' items: type: string enum: - F - P - C - I maxItems: 4 description: 'Employment type: ''F'' full-time, ''P'' part-time, ''C'' contract, ''I'' internship (the only codes LinkedIn Recruiter accepts here).' us_military_background: type: - boolean - 'null' description: true → members with a US military background (IS_VETERAN); false dispatches nothing. years_in_current_position: type: - object - 'null' properties: min: type: integer minimum: 1 maximum: 30 max: type: integer minimum: 1 maximum: 30 required: - min - max description: Years in the current position, {min, max} in 1..30 (30 = 30+). years_in_current_company: type: - object - 'null' properties: min: type: integer minimum: 1 maximum: 30 max: type: integer minimum: 1 maximum: 30 required: - min - max description: Years at the current company, {min, max} in 1..30. years_of_experience: type: - object - 'null' properties: min: type: integer minimum: 1 maximum: 30 max: type: integer minimum: 1 maximum: 30 required: - min - max description: Total years of experience, {min, max} in 1..30. description: 'EXCLUSIVE with `url`: send filters OR url, never both (422) and never neither (422). LinkedIn Recruiter people-search filters, the COMPLETE Recruiter facet vocabulary. At least one member must be non-empty. lookup = scrape_linkedin_recruiter_param_id_lookup; chip facets take [{id, text, exclude, required, scope}], closed enums take flat code arrays (their full sets are inline), the three year sliders take {min, max}.' page: type: integer minimum: 1 maximum: 40 description: LinkedIn Recruiter page number, default 1; 25 hits a page, 40 pages at most (1000 hits per search). Re-call with page + 1 while paging.has_more. On the url half OUR number wins over the start= in the URL. ScrapeLinkedinSearchSchoolsRequest: type: object description: Request body of `scrape_linkedin_search_schools`. properties: linkedin_account_sid: type: - string - 'null' minLength: 18 maxLength: 18 pattern: ^ln_ac_ description: '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).' idempotency_key: type: - string - 'null' maxLength: 128 description: '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.' url: type: - string - 'null' maxLength: 2048 pattern: ^https:\/\/www\.linkedin\.com\/search\/results\/schools\/ description: 'EXCLUSIVE 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/search/results/schools/ : 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.' filters: type: - object - 'null' properties: keywords: type: string maxLength: 256 description: 'REQUIRED, and the WHOLE vocabulary: the schools search has no wire form without a query and no second axis to combine with it. Passed to LinkedIn verbatim.' required: - keywords additionalProperties: false description: 'EXCLUSIVE with `url`: send filters OR url, never both (422) and never neither (422). LinkedIn school-search filters. keywords is the ONLY member the node reads, so any other key is REFUSED rather than ignored. In particular there is NO school-id member: the ids param_id_lookup returns for type=school fit no filter on this engine and reach LinkedIn only inside a pasted people-search URL. Every facet the schools screen offers beyond the keyword box is reachable only by building the search in the LinkedIn UI and pasting that URL into the url field.' page: type: integer minimum: 1 maximum: 100 description: '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.' ScrapeLinkedinSearchGroupsResponse: type: object properties: success: type: boolean enum: - true operation: type: string enum: - action action: type: string description: kebab-case verb; matches the route segment. item: {} result: type: object properties: rows: type: array items: type: object properties: group_ln_id: type: string description: LinkedIn's numeric group id, the only stable identity a group row carries. A card whose id cannot be read is dropped rather than returned half-formed, and a repeat of an id already seen on this page is dropped too. group_url: type: string description: https://www.linkedin.com/groups/{group_ln_id}/ , composed from the id, so it is stable and free of tracking parameters. name: type: string description: Group name. A card whose name cannot be read is dropped, so this is never null, which is exactly why a non-English executor returns an empty page instead of nameless rows. privacy: type: - string - 'null' description: '"Public" or "Private", LinkedIn''s own casing, read from the card''s "Public Group" / "Private Group" label. English-only, so it goes null on any other interface language rather than guessing.' member_count: type: - integer - 'null' description: Members, parsed out of members_text. 0 is a REAL value; null means no counter was read at all. Do not conflate the two, and do not test for falsiness. members_text: type: - string - 'null' description: The counter exactly as LinkedIn printed it ("1,204 members"). description: type: - string - 'null' description: 'Best-effort blurb, ASSEMBLED rather than read: LinkedIn splits the text into several chunks, so the parser collects the human-looking strings in the card and joins them. It can pick up unrelated card copy, so do not hang hard product logic on it.' logo_url: type: - string - 'null' description: 'Group logo, widest rendition available. Best effort: the card renders several images and the most repeated asset is taken as the logo, so cross-card bleed is possible.' position: type: - integer - 'null' description: The card's slot on THIS page, 0 based and page local, so it is not a rank across pages. Numbering happens before duplicate ids are dropped, which can leave gaps. The parser always fills it, so null means the key was missing from the wire row. required: - group_ln_id - group_url - name - privacy - member_count - members_text - description - logo_url - position description: The transient result list, in LinkedIn’s order; NOTHING persisted. paging: type: object properties: page: type: integer page_size: type: integer has_more: type: boolean total: type: - integer - 'null' required: - page - page_size - has_more - total description: 'Page-numbered paging: re-call with page + 1 while has_more. total carries LinkedIn''s own result count when the page printed one, best effort and null otherwise, so drive the loop from has_more.' data_request: type: object properties: {} description: 'The kind="scrape" DataRequest journal row for this call (terminal completed), embedded as result.data_request; served_from_cache always false. A receipt: its result_ref is null here, the page is result.rows next to it; get_data_request by sid reads the stored payload back. Full DataRequestDomain shape owned by ./data_requests.md, so it is left passthrough here.' required: - rows - paging - data_request meta: type: object properties: trace_id: type: string description: UUID v7; same 128-bit value as the X-Trace-Id header. span_id: type: string pattern: ^[0-9a-f]{16}$ description: 16 hex chars, root span of this request. timestamp: type: string description: ISO 8601 UTC (Y-m-dTH:i:sZ), response time. duration_ms: type: integer minimum: 0 description: Server-side wall clock. team_sid: type: - string - 'null' description: The team this call ran in (the token team, or the team_sid override). Null when unauthenticated; absent from pre-2026-08-20 backends. actor_type: type: - string - 'null' description: user | agent | api_key | system. Null when unauthenticated; absent from pre-2026-08-20 backends. required: - trace_id - span_id - timestamp - duration_ms pacing: type: object properties: linkedin_account_sid: type: string description: The account whose bucket the call spent. limit_type: type: string description: The smart-limit bucket the call spent, e.g. send_messages. next_call_after: type: string description: 'ISO 8601, the same form as retry_after: the moment this same call goes through again without waiting. The next pacing slot while the day has budget left, the daily reset when it has none, the end of a LinkedIn lock while one stands.' remaining_today: type: integer minimum: 0 description: daily_limit minus done_today_count of that bucket, after this call. required: - linkedin_account_sid - limit_type - next_call_after - remaining_today required: - success - operation - action - item - result - meta ScrapeLinkedinSearchJobsResponse: type: object properties: success: type: boolean enum: - true operation: type: string enum: - action action: type: string description: kebab-case verb; matches the route segment. item: {} result: type: object properties: rows: type: array items: type: object properties: job_ln_id: type: string description: LinkedIn's numeric posting id, the only stable identity a job row carries. A card whose id cannot be read is dropped rather than returned half-formed. job_url: type: string description: https://www.linkedin.com/jobs/view/{job_ln_id}/ , composed from the id, so it is stable and free of tracking parameters. title: type: string description: Posting title. A card whose title cannot be read is dropped, so this is never null. company_name: type: - string - 'null' company_logo_url: type: - string - 'null' description: 'Best effort: a card renders several images and the employer logo is picked as the most frequently repeated one.' location: type: - string - 'null' description: The card's location line VERBATIM, workplace parenthetical included ("Yerevan, Armenia (Hybrid)"). null whenever the card shows no parenthetical, even when a city is plainly on screen. workplace_type: type: - string - 'null' enum: - On-site - Remote - Hybrid description: Read out of the same parenthetical as location, so the two are null together and consistent together. listed_date_text: type: - string - 'null' description: Posting age the way LinkedIn writes it ("Posted 3 hours ago", "Posted 1 month ago"). Free text, not a timestamp. salary_text: type: - string - 'null' description: 'Compensation as printed on the card ("$18K/yr - $25K/yr"): currency, range and period all live inside the one string, nothing is broken out. Best effort.' easy_apply: type: boolean description: The posting takes LinkedIn's one-click Easy Apply. is_early_applicant: type: boolean description: The card carries LinkedIn's "Be an early applicant" hint. is_verified: type: boolean description: LinkedIn marks this as a verified job. social_proof_text: type: - string - 'null' description: The card's connection or alumni line ("1 connection works here", "93 school alumni work here"). Both kinds land here undifferentiated. Best effort. position: type: - integer - 'null' description: The card's slot on THIS page, 0 based and page local. It is not a rank across pages, so do not use it as a cross-page ordinal. The parser always fills it, so null here means the key was missing from the wire row, not that the card had no slot. required: - job_ln_id - job_url - title - company_name - company_logo_url - location - workplace_type - listed_date_text - salary_text - easy_apply - is_early_applicant - is_verified - social_proof_text - position description: The transient result list, in LinkedIn’s order; NOTHING persisted. paging: type: object properties: page: type: integer page_size: type: integer has_more: type: boolean total: type: - integer - 'null' required: - page - page_size - has_more - total description: 'Page-numbered paging: re-call with page + 1 while has_more. total carries LinkedIn''s own result count when the page printed one, best effort and null otherwise, so drive the loop from has_more.' data_request: type: object properties: {} description: 'The kind="scrape" DataRequest journal row for this call (terminal completed), embedded as result.data_request; served_from_cache always false. A receipt: its result_ref is null here, the page is result.rows next to it; get_data_request by sid reads the stored payload back. Full DataRequestDomain shape owned by ./data_requests.md, so it is left passthrough here.' required: - rows - paging - data_request meta: type: object properties: trace_id: type: string description: UUID v7; same 128-bit value as the X-Trace-Id header. span_id: type: string pattern: ^[0-9a-f]{16}$ description: 16 hex chars, root span of this request. timestamp: type: string description: ISO 8601 UTC (Y-m-dTH:i:sZ), response time. duration_ms: type: integer minimum: 0 description: Server-side wall clock. team_sid: type: - string - 'null' description: The team this call ran in (the token team, or the team_sid override). Null when unauthenticated; absent from pre-2026-08-20 backends. actor_type: type: - string - 'null' description: user | agent | api_key | system. Null when unauthenticated; absent from pre-2026-08-20 backends. required: - trace_id - span_id - timestamp - duration_ms pacing: type: object properties: linkedin_account_sid: type: string description: The account whose bucket the call spent. limit_type: type: string description: The smart-limit bucket the call spent, e.g. send_messages. next_call_after: type: string description: 'ISO 8601, the same form as retry_after: the moment this same call goes through again without waiting. The next pacing slot while the day has budget left, the daily reset when it has none, the end of a LinkedIn lock while one stands.' remaining_today: type: integer minimum: 0 description: daily_limit minus done_today_count of that bucket, after this call. required: - linkedin_account_sid - limit_type - next_call_after - remaining_today required: - success - operation - action - item - result - meta ScrapeLinkedinSearchServiceProvidersRequest: type: object description: Request body of `scrape_linkedin_search_service_providers`. properties: linkedin_account_sid: type: - string - 'null' minLength: 18 maxLength: 18 pattern: ^ln_ac_ description: '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).' idempotency_key: type: - string - 'null' maxLength: 128 description: '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.' url: type: - string - 'null' maxLength: 2048 pattern: ^https:\/\/www\.linkedin\.com\/search\/results\/services\/ description: 'EXCLUSIVE 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/search/results/services/ : 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.' filters: type: - object - 'null' properties: keywords: type: - string - 'null' maxLength: 256 service_categories: type: - array - 'null' items: type: string pattern: ^\d+$ maxItems: 10 description: 'LinkedIn''s own numeric service-category ids, from scrape_linkedin_param_id_lookup(type: "service_category"). The same ids the people search takes in its own service_categories member.' locations: type: - array - 'null' items: type: string pattern: ^\d+$ maxItems: 10 description: 'Numeric LinkedIn geo ids, from scrape_linkedin_param_id_lookup(type: "location").' network: type: - array - 'null' items: type: string enum: - 1st - 2nd - 3rd_plus maxItems: 3 description: Relationship-degree facet; the node maps each degree onto the LinkedIn wire code (F/S/O) itself. profile_languages: type: - array - 'null' items: type: string pattern: ^[a-z]{2}$ maxItems: 10 description: ISO 639-1 language codes. description: 'EXCLUSIVE with `url`: send filters OR url, never both (422) and never neither (422). Service-provider marketplace filters. At least one member must be non-empty. Anything these five cannot express is reachable by pasting the UI URL into this tool''s own url field instead.' page: type: integer minimum: 1 maximum: 100 description: LinkedIn page number, default 1; ONE page per call, so re-call with page + 1 while paging.has_more. ScrapeLinkedinGetPostReactorsResponse: type: object properties: success: type: boolean enum: - true operation: type: string enum: - action action: type: string description: kebab-case verb; matches the route segment. item: {} result: type: object properties: rows: type: array items: type: object properties: reactor_ln_member_id: type: string reactor_ln_id: type: - string - 'null' reactor_nickname: type: - string - 'null' reactor_full_name: type: - string - 'null' reactor_headline: type: - string - 'null' reactor_picture_url: type: - string - 'null' reaction_type: type: string enum: - like - celebrate - support - love - insightful - funny - interested description: The reaction, in OUR write vocabulary (the same names react_linkedin_post takes; wire INTEREST reads as insightful, MAYBE as interested). Until 2026-08-21 the wire names leaked here lowercased. reacted_at: type: - string - 'null' is_own: type: boolean required: - reactor_ln_member_id - reactor_ln_id - reactor_nickname - reactor_full_name - reactor_headline - reactor_picture_url - reaction_type - reacted_at - is_own description: The transient result list, in LinkedIn’s order; NOTHING persisted. paging: type: object properties: page_size: type: integer next_cursor: type: - string - 'null' has_more: type: boolean total: type: - integer - 'null' required: - page_size - next_cursor - has_more - total description: 'Cursor paging: feed next_cursor back verbatim; null = terminal page.' data_request: type: object properties: {} description: 'The kind="scrape" DataRequest journal row for this call (terminal completed), embedded as result.data_request; served_from_cache always false. A receipt: its result_ref is null here, the page is result.rows next to it; get_data_request by sid reads the stored payload back. Full DataRequestDomain shape owned by ./data_requests.md, so it is left passthrough here.' post: type: object properties: post_ln_id: type: string required: - post_ln_id description: The resolved post target the getter ran against. skipped_null_actors: type: integer description: Banned/suspended reactor slots skipped (KNOWLEDGE §7a). required: - rows - paging - data_request - post - skipped_null_actors meta: type: object properties: trace_id: type: string description: UUID v7; same 128-bit value as the X-Trace-Id header. span_id: type: string pattern: ^[0-9a-f]{16}$ description: 16 hex chars, root span of this request. timestamp: type: string description: ISO 8601 UTC (Y-m-dTH:i:sZ), response time. duration_ms: type: integer minimum: 0 description: Server-side wall clock. team_sid: type: - string - 'null' description: The team this call ran in (the token team, or the team_sid override). Null when unauthenticated; absent from pre-2026-08-20 backends. actor_type: type: - string - 'null' description: user | agent | api_key | system. Null when unauthenticated; absent from pre-2026-08-20 backends. required: - trace_id - span_id - timestamp - duration_ms pacing: type: object properties: linkedin_account_sid: type: string description: The account whose bucket the call spent. limit_type: type: string description: The smart-limit bucket the call spent, e.g. send_messages. next_call_after: type: string description: 'ISO 8601, the same form as retry_after: the moment this same call goes through again without waiting. The next pacing slot while the day has budget left, the daily reset when it has none, the end of a LinkedIn lock while one stands.' remaining_today: type: integer minimum: 0 description: daily_limit minus done_today_count of that bucket, after this call. required: - linkedin_account_sid - limit_type - next_call_after - remaining_today required: - success - operation - action - item - result - meta ScrapeLinkedinCompanyEmployeesRequest: type: object description: Request body of `scrape_linkedin_company_employees`. properties: linkedin_account_sid: type: - string - 'null' minLength: 18 maxLength: 18 pattern: ^ln_ac_ description: '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).' idempotency_key: type: - string - 'null' maxLength: 128 description: '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.' company: type: object properties: company_url: type: - string - 'null' maxLength: 512 description: https://www.linkedin.com/company/{slug}/ company_ln_id: type: - string - 'null' maxLength: 64 description: Numeric LinkedIn company id. description: 'Target company: exactly one of company_url / company_ln_id.' page: type: integer minimum: 1 maximum: 100 description: Default 1. required: - company ScrapeLinkedinSearchJobsRequest: type: object description: Request body of `scrape_linkedin_search_jobs`. properties: linkedin_account_sid: type: - string - 'null' minLength: 18 maxLength: 18 pattern: ^ln_ac_ description: '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).' idempotency_key: type: - string - 'null' maxLength: 128 description: '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.' url: type: - string - 'null' maxLength: 2048 pattern: ^https:\/\/www\.linkedin\.com\/jobs\/search-results\/ description: 'EXCLUSIVE 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.' filters: type: - object - 'null' properties: keywords: type: string maxLength: 256 description: 'REQUIRED: this engine has no wire form without a query, so a filter object carrying only geo_id or only a company is refused rather than run wide.' geo_id: type: - string - 'null' pattern: ^\d+$ description: 'ONE numeric LinkedIn geo id, from scrape_linkedin_param_id_lookup(type: "job_location"). Resolve it with THAT type: the ids type "location" answers feed the people search''s locations member and are a different family, so the two must not be crossed. One location per search; a second one, or a radius around it, needs the url half of this tool.' time_posted: type: - string - 'null' enum: - past_24h - past_week - past_month description: Keep only postings listed inside this window; the node maps it onto LinkedIn's own recency value. company_ids: type: - array - 'null' items: type: string pattern: ^\d+$ maxItems: 10 description: 'LinkedIn''s own numeric company ids, hiring company. scrape_linkedin_param_id_lookup(type: "company") answers in what looks like the same numeric org-id space, which is our reading of the wire rather than a mapping verified on this engine. A vanity slug is refused here, because the node would accept it and quietly match nothing.' easy_apply: type: - boolean - 'null' description: 'true keeps only postings with LinkedIn''s one-click Easy Apply. false and omitted are the same thing: the filter is simply not sent.' remote: type: - boolean - 'null' description: 'true keeps only REMOTE postings. Hybrid and on-site cannot be asked for: LinkedIn encodes all three as attributes of one workplace facet and only the remote attribute id is known from real traffic, so the other two would be a guess. Rows still come back labelled with workplace_type, so you can read the whole axis; to FILTER on hybrid or on-site, set it in the LinkedIn UI and paste the URL into this tool''s url field.' experience_level: type: - array - 'null' items: type: string enum: - entry - senior - manager - director - executive maxItems: 5 description: 'Seniority the posting asks for, in LinkedIn''s own dropdown order. Send these readable names: the node maps each onto LinkedIn''s opaque attribute id itself, and a numeric id is refused.' employment_type: type: - array - 'null' items: type: string enum: - part_time - contract - internship - full_time - volunteer maxItems: 5 description: Engagement type, in LinkedIn's own dropdown order. Readable names only, mapped node-side like experience_level. early_applicant: type: - boolean - 'null' description: 'true keeps postings LinkedIn tags "Be an early applicant" (few applications so far). UNPROVEN: the wire parameter behind this one toggle was reasoned out by symmetry with easy_apply and in_your_network, which were both read off captured traffic, and no live run has confirmed it yet. If a page comes back looking unfiltered on this axis, that is the likely cause.' in_your_network: type: - boolean - 'null' description: true keeps only postings at companies where someone in your network works. required: - keywords description: '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.' page: type: integer minimum: 1 maximum: 100 description: '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.' ScrapeLinkedinSimilarProfilesResponse: type: object properties: success: type: boolean enum: - true operation: type: string enum: - action action: type: string description: kebab-case verb; matches the route segment. item: {} result: type: object properties: rows: type: array items: type: object properties: ln_member_id: type: - string - 'null' description: Canonical member id (decoded in-process). ln_id: type: - string - 'null' sn_id: type: - string - 'null' nickname: type: - string - 'null' full_name: type: - string - 'null' headline: type: - string - 'null' description: The card's headline line on the regular and Recruiter engines; null on Sales Navigator people search. position: type: - string - 'null' description: Recruiter search only; the regular card prints no structured position (read the headline). company_name: type: - string - 'null' description: Recruiter search only; the regular card prints no structured company (read the headline). location: type: - string - 'null' description: The card's location line on the regular and Recruiter engines; null on Sales Navigator people search. avatar_url: type: - string - 'null' description: Best effort on the regular engines (a DOM lookup, null when the card image had not rendered). connection_degree: type: - string - 'null' description: 'The degree badge as the card prints it: 1st, 2nd, 3rd or 3rd+ on the regular engines; Recruiter spells 1st, 2nd, 3rd, out_of_network.' required: - ln_member_id - ln_id - sn_id - nickname - full_name - headline - position - company_name - location - avatar_url - connection_degree description: The transient result list, in LinkedIn’s order; NOTHING persisted. paging: {} data_request: type: object properties: {} description: 'The kind="scrape" DataRequest journal row for this call (terminal completed), embedded as result.data_request; served_from_cache always false. A receipt: its result_ref is null here, the page is result.rows next to it; get_data_request by sid reads the stored payload back. Full DataRequestDomain shape owned by ./data_requests.md, so it is left passthrough here.' required: - rows - paging - data_request meta: type: object properties: trace_id: type: string description: UUID v7; same 128-bit value as the X-Trace-Id header. span_id: type: string pattern: ^[0-9a-f]{16}$ description: 16 hex chars, root span of this request. timestamp: type: string description: ISO 8601 UTC (Y-m-dTH:i:sZ), response time. duration_ms: type: integer minimum: 0 description: Server-side wall clock. team_sid: type: - string - 'null' description: The team this call ran in (the token team, or the team_sid override). Null when unauthenticated; absent from pre-2026-08-20 backends. actor_type: type: - string - 'null' description: user | agent | api_key | system. Null when unauthenticated; absent from pre-2026-08-20 backends. required: - trace_id - span_id - timestamp - duration_ms pacing: type: object properties: linkedin_account_sid: type: string description: The account whose bucket the call spent. limit_type: type: string description: The smart-limit bucket the call spent, e.g. send_messages. next_call_after: type: string description: 'ISO 8601, the same form as retry_after: the moment this same call goes through again without waiting. The next pacing slot while the day has budget left, the daily reset when it has none, the end of a LinkedIn lock while one stands.' remaining_today: type: integer minimum: 0 description: daily_limit minus done_today_count of that bucket, after this call. required: - linkedin_account_sid - limit_type - next_call_after - remaining_today required: - success - operation - action - item - result - meta ScrapeLinkedinGetPostCommentsRequest: type: object description: Request body of `scrape_linkedin_get_post_comments`. properties: linkedin_account_sid: type: - string - 'null' minLength: 18 maxLength: 18 pattern: ^ln_ac_ description: '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).' idempotency_key: type: - string - 'null' maxLength: 128 description: '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.' post: type: string maxLength: 512 description: 'The post, named any way you have it. LinkedIn files a post''s comments under its social thread (the ugcPost or groupPost urn for those posts, the activity urn for a share post) and a wrong key fails quietly: the activity urn of a ugcPost post reads an EMPTY list newest-first and a first page with total 0 and no next cursor by relevance (live 2026-09-16), a share urn reads empty. So a `urn:li:ugcPost:<id>` or `urn:li:groupPost:<group>-<post>`, or a link carrying one (a `-ugcPost-<id>-` share link), is the thread and reads in one wire page; an `urn:li:activity:<id>` (bare, in a feed permalink or in a `/posts/<slug>-activity-<id>-<hash>` share link) or a `urn:li:share:<id>` costs one post read first (cached 7 days, so only the first call on a post pays it). result.post.post_ln_id is the urn the list was read by: page on with it to skip the read. A linkedin.com link with no id in it (a bare slug URL) adds one more page load to find the urn. An `lnkd.in` shortlink (the form Copy link to post mints) is expanded server-side and its slug id lifted locally, one wire page like any id-carrying URL; one that expands to a non-LinkedIn target is refused 422 post_not_resolvable, and so is a post the read returns null for (deleted, or not visible to the executing account). The post does NOT need to be tracked or owned by you.' page_size: type: integer minimum: 1 maximum: 100 description: Default 50; one wire page per call. sort_order: type: string enum: - RELEVANCE - CHRONOLOGICAL - REVERSE_CHRONOLOGICAL description: Order of the comment list. RELEVANCE is LinkedIn's own default and what you get when this is omitted; REVERSE_CHRONOLOGICAL is "Most recent" in the UI, CHRONOLOGICAL walks oldest-first. Polling a watched post for new comments wants REVERSE_CHRONOLOGICAL, because under relevance a new comment is not guaranteed to be on page 1. A cursor belongs to the order it was issued under - do not resume a walk with a different sort_order. cursor: type: - string - 'null' description: Opaque resume token from paging.next_cursor; null = first page. required: - post responses: McpServerError: description: MCP error envelope with `error.code` internal_error or service_unavailable. content: application/json: schema: $ref: '#/components/schemas/McpError' McpClientError: description: MCP error envelope. `error.code` is one of validation_failed, nothing_to_update, not_found, relation_not_found, invalid_transition, limit_exceeded, payment_required, duplicate_rejected, conflict, delete_blocked, unauthorized, forbidden, rate_limited, not_implemented. content: application/json: schema: $ref: '#/components/schemas/McpError' securitySchemes: BearerJwt: type: http scheme: bearer bearerFormat: JWT description: 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. TeamSid: type: apiKey in: header name: Team-SID description: Team scope for tokens that do not carry one. Ignored when the token already names a team. x-refined-from: - gtm-api-openapi-openapi.yml - gtm-api-linkedin-openapi.yaml