{"openapi":"3.0.0","info":{"title":"ConnectSafely LinkedIn API","version":"1.0.0","description":"API endpoints for LinkedIn automation and integration.\n\n## Per-minute calling limits\n\nSeparate from, and additional to, the daily/weekly quotas documented on each\nendpoint. Per LinkedIn account, per minute:\n\n| Endpoint group | Calls / minute |\n| --- | --- |\n| Search (`/search/*`, `/posts/search`) | 30 |\n| Profile & relationship (`/profile`, `/relationship/*`) | 30 |\n| Content reads (`/posts/*`, `/analytics/*`, `/groups/*`) | 30 |\n| Writes (`/connect`, `/messaging/send`, `/posts/comment`, `/posts/react`) | 15 |\n| Account (`/account/*`) | 10 |\n| Inbox reads (`/conversations/*`, `/messaging/recent-messages`) | unlimited |\n\nExceeding a limit returns `429` with `Retry-After` (seconds), plus\n`X-RateLimit-Limit`, `X-RateLimit-Remaining` and `X-RateLimit-Reset`.\n\n**These headers report the per-minute limit, not the daily quota.** An endpoint\ncan return 429 from either; check the error message to tell them apart."},"servers":[{"url":"/linkedin","description":"LinkedIn API Base"}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}},"schemas":{"Error":{"type":"object","properties":{"error":{"type":"string"}}},"RateLimitError":{"type":"object","properties":{"error":{"type":"string"},"success":{"type":"boolean"}}},"AccountStatus":{"type":"object","properties":{"id":{"type":"string","description":"Unique account identifier"},"firstName":{"type":"string","description":"LinkedIn user first name"},"lastName":{"type":"string","description":"LinkedIn user last name"},"publicId":{"type":"string","description":"LinkedIn public profile ID (e.g., \"john-doe-123\")"},"platform":{"type":"string","description":"Platform name (e.g., \"ConnectSafely\")"},"status":{"type":"string","enum":["AVAILABLE","IN_USE","ERROR","WARMUP","UNKNOWN"],"description":"Internal account operational status (AVAILABLE = available, IN_USE = busy, ERROR = needs attention)"},"enabled":{"type":"boolean","description":"Whether the account is enabled for automation"},"lastUsed":{"type":"string","format":"date-time","description":"Last activity timestamp"},"hasTokens":{"type":"boolean","description":"Whether valid LinkedIn session tokens exist"},"linkedinPlan":{"type":"object","nullable":true,"description":"LinkedIn premium plan info (only returned for accounts with AVAILABLE or WARMUP status, cached for 12 hours)","properties":{"premiumType":{"type":"string","enum":["SALES_NAVIGATOR","RECRUITER","BUSINESS_PREMIUM","NON_PREMIUM"],"description":"Type of LinkedIn premium subscription"},"isPremium":{"type":"boolean","description":"Whether the account has any premium subscription"},"hasSalesNavigator":{"type":"boolean","description":"Has Sales Navigator access"},"hasRecruiter":{"type":"boolean","description":"Has Recruiter access"},"hasAwayMessages":{"type":"boolean","description":"Has away messages (Business Premium feature)"},"hasAdvertiseBadge":{"type":"boolean","description":"Has advertise badge access"},"hasHiringManager":{"type":"boolean","description":"Has hiring manager mailbox access"}}}},"example":{"id":"696ce9e780e0483585e4e553","firstName":"John","lastName":"Doe","publicId":"john-doe-123","platform":"ConnectSafely","status":"AVAILABLE","enabled":true,"lastUsed":"2026-02-24T04:53:13.750Z","hasTokens":true,"linkedinPlan":{"premiumType":"NON_PREMIUM","isPremium":false,"hasSalesNavigator":false,"hasRecruiter":false,"hasAwayMessages":false,"hasAdvertiseBadge":false,"hasHiringManager":false}}},"PremiumStatusResponse":{"type":"object","properties":{"success":{"type":"boolean","description":"Whether the request was successful"},"accountId":{"type":"string","description":"Unique account identifier"},"accountName":{"type":"string","description":"Full name of the LinkedIn account owner"},"premiumType":{"type":"string","enum":["SALES_NAVIGATOR","RECRUITER","BUSINESS_PREMIUM","NON_PREMIUM"],"description":"Type of premium subscription"},"isPremium":{"type":"boolean","description":"Whether the account has any premium subscription"},"hasSalesNavigator":{"type":"boolean","description":"Whether the account has Sales Navigator access"},"hasRecruiter":{"type":"boolean","description":"Whether the account has Recruiter access"},"hasAwayMessages":{"type":"boolean","description":"Whether the account can set away messages (Business Premium feature)"},"hasAdvertiseBadge":{"type":"boolean","description":"Whether the account has advertise badge access"},"hasHiringManager":{"type":"boolean","description":"Whether the account has hiring manager mailbox access"}},"example":{"success":true,"accountId":"696ce9e780e0483585e4e553","accountName":"John Doe","premiumType":"SALES_NAVIGATOR","isPremium":true,"hasSalesNavigator":true,"hasRecruiter":false,"hasAwayMessages":false,"hasAdvertiseBadge":false,"hasHiringManager":false}},"CreatorDashboardMetric":{"type":"object","description":"One \"Track performance\" card. Values are null when LinkedIn rendered no number.","properties":{"value":{"type":"integer","nullable":true,"description":"The headline number"},"label":{"type":"string","nullable":true,"description":"Label as shown, including the period, e.g. \"Post impressions in 7 days\""},"changePercent":{"type":"integer","nullable":true,"description":"Period-over-period change, e.g. 9 for \"9%\""},"changeDirection":{"type":"string","nullable":true,"enum":["up","down","flat"],"description":"Direction of that change"},"comparison":{"type":"string","nullable":true,"description":"What the change compares against, e.g. \"vs. prior 7 days\""}}},"Post":{"type":"object","properties":{"postUrl":{"type":"string"},"authorName":{"type":"string"},"authorProfileUrl":{"type":"string"},"content":{"type":"string"},"timestamp":{"type":"string","description":"Relative time string (e.g., \"3mo\", \"2w\", \"1d\")","example":"3mo"},"likes":{"type":"number"},"comments":{"type":"number"},"shares":{"type":"number"},"isPeopleUpdate":{"type":"boolean","description":"True if this is a people update post (job change, promotion, work anniversary, etc.)"},"celebrationType":{"type":"string","description":"Type of celebration/update (e.g., \"Starting a new position\", \"Got promoted\", \"Work anniversary\")","example":"Starting a new position"},"isArticle":{"type":"boolean","description":"True if this is a collaborative article (pulse URL)"},"articleUrl":{"type":"string","description":"Article URL for collaborative articles","example":"https://www.linkedin.com/pulse/article-slug/"},"canComment":{"type":"boolean","description":"False if commenting is disabled (e.g., for collaborative articles)","default":true}}},"Comment":{"type":"object","properties":{"commentId":{"type":"string"},"authorName":{"type":"string"},"authorProfileUrl":{"type":"string"},"content":{"type":"string"},"timestamp":{"type":"string","description":"Relative time string (e.g., \"3mo\", \"2w\", \"1d\") or ISO date-time","example":"2w"},"likes":{"type":"number"}}},"Profile":{"type":"object","properties":{"profileId":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"headline":{"type":"string"},"location":{"type":"string"},"profileUrl":{"type":"string"},"connections":{"type":"number"}}},"Organization":{"type":"object","properties":{"entityUrn":{"type":"string","description":"Organization URN (e.g., \"urn:li:fsd_company:105672170\")"},"name":{"type":"string","description":"Company/organization name"},"universalName":{"type":"string","description":"URL slug/universal name (e.g., \"connectsafelyai\")"},"logoUrl":{"type":"string","nullable":true,"description":"Company logo URL (200x200)"},"coverImageUrl":{"type":"string","nullable":true,"description":"Company cover/banner image URL"},"followerCount":{"type":"number","description":"Number of followers"},"visitorsCount":{"type":"number","description":"Number of recent visitors"},"pageType":{"type":"string","description":"Page type (e.g., \"COMPANY\")"},"isFollowing":{"type":"boolean","description":"Whether the authenticated user follows this organization"}},"example":{"entityUrn":"urn:li:fsd_company:105672170","name":"ConnectSafely.AI","universalName":"connectsafelyai","logoUrl":"https://media.licdn.com/dms/image/v2/D560BAQExfwnRu-WH9g/company-logo_200_200/...","coverImageUrl":"https://media.licdn.com/dms/image/v2/D4D3DAQEZladqoLpujg/image-scale_191_1128/...","followerCount":1823,"visitorsCount":500,"pageType":"COMPANY","isFollowing":true}},"CompanyFollower":{"type":"object","description":"A follower of a LinkedIn company page","properties":{"profileUrn":{"type":"string","description":"LinkedIn profile URN (e.g., \"urn:li:fsd_profile:ACoAAB...\")"},"publicIdentifier":{"type":"string","description":"LinkedIn public identifier / vanity URL (e.g., \"john-doe-123\")"},"firstName":{"type":"string","description":"Follower first name"},"lastName":{"type":"string","description":"Follower last name"},"headline":{"type":"string","nullable":true,"description":"Professional headline"},"profilePictureUrl":{"type":"string","nullable":true,"description":"URL to profile picture"},"connectionDegree":{"type":"string","nullable":true,"description":"Connection degree (e.g., \"DISTANCE_2\", \"DISTANCE_3\")"},"followDate":{"type":"string","nullable":true,"description":"Human-readable follow date (e.g., \"February 2026\")"}},"example":{"profileUrn":"urn:li:fsd_profile:ACoAABJefVoBrz2LR3f...","publicIdentifier":"john-doe-123","firstName":"John","lastName":"Doe","headline":"Software Engineer at Tech Company","profilePictureUrl":"https://media.licdn.com/dms/image/v2/...","connectionDegree":"DISTANCE_2","followDate":"February 2026"}},"CompanyFollowInvitationResult":{"type":"object","description":"Result of a single company follow invitation","properties":{"profileUrn":{"type":"string","description":"LinkedIn profile URN that was invited"},"success":{"type":"boolean","description":"Whether the invitation was sent successfully"},"status":{"type":"string","nullable":true,"description":"Status of the invitation (e.g., \"SENT\", \"FAILED\")"},"error":{"type":"string","nullable":true,"description":"Error message if the invitation failed"}},"example":{"profileUrn":"urn:li:fsd_profile:ACoAABJefVoBrz2LR3f...","success":true,"status":"SENT"}},"JobSearchResult":{"type":"object","description":"Job posting from LinkedIn search results","properties":{"jobId":{"type":"string","description":"Unique LinkedIn job posting ID"},"title":{"type":"string","description":"Job title"},"companyName":{"type":"string","description":"Name of the hiring company"},"companyLogo":{"type":"string","description":"Company logo URL"},"location":{"type":"string","description":"Job location, without the work-type suffix (e.g., \"Bengaluru, Karnataka, India\")"},"isRemote":{"type":"boolean","description":"Whether the position is fully remote"},"isHybrid":{"type":"boolean","description":"Whether the position is hybrid (partial remote)"},"postedDate":{"type":"string","description":"Relative posting age as LinkedIn renders it (e.g., \"Posted 4 days ago\")"},"salary":{"type":"string","description":"Pay-rate badge when the posting shows one (e.g., \"30K INR/month - 45K INR/month\")"},"jobUrl":{"type":"string","description":"Direct URL to the job posting"},"easyApply":{"type":"boolean","description":"Whether LinkedIn Easy Apply is available"}},"example":{"jobId":"4367156030","title":"Founding Software Engineer - AI and Backend","companyName":"Dexicon","companyLogo":"https://media.licdn.com/dms/image/v2/D560BAQ.../company-logo_100_100/...","location":"Bengaluru, Karnataka, India","isRemote":true,"isHybrid":false,"postedDate":"Posted 4 days ago","jobUrl":"https://www.linkedin.com/jobs/view/4367156030/","easyApply":true}},"CompanySearchResult":{"type":"object","description":"Company from LinkedIn search results","properties":{"companyId":{"type":"string","description":"Unique LinkedIn company ID"},"name":{"type":"string","description":"Company name"},"universalName":{"type":"string","description":"Company URL slug (e.g., \"google\")"},"headline":{"type":"string","description":"Company headline with industry and location"},"description":{"type":"string","description":"Brief company description"},"logoUrl":{"type":"string","description":"URL to company logo image"},"industry":{"type":"string","description":"Primary industry classification"},"followerCount":{"type":"number","description":"Number of LinkedIn followers"},"companyUrl":{"type":"string","description":"Direct URL to company page"},"location":{"type":"string","description":"Company headquarters location"}},"example":{"companyId":"1441","name":"Google","universalName":"google","headline":"Software Development • Mountain View, CA","description":"A problem isn't truly solved until it's solved for all.","logoUrl":"https://media.licdn.com/dms/image/.../google_logo","industry":"Software Development","followerCount":41000000,"companyUrl":"https://www.linkedin.com/company/google/","location":"Mountain View, CA"}},"GroupSearchResult":{"type":"object","description":"Group from LinkedIn search results","properties":{"groupId":{"type":"string","description":"Unique LinkedIn group ID"},"name":{"type":"string","description":"Group name"},"memberCount":{"type":"number","description":"Members in the group. LinkedIn rounds this on the search screen — anything past ~999 comes back as the displayed approximation (`1K members` → `1000`, `89K members` → `89000`). Counts below 1000 are exact. Fetch the group with `get-group-details` for a precise figure."},"logoUrl":{"type":"string","description":"URL to group logo/image"},"groupUrl":{"type":"string","description":"Direct URL to group page"},"isPrivate":{"type":"boolean","description":"Whether the group requires approval to join"}},"example":{"groupId":"6732842","name":"Startup Founders, CEOs & Investors – Tech Innovation Network | AI, Data, FinTech & SaaS","memberCount":89000,"logoUrl":"https://media.licdn.com/dms/image/.../group-logo","groupUrl":"https://www.linkedin.com/groups/6732842/","isPrivate":false}},"GroupSearchPagination":{"type":"object","description":"Pagination for group search.\n\n`total` is LinkedIn's own result count for the query, and is **optional** — most queries report one, but some render none and a zero-result query never does. Treat it as approximate for large result sets: LinkedIn displays exact counts for small ones (\"580 results\") and rounded ones for big ones (\"About 90,000 results\"), and both arrive here as a plain number.\n\n`hasMore` is always present and is the field to page on.","properties":{"count":{"type":"number","description":"Number of results returned in this response"},"start":{"type":"number","description":"Starting offset of results"},"total":{"type":"number","description":"LinkedIn's reported result count for this query. Omitted when LinkedIn renders no count. Approximate for large result sets."}},"required":["count","start"],"example":{"count":10,"start":0,"total":580}},"PeopleSearchResult":{"type":"object","description":"Person from LinkedIn search results","properties":{"profileId":{"type":"string","description":"LinkedIn profile vanity URL slug (the part after linkedin.com/in/)"},"profileUrn":{"type":"string","description":"LinkedIn profile URN (e.g., \"urn:li:fsd_profile:ACoAABJefVoBrz2LR3f...\")"},"firstName":{"type":"string","description":"First name"},"lastName":{"type":"string","description":"Last name"},"headline":{"type":"string","description":"Profile headline (usually job title and company)"},"profilePicture":{"type":"string","description":"URL to profile picture"},"location":{"type":"string","description":"Location (city, region, or country)"},"connectionDegree":{"type":"string","description":"Connection degree: \"1st\", \"2nd\", \"3rd+\""},"currentPosition":{"type":"string","description":"Current job position text"},"profileUrl":{"type":"string","description":"Direct URL to profile"},"isPremium":{"type":"boolean","description":"Whether the user has LinkedIn Premium"},"isOpenToWork":{"type":"boolean","description":"Whether the user has Open to Work status"}},"example":{"profileId":"johndoe","profileUrn":"urn:li:fsd_profile:ACoAABdttMcBYTOrkGpTYrhL6waE6Tu8UhpZdeo","firstName":"John","lastName":"Doe","headline":"Software Engineer @ Google","profilePicture":"https://media.licdn.com/dms/image/.../profile-displayphoto","location":"San Francisco Bay Area","connectionDegree":"2nd","currentPosition":"Software Engineer @ Google","profileUrl":"https://www.linkedin.com/in/johndoe/","isPremium":false,"isOpenToWork":false}},"GeoLocation":{"type":"object","description":"Geographic location from LinkedIn for use in search filters","properties":{"name":{"type":"string","description":"Location name (city, region, or country)"},"geoId":{"type":"string","description":"LinkedIn geo ID for use in search filters"},"countryCode":{"type":"string","description":"ISO country code (e.g., \"US\", \"GB\")"}},"example":{"name":"San Francisco Bay Area","geoId":"90000084","countryCode":"US"}},"PeopleSearchPagination":{"type":"object","description":"Pagination for people search.\n\n`total` is **absent for a normal keyword search**: LinkedIn reports no result count for it, so any total would be invented. Use `hasMore` instead — it is exact, derived by fetching one row past the requested `count`.\n\n`total` **is** returned when searching by a Sales Navigator `url`, because Sales Navigator reports a real result count. Treat it as optional.","properties":{"count":{"type":"number","description":"Number of results returned in this response"},"start":{"type":"number","description":"Starting offset of results"},"total":{"type":"number","description":"Total matching results. Only present for Sales Navigator URL searches — omitted for normal keyword searches, where LinkedIn provides no count."}},"required":["count","start"],"example":{"count":10,"start":0}},"CompanySearchPagination":{"type":"object","description":"Pagination for company search.\n\n`total` is **absent for a normal keyword search**: LinkedIn reports no result count for it, so any total would be invented. Use `hasMore` instead — it is derived by fetching one row past the requested `count`.\n\n`total` **is** returned when searching by a Sales Navigator `url`, because Sales Navigator reports a real result count. Treat it as optional.","properties":{"count":{"type":"number","description":"Number of results returned in this response"},"start":{"type":"number","description":"Starting offset of results"},"total":{"type":"number","description":"Total matching results. Only present for Sales Navigator URL searches — omitted for normal keyword searches, where LinkedIn provides no count."}},"required":["count","start"],"example":{"count":25,"start":0}},"JobSearchPagination":{"type":"object","description":"Pagination for job search.\n\n`total` is **never returned**: LinkedIn reports no result count for job search, so any total would be invented. Unlike people and company search there is no Sales Navigator variant that reports one. Use `hasMore` instead.","properties":{"count":{"type":"number","description":"Number of results returned in this response"},"start":{"type":"number","description":"Starting offset of results"}},"required":["count","start"],"example":{"count":25,"start":0}},"SearchPagination":{"type":"object","description":"Pagination information for search results","properties":{"count":{"type":"number","description":"Number of results returned in this response"},"start":{"type":"number","description":"Starting offset of results"},"total":{"type":"number","description":"Total number of matching results available"}},"example":{"count":25,"start":0,"total":1000}},"ValidationError":{"type":"object","description":"Validation error response when request body is invalid","properties":{"success":{"type":"boolean","example":false},"error":{"type":"object","properties":{"issues":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string","description":"Error code (e.g., \"invalid_type\", \"too_small\")"},"expected":{"type":"string","description":"Expected value type"},"received":{"type":"string","description":"Received value type"},"path":{"type":"array","items":{"type":"string"},"description":"Path to the invalid field"},"message":{"type":"string","description":"Human-readable error message"}}}},"name":{"type":"string","example":"ZodError"}}}},"example":{"success":false,"error":{"issues":[{"code":"invalid_type","expected":"string","received":"undefined","path":["keywords"],"message":"Required"}],"name":"ZodError"}}}}},"security":[{"bearerAuth":[]}],"paths":{"/account/status":{"get":{"operationId":"get-account-status","tags":["Account"],"summary":"Get account status","description":"Retrieve the current status of a LinkedIn account. If accountId is provided, returns that specific account. Otherwise returns the most recently used account for the authenticated user.","parameters":[{"name":"accountId","in":"query","required":false,"schema":{"type":"string"},"description":"Optional LinkedIn account ID. If omitted, returns the most recently used account."}],"responses":{"200":{"description":"Account status retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccountStatus"}}}},"401":{"description":"Unauthorized - Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unauthorized - Invalid credentials"}}}},"404":{"description":"No LinkedIn account found for this user","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/account/{accountId}/status":{"get":{"operationId":"get-account-status-by-id","tags":["Account"],"summary":"Get specific account status","description":"Retrieve the current status of a specific LinkedIn account by ID. Returns account details including name, public ID, operational status, session validity, and LinkedIn plan info (premium type). Useful for multi-account setups to check individual account status.","parameters":[{"name":"accountId","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-fA-F0-9]{24}$"},"description":"Unique identifier for the LinkedIn account (24-character hex string)","example":"696ce9e780e0483585e4e553"}],"responses":{"200":{"description":"Account status retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccountStatus"}}}},"400":{"description":"Invalid account ID format","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Invalid account ID format"}}}},"401":{"description":"Unauthorized - Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unauthorized - Invalid credentials"}}}},"404":{"description":"Account not found or not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"LinkedIn account not found or not accessible"}}}}}}},"/account/{accountId}/premium":{"get":{"operationId":"get-account-premium-status","tags":["Account"],"summary":"Get account premium status","description":"Retrieve the premium subscription status of a LinkedIn account. Checks Sales Navigator license first via Identity API, then falls back to feature access API for other premium types (Recruiter, Business Premium). Returns premium type and access flags.","parameters":[{"name":"accountId","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-fA-F0-9]{24}$"},"description":"Unique identifier for the LinkedIn account (24-character hex string)","example":"696ce9e780e0483585e4e553"}],"responses":{"200":{"description":"Premium status retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PremiumStatusResponse"}}}},"400":{"description":"Invalid account ID format","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Invalid account ID format","success":false}}}},"401":{"description":"Unauthorized - Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unauthorized"}}}},"404":{"description":"Account not found or not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"LinkedIn account not found or not accessible","success":false}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Failed to fetch premium status","success":false}}}}}}},"/account/{accountId}/activity":{"get":{"operationId":"get-account-activity","tags":["Account"],"summary":"Get account activity history","description":"Retrieve activity history for the last 15 days including comments and reactions. Returns summary statistics, daily breakdown, and individual activity records. Useful for tracking account behavior, auditing actions, and monitoring engagement patterns.","parameters":[{"name":"accountId","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-fA-F0-9]{24}$"},"description":"Unique identifier for the LinkedIn account (24-character hex string)","example":"696ce9e780e0483585e4e553"}],"responses":{"200":{"description":"Activity history retrieved successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"accountId":{"type":"string"},"accountName":{"type":"string"},"dateRange":{"type":"object","properties":{"start":{"type":"string","format":"date-time"},"end":{"type":"string","format":"date-time"},"days":{"type":"number"}}},"summary":{"type":"object","properties":{"totalComments":{"type":"number"},"totalReactions":{"type":"number"},"totalActivities":{"type":"number"},"totalCommentsFetched":{"type":"number"},"totalReactionsFetched":{"type":"number"},"reachedCommentLimit":{"type":"boolean"},"reachedReactionLimit":{"type":"boolean"}}},"dailyBreakdown":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string","example":"2026-02-24"},"comments":{"type":"number"},"reactions":{"type":"number"},"total":{"type":"number"}}}},"comments":{"type":"array","items":{"type":"object","properties":{"activityUrn":{"type":"string"},"date":{"type":"string"},"isoDate":{"type":"string","format":"date-time"},"timestamp":{"type":"number"},"type":{"type":"string","enum":["comment"]}}}},"reactions":{"type":"array","items":{"type":"object","properties":{"activityUrn":{"type":"string"},"date":{"type":"string"},"isoDate":{"type":"string","format":"date-time"},"timestamp":{"type":"number"},"type":{"type":"string","enum":["reaction"]}}}}}}}}},"400":{"description":"Invalid account ID format","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Invalid account ID format","success":false}}}},"401":{"description":"Unauthorized - Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unauthorized"}}}},"404":{"description":"Account not found or not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"LinkedIn account not found or not accessible","success":false}}}}}}},"/account/{accountId}/comments":{"get":{"operationId":"get-account-comments","tags":["Analytics"],"summary":"List an account's own comments","description":"Return a single page of the LinkedIn account's own comment history (the comments this account left on posts), newest first. Lightweight — one LinkedIn call per request. To page, pass the returned `paginationToken` on the next request; keep going until `paginationToken` is null.","parameters":[{"name":"accountId","in":"path","required":true,"description":"LinkedIn account ID (24-character hex string)","schema":{"type":"string"},"example":"6971843ca43920b1889fa28f"},{"name":"count","in":"query","required":false,"description":"Number of comments to return per page","schema":{"type":"number","minimum":1,"maximum":100,"default":20}},{"name":"start","in":"query","required":false,"description":"Pagination offset (0-indexed). Ignored when paginationToken is provided.","schema":{"type":"number","minimum":0,"default":0}},{"name":"paginationToken","in":"query","required":false,"description":"Token from a previous response to fetch the next page","schema":{"type":"string"}}],"responses":{"200":{"description":"Comment history page retrieved successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"accountId":{"type":"string","description":"LinkedIn account ID used"},"profileId":{"type":"string","description":"LinkedIn profile URN"},"count":{"type":"number","description":"Number of comments returned in this page"},"paginationToken":{"type":"string","nullable":true,"description":"Token for the next page; null when there are no more pages"},"comments":{"type":"array","items":{"type":"object","properties":{"activityUrn":{"type":"string","description":"Internal update/activity URN for this comment item"},"postActivityUrn":{"type":"string","nullable":true,"description":"Numeric activity id of the post that was commented on"},"commentUrn":{"type":"string","nullable":true,"description":"URN of the account's comment"},"commentText":{"type":"string","nullable":true,"description":"Text of the comment the account left (null if not exposed)"},"postUrl":{"type":"string","nullable":true,"description":"Permalink of the post that was commented on (null if not exposed)"},"timestamp":{"type":"number","nullable":true,"description":"Comment time (epoch ms)"},"isoDate":{"type":"string","nullable":true,"description":"Comment time (ISO 8601)"},"type":{"type":"string","enum":["comment"]}}}}}},"example":{"success":true,"accountId":"6971843ca43920b1889fa28f","profileId":"ACoAAFntzT4BELbT50_fDUyqwwNPXPXu-8P3HWc","count":1,"paginationToken":"dXJuOmxpOmFjdGl2aXR5Ojc0NzQ5NzY5OTU3...","comments":[{"activityUrn":"7475780581708955648","postActivityUrn":"7475769998100099073","commentUrn":"urn:li:fsd_comment:(7475777250785914880,urn:li:activity:7475769998100099073)","commentText":"This is awesome to see 😀","postUrl":"https://www.linkedin.com/posts/karan-kumar-62b763103_linkedingrowth-inboundleads-saas-activity-7475769998100099073-AHix","timestamp":1782308004423,"isoDate":"2026-06-24T13:33:24.423Z","type":"comment"}]}}}}}}},"/account/{accountId}/reactions":{"get":{"operationId":"get-account-reactions","tags":["Analytics"],"summary":"List an account's own reactions","description":"Return a single page of the LinkedIn account's own reaction history (the posts this account reacted to), newest first. Lightweight — one LinkedIn call per request. To page, pass the returned `paginationToken` on the next request; keep going until `paginationToken` is null.","parameters":[{"name":"accountId","in":"path","required":true,"description":"LinkedIn account ID (24-character hex string)","schema":{"type":"string"},"example":"6971843ca43920b1889fa28f"},{"name":"count","in":"query","required":false,"description":"Number of reactions to return per page","schema":{"type":"number","minimum":1,"maximum":100,"default":20}},{"name":"start","in":"query","required":false,"description":"Pagination offset (0-indexed). Ignored when paginationToken is provided.","schema":{"type":"number","minimum":0,"default":0}},{"name":"paginationToken","in":"query","required":false,"description":"Token from a previous response to fetch the next page","schema":{"type":"string"}}],"responses":{"200":{"description":"Reaction history page retrieved successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"accountId":{"type":"string","description":"LinkedIn account ID used"},"profileId":{"type":"string","description":"LinkedIn profile URN"},"count":{"type":"number","description":"Number of reactions returned in this page"},"paginationToken":{"type":"string","nullable":true,"description":"Token for the next page; null when there are no more pages"},"reactions":{"type":"array","items":{"type":"object","properties":{"activityUrn":{"type":"string","description":"Activity URN of the reacted-to post"},"postUrl":{"type":"string","nullable":true,"description":"Permalink of the post that was reacted to (tracking params stripped)"},"reactionType":{"type":"string","nullable":true,"enum":["LIKE","PRAISE","APPRECIATION","EMPATHY","INTEREST","ENTERTAINMENT"],"description":"Reaction the account applied — LIKE (👍), PRAISE (👏 Celebrate), APPRECIATION (❤️ Love), EMPATHY (🤝 Support), INTEREST (💡 Insightful), ENTERTAINMENT (😄 Funny). Null if the type could not be determined."},"timestamp":{"type":"number","nullable":true,"description":"Reaction time (epoch ms)"},"isoDate":{"type":"string","nullable":true,"description":"Reaction time (ISO 8601)"},"type":{"type":"string","enum":["reaction"]}}}}}},"example":{"success":true,"accountId":"6971843ca43920b1889fa28f","profileId":"ACoAAFntzT4BELbT50_fDUyqwwNPXPXu-8P3HWc","count":1,"paginationToken":"dXJuOmxpOmFjdGl2aXR5Ojc0NzU1MzkxNzU0...","reactions":[{"activityUrn":"7475545578450419712","postUrl":"https://www.linkedin.com/posts/janedoe_growth-activity-7475545578450419712-abcd","reactionType":"LIKE","timestamp":1782308954823,"isoDate":"2026-06-24T13:49:14.823Z","type":"reaction"}]}}}}}}},"/account/{accountId}/quota":{"get":{"operationId":"get-account-quota","tags":["Account"],"summary":"Quota usage for one account","description":"How much of each rate-limited action this account has spent in the current window. `used` counts every call made through the ConnectSafely API — your own API/MCP calls as well as anything ConnectSafely runs for you — so this is the way to spot an agent burning a quota you did not expect.\n\nA fast, database-only read: it makes no LinkedIn call and consumes none of the quotas it reports, so it is safe to poll.\n\n`limit` is the ceiling ConnectSafely enforces (calls are rejected with 429 at this value); `linkedinLimit` is LinkedIn's own higher ceiling, for context. `window` is `daily` (resets midnight UTC) or `weekly` (resets Monday midnight UTC).","parameters":[{"name":"accountId","in":"path","required":true,"schema":{"type":"string"},"description":"LinkedIn account id (24-character Mongo ObjectId). Must be an account you own or one shared with you through a workspace.","example":"60d21b4667d0d8992e610c85"}],"responses":{"200":{"description":"Current rate-limit usage for the account","content":{"application/json":{"schema":{"type":"object","properties":{"accountId":{"type":"string"},"quotas":{"type":"array","items":{"type":"object","properties":{"feature":{"type":"string","description":"Action name, e.g. PROFILE_VIEW, COMMENT, CONNECT, SEARCH_PEOPLE"},"window":{"type":"string","enum":["daily","weekly"]},"used":{"type":"integer","description":"Calls made on this account in the current window (all sources)"},"limit":{"type":"integer","description":"Ceiling ConnectSafely enforces — 429 at this value"},"linkedinLimit":{"type":"integer","description":"LinkedIn's own higher ceiling, for context only"},"remaining":{"type":"integer","description":"limit minus used, floored at 0"},"limitReached":{"type":"boolean","description":"True when LinkedIn returned 429 for this feature in the current window"},"limitReachedAt":{"type":"string","format":"date-time","nullable":true},"resetAt":{"type":"string","format":"date-time","description":"When this window resets"}}}}}},"example":{"accountId":"60d21b4667d0d8992e610c85","quotas":[{"feature":"PROFILE_VIEW","window":"daily","used":118,"limit":120,"linkedinLimit":150,"remaining":2,"limitReached":false,"limitReachedAt":null,"resetAt":"2026-08-28T00:00:00.000Z"},{"feature":"CONNECT","window":"weekly","used":12,"limit":90,"linkedinLimit":100,"remaining":78,"limitReached":false,"limitReachedAt":null,"resetAt":"2026-09-01T00:00:00.000Z"}]}}}},"401":{"description":"Unauthorized - Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unauthorized"}}}},"403":{"description":"No active API seat for this account. The path accountId is seat-checked by the shared auth middleware before the handler runs, exactly as on every other account-scoped endpoint.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"No API seat allocated for this LinkedIn account.","code":"NO_API_SEAT_FOR_ACCOUNT"}}}},"404":{"description":"Account not found, deleted, or not visible to the caller","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Account not found"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Internal Server Error"}}}}}}},"/accounts":{"get":{"operationId":"list-linkedin-accounts","tags":["Account"],"summary":"List connected LinkedIn accounts (lightweight + searchable)","description":"Returns a lightweight list of the LinkedIn accounts visible to the caller (their own plus any shared via a workspace). This is a fast, **database-only** endpoint — it does NOT call LinkedIn or fetch premium status — so it is the preferred way to populate account pickers and run searches.\n\nEach item includes `apiSeatAllocated`, indicating whether the caller currently holds an active API seat for that account.\n\nUse the optional `q` query parameter to search. `q` matches (case-insensitive) against firstName, lastName and publicId; if `q` is a 24-character account id it matches that account exactly; and if `q` is a LinkedIn profile URL the public_id slug is extracted and matched against publicId.\n\n**Example requests:**\n- `GET /linkedin/accounts` — all visible accounts\n- `GET /linkedin/accounts?q=john` — by first/last name or public id\n- `GET /linkedin/accounts?q=john-doe-123` — by public id\n- `GET /linkedin/accounts?q=60d21b4667d0d8992e610c85` — exact account id\n- `GET /linkedin/accounts?q=https://www.linkedin.com/in/john-doe-123/` — by LinkedIn profile URL","parameters":[{"name":"q","in":"query","required":false,"schema":{"type":"string"},"description":"Optional search term. Matches firstName / lastName / publicId (case-insensitive), an exact 24-hex account id, or a LinkedIn profile URL (the public_id slug is extracted).","examples":{"byName":{"summary":"Search by name","value":"john"},"byPublicId":{"summary":"Search by public id","value":"john-doe-123"},"byAccountId":{"summary":"Exact account id","value":"60d21b4667d0d8992e610c85"},"byLinkedInUrl":{"summary":"LinkedIn profile URL","value":"https://www.linkedin.com/in/john-doe-123/"}}}],"responses":{"200":{"description":"List of accounts visible to the caller (each tagged with API-seat allocation)","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"LinkedIn account id (Mongo ObjectId)"},"firstName":{"type":"string"},"lastName":{"type":"string","nullable":true},"publicId":{"type":"string","nullable":true,"description":"LinkedIn public identifier (the slug after linkedin.com/in/)"},"picture":{"type":"string","nullable":true,"description":"Profile picture URL"},"status":{"type":"string","description":"Account status (e.g., AVAILABLE, WARMUP, ERROR, UNKNOWN)"},"enabled":{"type":"boolean"},"apiSeatAllocated":{"type":"boolean","description":"True when the caller holds an active API seat for this account"}}}},"example":[{"id":"60d21b4667d0d8992e610c85","firstName":"John","lastName":"Doe","publicId":"john-doe-123","picture":"https://media.licdn.com/dms/image/v2/D5603...","status":"AVAILABLE","enabled":true,"apiSeatAllocated":true},{"id":"60d21b4667d0d8992e610c86","firstName":"Jane","lastName":"Smith","publicId":"jane-smith","picture":null,"status":"WARMUP","enabled":true,"apiSeatAllocated":false}]}}},"401":{"description":"Unauthorized - Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unauthorized"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Internal Server Error"}}}}}}},"/follow":{"post":{"operationId":"follow-user","tags":["Actions"],"summary":"Follow or unfollow a LinkedIn profile","description":"Follow or unfollow a LinkedIn member to see their posts in your feed. Following does not require a connection. Provide either profileId (public identifier) or profileUrn (internal URN). Following builds your network visibility without sending connection requests. **Rate limit: 100 actions per day.**","x-rateLimit":{"limit":100,"period":"day","scope":"linkedinAccount","description":"100 follow/unfollow actions per day per LinkedIn account (not per API key)"},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"accountId":{"type":"string","description":"LinkedIn account ID to use. If not provided, uses the default account."},"profileId":{"type":"string","description":"LinkedIn profile vanity URL slug (the part after linkedin.com/in/, e.g., \"john-doe-123\"). Prefer profileUrn when available.","example":"hasam-ud-din"},"profileUrn":{"type":"string","description":"LinkedIn profile URN (e.g., \"urn:li:fsd_profile:ACoAABJefVoBrz2LR3f...\"). Preferred over profileId."},"action":{"type":"string","enum":["follow","unfollow"],"default":"follow","description":"Action to perform: follow or unfollow the profile"}}}}}},"responses":{"200":{"description":"Follow action completed successfully","headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Maximum actions allowed per day"},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Remaining actions for today"},"X-RateLimit-Reset":{"schema":{"type":"string","format":"date-time"},"description":"When the rate limit resets (midnight UTC)"}},"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"action":{"type":"string","enum":["follow","unfollow"]},"profileUrn":{"type":"string","description":"LinkedIn profile URN of the followed/unfollowed user"},"message":{"type":"string"}}},"example":{"success":true,"action":"follow","profileUrn":"urn:li:fsd_profile:ACoAAA24A-MBVEvT49xpVF2gnWrhvmUIPDJshSM","message":"Successfully followed user"}}}},"400":{"description":"Bad request - Missing required fields","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Either profileId or profileUrn must be provided","success":false}}}},"401":{"description":"Unauthorized - Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unauthorized"}}}},"404":{"description":"Profile not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"LinkedIn profile not found: invalid-profile-id","success":false}}}},"429":{"description":"Rate limit exceeded","headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Maximum actions allowed per day"},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Remaining actions (0 when exceeded)"},"X-RateLimit-Reset":{"schema":{"type":"string","format":"date-time"},"description":"When the rate limit resets"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimitError"},"example":{"error":"Rate limit exceeded for FOLLOW: 100/100 used. Resets at 2024-01-16T00:00:00.000Z","success":false}}}}}}},"/message":{"post":{"operationId":"send-message","tags":["Actions"],"summary":"Send a LinkedIn message","deprecated":true,"description":"**DEPRECATED: Use POST /conversations/send instead.** This endpoint still works but returns a `_deprecated` warning in the response body. The /conversations/send endpoint auto-detects Sales Navigator accounts, supports attachments, and handles both standard and Sales Nav messaging transparently. For attachments, upload each file via POST /conversations/upload-attachment (raw binary body; use an image/* content type to render images inline), then pass the returned attachment wrapped as { file: <attachment> }. --- Send a direct message to a LinkedIn member. Supports regular messages (requires 1st-degree connection) and InMail (for non-connections, requires Premium). Can also send messages in group context. **Either recipientProfileId or recipientProfileUrn must be provided.** **Rate limit: 150 messages per day per LinkedIn account** (resets at midnight UTC). **Unlike every other send endpoint, EVERY message counts here — replies included.** This endpoint predates the cold-outreach quota and still counts unconditionally; POST /conversations/send and the /messaging/send* endpoints exempt replies into an existing conversation. That difference alone is a reason to migrate: a reply-heavy integration burns quota on this endpoint that it would not burn on /conversations/send. Over-quota sends return 429 with `X-RateLimit-Limit`, `X-RateLimit-Remaining` and `X-RateLimit-Reset`, and are rejected before any LinkedIn request is made.","x-rateLimit":{"limit":150,"period":"day","scope":"linkedinAccount","description":"150 messages per day per LinkedIn account (not per API key). Counts EVERY send on this deprecated endpoint, replies included — unlike /conversations/send, which counts cold sends only."},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["message"],"properties":{"accountId":{"type":"string","description":"LinkedIn account ID to use. If not provided, uses the default account."},"recipientProfileId":{"type":"string","description":"Recipient LinkedIn profile vanity URL slug (e.g., \"john-doe-123\"). Either this or recipientProfileUrn is required. Prefer recipientProfileUrn when available."},"recipientProfileUrn":{"type":"string","description":"Recipient LinkedIn profile URN (e.g., \"urn:li:fsd_profile:ACoAABJefVoBrz2LR3f...\"). Either this or recipientProfileId is required. Preferred over recipientProfileId."},"message":{"type":"string","description":"Message content to send. Supports basic formatting."},"subject":{"type":"string","description":"Subject line (required for InMail, optional for regular messages)"},"messageType":{"type":"string","enum":["normal","inmail"],"default":"normal","description":"Message type: normal (1st-degree connections) or inmail (non-connections, requires Premium credits)"},"groupId":{"type":"string","description":"Group ID to send message in group context (enables messaging non-connections who are group members)"}}},"example":{"accountId":"acc_12345","recipientProfileId":"john-doe-123","message":"Hello! I would like to connect with you.","messageType":"normal"}}}},"responses":{"200":{"description":"Message sent successfully","headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Maximum messages allowed per day"},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Remaining messages for today"},"X-RateLimit-Reset":{"schema":{"type":"string","format":"date-time"},"description":"When the rate limit resets (midnight UTC)"}},"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string","description":"Status message"},"recipientProfileUrn":{"type":"string","description":"LinkedIn URN of the recipient"}}},"example":{"success":true,"message":"Message sent successfully","recipientProfileUrn":"urn:li:fsd_profile:ACoAAA24A-MBVEvT49xpVF2gnWrhvmUIPDJshSM"}}}},"400":{"description":"Bad request - Missing required fields","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Either recipientProfileId or recipientProfileUrn is required","success":false}}}},"401":{"description":"Unauthorized - Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unauthorized"}}}},"404":{"description":"Recipient profile not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"LinkedIn profile not found: invalid-profile-id","success":false}}}},"429":{"description":"Rate limit exceeded","headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Maximum messages allowed per day"},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Remaining messages (0 when exceeded)"},"X-RateLimit-Reset":{"schema":{"type":"string","format":"date-time"},"description":"When the rate limit resets"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimitError"},"example":{"error":"Rate limit exceeded for MESSAGE: 100/100 used. Resets at 2024-01-16T00:00:00.000Z","success":false}}}}}}},"/connect":{"post":{"operationId":"send-connection-request","tags":["Actions"],"summary":"Send a connection request","description":"Send a connection request to a LinkedIn member to become 1st-degree connections. Optionally include a personalized message (300 character limit). Connection requests with custom messages have higher acceptance rates. **Either profileId or profileUrn must be provided.** **Rate limit: 90 connection requests per week per LinkedIn account (resets every Monday at midnight UTC). Exceeding the limit puts the account on hold for 24 hours.**","x-rateLimit":{"limit":90,"period":"week","scope":"linkedinAccount","description":"90 connection requests per week per LinkedIn account (resets every Monday at midnight UTC)"},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"accountId":{"type":"string","description":"LinkedIn account ID to use. If not provided, uses the default account."},"profileId":{"type":"string","description":"Target LinkedIn profile vanity URL slug (e.g., \"john-doe-123\"). Either this or profileUrn is required. Prefer profileUrn when available."},"profileUrn":{"type":"string","description":"Target LinkedIn profile URN (e.g., \"urn:li:fsd_profile:ACoAABJefVoBrz2LR3f...\"). Either this or profileId is required. Preferred over profileId."},"customMessage":{"type":"string","description":"Personalized connection message (max 300 characters). Leave empty for default request."}}},"example":{"accountId":"acc_12345","profileId":"john-doe-123","customMessage":"Hi John, I would love to connect with you!"}}}},"responses":{"200":{"description":"Connection request sent successfully","headers":{"X-RateLimit-Action":{"schema":{"type":"string"},"description":"The action type (CONNECT)"},"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Maximum connection requests allowed per week (90)"},"X-RateLimit-Used":{"schema":{"type":"integer"},"description":"Connection requests used this week"},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Remaining requests for this week"},"X-RateLimit-Reset":{"schema":{"type":"string","format":"date-time"},"description":"When the rate limit resets (next Monday at midnight UTC)"}},"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string","description":"Status message"},"profileUrn":{"type":"string","description":"LinkedIn URN of the target profile"}}},"example":{"success":true,"message":"Connection request sent successfully","profileUrn":"urn:li:fsd_profile:ACoAAA24A-MBVEvT49xpVF2gnWrhvmUIPDJshSM"}}}},"400":{"description":"Bad request - Missing required fields or already connected","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"missingProfile":{"summary":"Missing profile identifier","value":{"error":"Either profileId or profileUrn must be provided","success":false}},"alreadyConnected":{"summary":"Already connected","value":{"error":"Already connected to this user","success":false,"status":"CONNECTED"}},"pendingRequest":{"summary":"Request already sent","value":{"error":"Connection request already sent","success":false,"status":"PENDING"}}}}}},"401":{"description":"Unauthorized - Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unauthorized"}}}},"404":{"description":"Profile not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"LinkedIn profile not found: invalid-profile-id","success":false}}}},"429":{"description":"Rate limit exceeded - Connection requests blocked for 24 hours (other activities remain unaffected)","headers":{"X-RateLimit-Action":{"schema":{"type":"string"},"description":"The action type (CONNECT)"},"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Maximum connection requests allowed per week (90)"},"X-RateLimit-Used":{"schema":{"type":"integer"},"description":"Connection requests used this week"},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Remaining connection requests (0 when exceeded)"},"X-RateLimit-Reset":{"schema":{"type":"string","format":"date-time"},"description":"When the weekly limit resets (next Monday at midnight UTC)"},"X-Hold-Until":{"schema":{"type":"string","format":"date-time"},"description":"Connection requests blocked until this time (24 hours from limit exceeded)"}},"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Error message describing the rate limit"},"success":{"type":"boolean"},"holdUntil":{"type":"string","format":"date-time","description":"Connection requests blocked until this time"},"weeklyReset":{"type":"string","format":"date-time","description":"When the weekly limit resets"}}},"example":{"error":"Connection request limit exceeded: 90/90 used. New connection requests blocked for 24 hours. Other activities (messaging, following, etc.) are not affected.","success":false,"holdUntil":"2024-01-16T10:30:00.000Z","weeklyReset":"2024-01-22T00:00:00.000Z"}}}}}}},"/posts/comment":{"post":{"operationId":"comment-on-post","tags":["Posts"],"summary":"Comment on a LinkedIn post","description":"Add a comment to a LinkedIn post. Comments increase engagement and visibility. Set `tagPostAuthor` to true to tag the post author with a real @mention that notifies them. Supports posting as a company page if you have admin access. **Rate limit: 100 comments per day.**","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["postUrl","comment"],"properties":{"accountId":{"type":"string","description":"LinkedIn account ID to use. If not provided, uses the default account."},"postUrl":{"type":"string","description":"Full URL of the LinkedIn post to comment on"},"comment":{"type":"string","description":"Comment text content. Supports @mentions and hashtags. When `tagPostAuthor` is true, place a {{author}} placeholder where the author tag should appear — otherwise the author name is prepended.","example":"Great breakdown, {{author}} — thanks for sharing this!"},"tagPostAuthor":{"type":"boolean","default":false,"description":"When true, tags the post author with a real @mention that notifies them. The tag is inserted at a {{author}} placeholder in `comment` if present, otherwise the author name is prepended.","example":true},"companyUrn":{"type":"string","description":"Company URN to post comment as company page instead of personal profile"}}}}}},"responses":{"200":{"description":"Comment posted successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string","description":"Success message"},"postUrl":{"type":"string","description":"URL of the post that was commented on"},"comment":{"type":"string","description":"The comment text that was posted"},"accountId":{"type":"string","description":"LinkedIn account ID that posted the comment"},"postDetails":{"type":"object","description":"Details about the post that was commented on","properties":{"activityUrn":{"type":"string","description":"Activity URN of the post"},"ugcPostUrn":{"type":"string","nullable":true,"description":"UGC Post URN if available"},"shareUrn":{"type":"string","description":"Share URN of the post"},"featuredActivityUrn":{"type":"string","nullable":true,"description":"Featured activity URN if applicable"},"content":{"type":"string","description":"Content/text of the original post"}}}}},"example":{"success":true,"message":"Successfully posted comment","postUrl":"https://www.linkedin.com/feed/update/urn:li:activity:7430667226199830528/","comment":"Great insights! Thanks for sharing.","accountId":"696ce9e780e0483585e4e553","postDetails":{"activityUrn":"urn:li:activity:7430667226199830528","ugcPostUrn":null,"shareUrn":"urn:li:share:7430667225633550337","featuredActivityUrn":null,"content":"Discover proven LinkedIn strategies for consultants to attract high-value clients..."}}}}},"400":{"description":"Bad request - Missing required fields or validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"postUrl is required","success":false}}}},"401":{"description":"Unauthorized - Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unauthorized"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"success":{"type":"boolean"}}},"example":{"error":"Rate limit exceeded for COMMENT: 100/100 used. Resets at 2024-01-16T00:00:00.000Z","success":false}}}},"500":{"description":"Server error - Failed to post comment","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Failed to post comment","success":false}}}}}}},"/posts/latest":{"post":{"operationId":"get-latest-posts","tags":["Posts"],"summary":"Get latest posts from a profile","description":"Retrieve the most recent posts from a LinkedIn profile. Useful for monitoring competitor content, tracking influencer activity, or finding engagement opportunities. Can include or exclude reposts/shares.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"accountId":{"type":"string","description":"LinkedIn account ID to use. If not provided, uses the default account."},"profileId":{"type":"string","description":"LinkedIn profile vanity URL slug (the part after linkedin.com/in/, e.g., \"john-doe-123\"). When possible, use profileUrn instead for more reliable results."},"profileUrn":{"type":"string","description":"LinkedIn profile URN (e.g., \"urn:li:fsd_profile:ACoAABJefVoBrz2LR3f...\"). Preferred over profileId — avoids an extra API call to resolve the vanity name."},"count":{"type":"number","minimum":1,"maximum":20,"default":1,"description":"Number of recent posts to retrieve (max 20)"},"includeReposts":{"type":"boolean","default":true,"description":"Whether to include reposts/shares in results"}}}}}},"responses":{"200":{"description":"Posts retrieved successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"profileUrn":{"type":"string","description":"LinkedIn profile URN of the target user"},"accountId":{"type":"string","description":"LinkedIn account ID used for the request"},"posts":{"type":"array","items":{"type":"object","properties":{"activityUrn":{"type":"string","description":"LinkedIn activity URN of the post"},"url":{"type":"string","description":"Direct URL to the LinkedIn post"},"content":{"type":"string","description":"Post text content (commentary)"},"isGroupPost":{"type":"boolean","description":"Whether this post is from a LinkedIn group"},"numLikes":{"type":"number","description":"Number of likes/reactions on the post"},"numComments":{"type":"number","description":"Number of comments on the post"},"numShares":{"type":"number","description":"Number of shares/reposts of this post"},"authorName":{"type":"string","description":"Name of the post author"},"authorProfileUrl":{"type":"string","description":"LinkedIn profile URL of the author"},"authorProfilePicture":{"type":"string","description":"Profile picture URL of the author"},"authorType":{"type":"string","enum":["person","company","unknown"],"description":"Type of author (person or company)"},"isEdited":{"type":"boolean","description":"Whether the post has been edited"},"isArticle":{"type":"boolean","description":"Whether the post is an article"},"isRepost":{"type":"boolean","description":"Whether the post is a repost/reshare"},"timestamp":{"type":"string","description":"Relative timestamp of the post (e.g., \"1w\", \"3d\")"}}}},"count":{"type":"number","description":"Number of posts requested"},"totalPostsFound":{"type":"number","description":"Total number of posts found and returned"},"message":{"type":"string","description":"Status message describing the result"}}},"example":{"success":true,"profileUrn":"urn:li:fsd_profile:ACoAAA24A-MBVEvT49xpVF2gnWrhvmUIPDJshSM","accountId":"696ce9e780e0483585e4e553","posts":[{"activityUrn":"urn:li:activity:7429892659739029504","url":"https://www.linkedin.com/feed/update/urn:li:activity:7429892659739029504","content":"The end of Q1. And what a three months it's been...","isGroupPost":false,"numLikes":86,"numComments":5,"numShares":2,"authorName":"Andy Burrows","authorProfileUrl":"https://www.linkedin.com/in/andy-burrows-60256731","authorProfilePicture":"https://media.licdn.com/dms/image/...","authorType":"person","isEdited":false,"isArticle":false,"isRepost":false,"timestamp":"1w"}],"count":1,"totalPostsFound":1,"message":"Found 1 valid posts out of 1 total posts"}}}},"400":{"description":"Bad request - Missing required fields","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Either profileId or profileUrn must be provided","success":false}}}},"401":{"description":"Unauthorized - Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unauthorized"}}}},"404":{"description":"Profile not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"LinkedIn profile not found: invalid-profile-id","success":false}}}}}}},"/posts/react":{"post":{"operationId":"react-to-post","tags":["Posts"],"summary":"React to a LinkedIn post","description":"Add a reaction to a LinkedIn post. LinkedIn supports 6 reaction types beyond simple likes. Reactions are a lightweight way to engage with content and increase visibility in your network. Supports reacting as a company page if you have admin access.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["postUrl"],"properties":{"accountId":{"type":"string","description":"LinkedIn account ID to use. If not provided, uses the default account."},"postUrl":{"type":"string","description":"Full URL of the LinkedIn post to react to. Can also provide threadUrn directly instead."},"threadUrn":{"type":"string","description":"Thread URN of the post. If provided, skips URL scraping for faster execution."},"reactionType":{"type":"string","enum":["LIKE","PRAISE","APPRECIATION","EMPATHY","INTEREST","ENTERTAINMENT"],"default":"LIKE","description":"Reaction type: LIKE (thumbs up), PRAISE (clap), APPRECIATION (heart), EMPATHY (caring), INTEREST (insightful), ENTERTAINMENT (funny)"},"companyUrn":{"type":"string","description":"Company URN to react as company page instead of personal profile (e.g., \"urn:li:fsd_company:123456\"). Requires admin access to the company page."}}}}}},"responses":{"200":{"description":"Reaction added successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string","description":"Success message describing the action"},"postUrl":{"type":"string","nullable":true,"description":"URL of the post that was reacted to"},"threadUrn":{"type":"string","description":"Thread URN of the post"},"reactionType":{"type":"string","description":"Type of reaction applied (LIKE, PRAISE, etc.)"},"accountId":{"type":"string","description":"LinkedIn account ID that added the reaction"},"companyUrn":{"type":"string","description":"Company URN if reacted as company page"},"postDetails":{"type":"object","description":"Details about the post that was reacted to","properties":{"activityUrn":{"type":"string","description":"Activity URN of the post"},"ugcPostUrn":{"type":"string","nullable":true,"description":"UGC Post URN if available"},"shareUrn":{"type":"string","description":"Share URN of the post"},"featuredActivityUrn":{"type":"string","nullable":true,"description":"Featured activity URN if applicable"},"content":{"type":"string","description":"Content/text of the original post"}}}}},"example":{"success":true,"message":"Successfully reacted to post with LIKE reaction","postUrl":"https://www.linkedin.com/feed/update/urn:li:activity:7430667226199830528/","threadUrn":"urn:li:activity:7430667226199830528","reactionType":"LIKE","accountId":"696ce9e780e0483585e4e553","postDetails":{"activityUrn":"urn:li:activity:7430667226199830528","ugcPostUrn":null,"shareUrn":"urn:li:share:7430667225633550337","featuredActivityUrn":null,"content":"Discover proven LinkedIn strategies for consultants to attract high-value clients..."}}}}},"400":{"description":"Bad request - Missing required fields or validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Either postUrl or threadUrn must be provided","success":false}}}},"401":{"description":"Unauthorized - Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unauthorized"}}}},"500":{"description":"Server error - Failed to react to post","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Failed to react to post","success":false}}}}}}},"/posts/comments":{"post":{"operationId":"get-post-comments","tags":["Posts"],"summary":"Get comments from a post","description":"Retrieve comments from a LinkedIn post with pagination support. Useful for analyzing engagement, finding leads who commented, or monitoring discussions. Returns comment content, author info, timestamps, and like counts.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["postUrl"],"properties":{"accountId":{"type":"string","description":"LinkedIn account ID to use. If not provided, uses the default account."},"postUrl":{"type":"string","description":"Full URL of the LinkedIn post to get comments from"},"commentCount":{"type":"number","minimum":1,"maximum":100,"description":"Number of comments per page (enables pagination mode)"},"start":{"type":"number","minimum":0,"description":"Pagination offset (0-indexed)"},"paginationToken":{"type":"string","description":"Token from previous response to fetch next page"},"maxComments":{"type":"number","minimum":10,"maximum":5000,"default":1000,"description":"Maximum total comments to retrieve"},"batchSize":{"type":"number","minimum":10,"maximum":100,"default":50,"description":"Internal batch size for fetching"}}}}}},"responses":{"200":{"description":"Comments retrieved successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"postUrl":{"type":"string","description":"URL of the post"},"accountId":{"type":"string","description":"LinkedIn account ID used for the request"},"comments":{"type":"array","items":{"type":"object","properties":{"commentId":{"type":"string","description":"Unique identifier for the comment"},"authorName":{"type":"string","description":"Name of the comment author"},"commentText":{"type":"string","description":"Content of the comment"},"authorDesignation":{"type":"string","description":"Headline/designation of the author"},"commenterProfileId":{"type":"string","description":"Profile URN of the commenter"},"publicIdentifier":{"type":"string","description":"Public ID for the profile URL"},"profileUrl":{"type":"string","description":"Full URL to the commenter profile"},"authorProfilePicture":{"type":"string","description":"URL to profile picture"},"createdAt":{"type":"number","description":"Timestamp when comment was created"},"likeCount":{"type":"number","description":"Number of likes on the comment"},"replyCount":{"type":"number","description":"Number of replies to the comment"},"hasProfile":{"type":"boolean","description":"Whether commenter has a profile"},"commenterUrn":{"type":"string","description":"URN of the commenter"}}}},"pagination":{"type":"object","properties":{"start":{"type":"number"},"count":{"type":"number"},"total":{"type":"number"},"hasNextPage":{"type":"boolean"},"nextPaginationToken":{"type":"string","nullable":true},"nextStart":{"type":"number","nullable":true}}},"postDetails":{"type":"object","properties":{"activityUrn":{"type":"string"},"ugcPostUrn":{"type":"string","nullable":true},"shareUrn":{"type":"string","nullable":true},"featuredActivityUrn":{"type":"string","nullable":true},"content":{"type":"string"}}}}},"example":{"success":true,"postUrl":"https://www.linkedin.com/feed/update/urn:li:activity:7431898703118110720","accountId":"696ce9e780e0483585e4e553","comments":[{"commentId":"urn:li:fsd_comment:(7431913248658059265,urn:li:ugcPost:7431898701888966656)","authorName":"John Doe","commentText":"Great insights! Thanks for sharing.","authorDesignation":"Software Engineer at Tech Company","commenterProfileId":"ACoAAAkEZoAB2YcQrbVMuMkEvlMH8zSEC5ESxec","publicIdentifier":"john-doe-123","profileUrl":"https://www.linkedin.com/in/john-doe-123","authorProfilePicture":"https://media.licdn.com/dms/image/...","createdAt":1771906196752,"likeCount":5,"replyCount":2,"hasProfile":true,"commenterUrn":"urn:li:fsd_profile:ACoAAAkEZoAB2YcQrbVMuMkEvlMH8zSEC5ESxec"}],"pagination":{"start":0,"count":5,"total":10,"hasNextPage":true,"nextPaginationToken":"abc123","nextStart":5},"postDetails":{"activityUrn":"urn:li:activity:7431898703118110720","ugcPostUrn":"urn:li:ugcPost:7431898701888966656","shareUrn":null,"featuredActivityUrn":null,"content":"Post content here..."}}}}},"400":{"description":"Bad request - Missing required fields","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":"object"}}},"example":{"success":false,"error":{"issues":[{"code":"invalid_type","expected":"string","received":"undefined","path":["postUrl"],"message":"Required"}],"name":"ZodError"}}}}},"401":{"description":"Unauthorized - Invalid or missing API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}},"example":{"error":"Unauthorized - Invalid credentials"}}}}}}},"/posts/comments/all":{"post":{"operationId":"get-all-post-comments","tags":["Posts"],"summary":"Get all comments from a post","description":"Fetch all comments from a LinkedIn post in a single request. Automatically handles pagination internally. Ideal for bulk analysis of post engagement. Use for posts with many comments where you need complete data.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["postUrl"],"properties":{"accountId":{"type":"string","description":"LinkedIn account ID to use. If not provided, uses the default account."},"postUrl":{"type":"string","description":"Full URL of the LinkedIn post to get all comments from"},"batchSize":{"type":"number","minimum":10,"maximum":100,"default":50,"description":"Number of comments to fetch per internal request"},"maxComments":{"type":"number","minimum":10,"maximum":5000,"default":1000,"description":"Maximum total comments to retrieve (safety limit)"}}}}}},"responses":{"200":{"description":"All comments retrieved successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"postUrl":{"type":"string","description":"URL of the post"},"accountId":{"type":"string","description":"LinkedIn account ID used for the request"},"comments":{"type":"array","items":{"type":"object","properties":{"commentId":{"type":"string"},"authorName":{"type":"string"},"commentText":{"type":"string"},"authorDesignation":{"type":"string"},"commenterProfileId":{"type":"string"},"publicIdentifier":{"type":"string"},"profileUrl":{"type":"string"},"authorProfilePicture":{"type":"string"},"createdAt":{"type":"number"},"likeCount":{"type":"number"},"replyCount":{"type":"number"},"hasProfile":{"type":"boolean"},"commenterUrn":{"type":"string"}}}},"summary":{"type":"object","description":"Summary of the fetch operation","properties":{"totalComments":{"type":"number","description":"Total number of comments fetched"},"fetchDurationMs":{"type":"number","description":"Time taken to fetch all comments in milliseconds"},"maxCommentsLimit":{"type":"number","description":"Maximum comments limit configured"},"batchSize":{"type":"number","description":"Batch size used for fetching"},"truncated":{"type":"boolean","description":"Whether results were truncated due to limit"}}},"postDetails":{"type":"object","properties":{"activityUrn":{"type":"string"},"ugcPostUrn":{"type":"string","nullable":true},"shareUrn":{"type":"string","nullable":true},"featuredActivityUrn":{"type":"string","nullable":true},"content":{"type":"string"}}}}},"example":{"success":true,"postUrl":"https://www.linkedin.com/feed/update/urn:li:activity:7431898703118110720","accountId":"696ce9e780e0483585e4e553","comments":[{"commentId":"urn:li:fsd_comment:(7431913248658059265,urn:li:ugcPost:7431898701888966656)","authorName":"John Doe","commentText":"Great post!","authorDesignation":"Engineer","commenterProfileId":"ACoAAA...","publicIdentifier":"john-doe","profileUrl":"https://www.linkedin.com/in/john-doe","authorProfilePicture":"https://media.licdn.com/...","createdAt":1771906196752,"likeCount":3,"replyCount":1,"hasProfile":true,"commenterUrn":"urn:li:fsd_profile:ACoAAA..."}],"summary":{"totalComments":15,"fetchDurationMs":4253,"maxCommentsLimit":1000,"batchSize":50,"truncated":false},"postDetails":{"activityUrn":"urn:li:activity:7431898703118110720","ugcPostUrn":"urn:li:ugcPost:7431898701888966656","shareUrn":null,"featuredActivityUrn":null,"content":"Post content here..."}}}}},"400":{"description":"Bad request - Missing required fields","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":"object"}}},"example":{"success":false,"error":{"issues":[{"code":"invalid_type","expected":"string","received":"undefined","path":["postUrl"],"message":"Required"}],"name":"ZodError"}}}}},"401":{"description":"Unauthorized - Invalid or missing API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}},"example":{"error":"Unauthorized - Invalid credentials"}}}}}}},"/posts/search":{"post":{"operationId":"search-posts","tags":["Posts"],"summary":"Search posts by keyword","description":"Search LinkedIn posts by keywords with filtering options. Find relevant content for engagement, monitor industry discussions, or discover trending topics. Filter by date posted, sort by relevance or recency, and target posts by author job titles.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["keywords"],"properties":{"accountId":{"type":"string","description":"LinkedIn account ID to use. If not provided, uses the default account."},"keywords":{"type":"string","minLength":1,"description":"Search keywords to find in post content"},"count":{"type":"number","minimum":1,"maximum":500,"default":50,"description":"Number of posts to return per page"},"start":{"type":"number","minimum":0,"default":0,"description":"Pagination offset (0-indexed)"},"datePosted":{"type":"string","enum":["past-24h","past-week","past-month","any-time"],"default":"any-time","description":"Filter by when post was published"},"sortBy":{"type":"string","enum":["relevance","date_posted"],"default":"relevance","description":"Sort order: relevance (best match) or date_posted (newest first)"},"authorJobTitles":{"type":"array","items":{"type":"string"},"maxItems":5,"description":"Filter by author job titles (e.g., [\"CEO\", \"VP Marketing\"])"},"exactSearch":{"type":"boolean","default":true,"description":"When true (default), the keyword is wrapped in double quotes for an exact-phrase match. Set to false for a broader search that also matches individual terms."}}}}}},"responses":{"200":{"description":"Search results retrieved successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"keyword":{"type":"string"},"accountId":{"type":"string"},"posts":{"type":"array","items":{"type":"object","properties":{"urn":{"type":"string","description":"Activity URN of the post"},"url":{"type":"string","description":"Permalink of the post"},"postUrl":{"type":"string","description":"Alias of `url` — same value, kept for legacy clients"},"text":{"type":"string"},"userCommentary":{"type":"string","description":"Same value as `text`"},"content":{"type":"string","description":"Alias of `text` — same value, kept for legacy clients"},"authorName":{"type":"string","description":"Alias of `author.name`"},"authorProfileUrl":{"type":"string","description":"Alias of `author.profileUrl`"},"canComment":{"type":"boolean","description":"Always true here — the real permission check happens when the post is scraped"},"timestamp":{"type":"string","description":"Relative time string (e.g. \"3mo\", \"2w\", \"1d\")"},"likes":{"type":"number"},"comments":{"type":"number"},"shares":{"type":"number"},"author":{"type":"object","properties":{"name":{"type":"string"},"profileUrl":{"type":"string"},"profilePicture":{"type":"string"},"type":{"type":"string","enum":["person","company"]}}},"authorHeadline":{"type":"string"},"isEdited":{"type":"boolean"},"isArticle":{"type":"boolean"},"isRepost":{"type":"boolean"}}}},"pagination":{"type":"object","properties":{"start":{"type":"number"},"count":{"type":"number","description":"Posts actually returned"},"requested":{"type":"number","description":"Count originally asked for, before the cap"},"capped":{"type":"number","description":"Ceiling actually applied (30 max)"},"total":{"type":"number","nullable":true,"description":"Always null — the underlying pager reports only whether another page exists, never a result total"},"hasMore":{"type":"boolean"},"nextStart":{"type":"number","nullable":true},"batchesFetched":{"type":"number"},"fetchDurationMs":{"type":"number"}}},"filters":{"type":"object","properties":{"datePosted":{"type":"string"},"sortBy":{"type":"string"},"authorJobTitles":{"type":"array","items":{"type":"string"}}}},"searchId":{"type":"string","nullable":true,"description":"Always null"},"message":{"type":"string"}}}}}}}}},"/posts/repost":{"post":{"operationId":"repost-post","tags":["Posts"],"summary":"Repost a LinkedIn post","description":"Repost/share a LinkedIn post to your feed. This creates an instant repost without additional commentary. Requires either a post URL (will be scraped for URNs) or the share/ugcPost URN directly. The activityUrn is optional but helps with engagement tracking. Provide companyUrn to repost as a company page you administer instead of your personal profile.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"accountId":{"type":"string","description":"LinkedIn account ID to use. If not provided, uses the default account."},"postUrl":{"type":"string","description":"Full URL of the LinkedIn post to repost. Will be scraped to extract URNs."},"shareUrn":{"type":"string","description":"Share URN of the post (e.g., \"urn:li:share:7416350085304987648\"). Use if you already have the URN."},"ugcPostUrn":{"type":"string","description":"UGC Post URN (e.g., \"urn:li:ugcPost:7417595827650519041\"). Used as fallback if shareUrn not available."},"activityUrn":{"type":"string","description":"Activity URN for pre-repost signal (e.g., \"urn:li:activity:7416350088379338752\"). Optional but recommended."},"companyUrn":{"type":"string","description":"Optional. Repost as a company page you administer instead of your personal profile. Provide the company URN (e.g., \"urn:li:fsd_company:134684122\")."}}}}}},"responses":{"200":{"description":"Repost created successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"repostUrn":{"type":"string","description":"URN of the created repost"},"repostUrl":{"type":"string","description":"Viewable permalink of the created repost (LinkedIn's \"View repost\" link). May be absent if LinkedIn returns no toast link."},"resourceKey":{"type":"string","description":"Resource key of the repost"},"shareUrn":{"type":"string","description":"Share URN that was reposted"},"ugcPostUrn":{"type":"string","description":"UGC Post URN that was reposted (if shareUrn not available)"},"activityUrn":{"type":"string","description":"Activity URN used for tracking"},"accountId":{"type":"string","description":"LinkedIn account ID that performed the repost"},"companyUrn":{"type":"string","description":"Company URN the repost was authored as (only present when reposting as a company page)"}}},"example":{"success":true,"message":"Repost successful","repostUrn":"urn:li:fsd_repost:urn:li:instantRepost:(urn:li:share:7430667225633550337,7432008152021254154)","repostUrl":"https://www.linkedin.com/feed/update/urn:li:activity:7432008152089161728","resourceKey":"urn:li:fsd_repost:urn:li:instantRepost:(urn:li:share:7430667225633550337,7432008152021254154)","shareUrn":"urn:li:share:7430667225633550337","activityUrn":"urn:li:activity:7430667226199830528","accountId":"696ce9e780e0483585e4e553","companyUrn":"urn:li:fsd_company:134684122"}}}},"400":{"description":"Invalid request or unable to extract URNs","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":"string"},"shareUrn":{"type":"string"},"ugcPostUrn":{"type":"string"}}},"example":{"error":"Either postUrl, shareUrn, or ugcPostUrn must be provided","success":false}}}},"401":{"description":"Unauthorized - Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unauthorized"}}}},"500":{"description":"Server error - Failed to repost","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Failed to repost","success":false}}}}}}},"/posts/scrape":{"post":{"operationId":"scrape-post","tags":["Posts"],"summary":"Scrape LinkedIn post details","description":"Extract detailed information from a LinkedIn post URL including content, author, engagement metrics, and media. Uses caching to reduce API calls. Supports both authenticated and public scraping with proxy rotation for reliability. Use forceRefresh to bypass cache.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["postUrl"],"properties":{"postUrl":{"type":"string","format":"uri","description":"Full LinkedIn post URL to scrape"},"accountId":{"type":"string","description":"LinkedIn account ID for authenticated scraping (gets more data than public)"},"useCache":{"type":"boolean","default":true,"description":"Whether to use cached results if available"},"maxCacheAge":{"type":"number","default":24,"description":"Maximum age of cached data in hours before refresh"},"maxProxyRetries":{"type":"number","default":3,"description":"Number of proxy rotation attempts on failure"},"forceRefresh":{"type":"boolean","default":false,"description":"Force fresh scrape ignoring cache"}}},"example":{"postUrl":"https://www.linkedin.com/posts/john-doe-123_example-post-activity-7430667226199830528-Cu89","forceRefresh":false}}}},"responses":{"200":{"description":"Post details scraped successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Whether the scrape was successful"},"originalUrl":{"type":"string","description":"Original URL provided in the request"},"finalUrl":{"type":"string","description":"Final URL after any redirects"},"scrapeDuration":{"type":"number","description":"Time taken to scrape in milliseconds"},"data":{"type":"object","description":"Scraped post data","properties":{"content":{"type":"string","description":"Full text content of the post"},"ugcPostUrn":{"type":"string","nullable":true,"description":"UGC Post URN identifier"},"activityUrn":{"type":"string","description":"Activity URN identifier"},"shareUrn":{"type":"string","nullable":true,"description":"Share URN if available"},"featuredActivityUrn":{"type":"string","nullable":true,"description":"Featured activity URN if applicable"},"author":{"type":"object","properties":{"name":{"type":"string","description":"Author display name"},"headline":{"type":"string","description":"Author headline/title"},"profileUrl":{"type":"string","description":"URL to author LinkedIn profile"}}},"timestamp":{"type":"string","description":"Post timestamp (relative or absolute)"},"engagement":{"type":"object","properties":{"likes":{"type":"number","description":"Number of likes/reactions"},"comments":{"type":"number","description":"Number of comments"},"shares":{"type":"number","description":"Number of shares/reposts"}}},"media":{"type":"object","description":"Media types present in the post","properties":{"hasImages":{"type":"boolean"},"hasVideo":{"type":"boolean"},"hasDocument":{"type":"boolean"},"hasArticle":{"type":"boolean"},"hasLink":{"type":"boolean"},"hasPoll":{"type":"boolean"}}},"permissions":{"type":"object","properties":{"canComment":{"type":"boolean","description":"Whether the authenticated account can comment on this post. false when the post restricts commenting (e.g. \"Only connections can comment on this post\" and the account is not a connection) or commenting is fully disabled. Accurate only with authenticated scraping; defaults to true otherwise."}}},"url":{"type":"string","description":"Canonical post URL"},"scraped":{"type":"boolean","description":"Indicates data was scraped"},"scrapedAt":{"type":"string","format":"date-time","description":"Timestamp when post was scraped"}}},"usingAuthenticatedScraping":{"type":"boolean","description":"Whether authenticated scraping was used"},"message":{"type":"string","description":"Success message"}}},"example":{"success":true,"originalUrl":"https://www.linkedin.com/posts/john-doe-123_example-post-activity-7430667226199830528-Cu89","finalUrl":"https://www.linkedin.com/posts/john-doe-123_example-post-activity-7430667226199830528-Cu89","scrapeDuration":4092,"data":{"content":"This is an example post content with insights about technology and business...","ugcPostUrn":"urn:li:ugcPost:7430667225633550337","activityUrn":"urn:li:activity:7430667226199830528","shareUrn":null,"featuredActivityUrn":null,"author":{"name":"John Doe","headline":"Software Engineer at Tech Company","profileUrl":"https://www.linkedin.com/in/john-doe-123"},"timestamp":"2d","engagement":{"likes":86,"comments":12,"shares":3},"media":{"hasImages":true,"hasVideo":false,"hasDocument":false,"hasArticle":false,"hasLink":true,"hasPoll":false},"permissions":{"canComment":true},"url":"https://www.linkedin.com/posts/john-doe-123_example-post-activity-7430667226199830528-Cu89","scraped":true,"scrapedAt":"2026-02-24T12:56:58.220Z"},"usingAuthenticatedScraping":true,"message":"Successfully scraped post content"}}}},"400":{"description":"Bad request - Missing or invalid postUrl","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":"object","properties":{"issues":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"path":{"type":"array","items":{"type":"string"}}}}},"name":{"type":"string"}}}}},"examples":{"missingUrl":{"summary":"Missing postUrl","value":{"success":false,"error":{"issues":[{"code":"invalid_type","expected":"string","received":"undefined","path":["postUrl"],"message":"Required"}],"name":"ZodError"}}},"invalidUrl":{"summary":"Invalid URL format","value":{"success":false,"error":{"issues":[{"validation":"url","code":"invalid_string","message":"Invalid url","path":["postUrl"]}],"name":"ZodError"}}}}}}},"401":{"description":"Unauthorized - Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unauthorized - Invalid credentials"}}}},"500":{"description":"Server error - Failed to scrape post after all retry attempts","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Error message describing the failure"},"success":{"type":"boolean"},"status":{"type":"number"},"originalUrl":{"type":"string","description":"The URL that failed to scrape"}}},"example":{"error":"Scraping error: Failed to scrape LinkedIn post after all attempts. Errors: Rotating proxy attempt 1: partial/no data; Rotating proxy attempt 2: partial/no data","success":false,"status":500,"originalUrl":"https://www.linkedin.com/posts/invalid-post-12345"}}}}}}},"/profile":{"get":{"operationId":"get-profile","tags":["Profile"],"summary":"Fetch LinkedIn profile information (GET)","description":"Retrieve detailed profile information for a LinkedIn member via query parameters. Same functionality as POST /profile but using GET method for simpler integration. Results are cached for 6 hours. **Rate limit: 120 unique profiles per day per LinkedIn account (cached requests do not count against limit).**\n\n`relationshipStatus` is the source of truth for the relationship — `CONNECTED`, `INVITATION_SENT`, `INVITATION_RECEIVED` or `NOT_CONNECTED`. `INVITATION_RECEIVED` means they invited YOU: they are not a connection and cannot be messaged. `connectionDegree` is `\"1st\"` when connected and `null` otherwise.\n\n**Cached responses can be stale by up to 6 hours**, including the relationship fields. Pass `forceRefresh=true` when the answer must be current — for example before deciding whether someone can be messaged.","x-rateLimit":{"limit":120,"period":"day","scope":"linkedinAccount","cacheTime":"6 hours","description":"120 unique profile fetches per day per LinkedIn account. Cached profiles (within 6 hours) do not count against limit."},"parameters":[{"name":"profileId","in":"query","required":true,"schema":{"type":"string"},"description":"LinkedIn profile vanity URL slug (the part after linkedin.com/in/, e.g., \"john-doe-123\")"},{"name":"accountId","in":"query","required":false,"schema":{"type":"string"},"description":"LinkedIn account ID to use. If not provided, uses the default account."},{"name":"includeGeoLocation","in":"query","required":false,"schema":{"type":"boolean","default":false},"description":"Include detailed geo location data"},{"name":"includeContact","in":"query","required":false,"schema":{"type":"boolean","default":false},"description":"Include contact info if visible"},{"name":"forceRefresh","in":"query","required":false,"schema":{"type":"boolean","default":false},"description":"Skip cache and fetch fresh data"}],"responses":{"200":{"description":"Profile information retrieved successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"profileId":{"type":"string"},"accountId":{"type":"string"},"cached":{"type":"boolean","description":"true when served from the 6-hour cache. Cached relationship fields can be up to 6 hours stale — use forceRefresh=true when that matters."},"profile":{"type":"object","description":"Profile object — identical shape to POST /profile."}}},"example":{"success":true,"profileId":"anandi-devi","accountId":"696ce9e780e0483585e4e553","cached":false,"profile":{"firstName":"Anandi","lastName":"Devi","headline":"Go-To-Market (GTM) Engineer @ ConnectSafely | MBA","entityUrn":"urn:li:fsd_profile:ACoAAFpqSoMB8vTqRbg4mN_wbabO8w0gjgFu-6o","publicIdentifier":"anandi-devi","isPremium":true,"isConnected":true,"relationshipStatus":"CONNECTED","connectionDegree":"1st"}}}}},"400":{"description":"Bad request - Missing profileId","content":{"application/json":{"example":{"error":"profileId query parameter is required","success":false}}}},"401":{"description":"Unauthorized","content":{"application/json":{"example":{"error":"Unauthorized"}}}}}},"post":{"operationId":"fetch-profile","tags":["Profile"],"summary":"Fetch LinkedIn profile information","description":"Retrieve detailed profile information for a LinkedIn member including name, headline, premium status, and profile URNs. Results are cached for 6 hours to reduce API calls. Optionally include geo location details and contact information (email, phone if visible). **Rate limit: 120 unique profiles per day per LinkedIn account (cached requests do not count against limit).**","x-rateLimit":{"limit":120,"period":"day","scope":"linkedinAccount","cacheTime":"6 hours","description":"120 unique profile fetches per day per LinkedIn account. Cached profiles (within 6 hours) do not count against limit."},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["profileId"],"properties":{"accountId":{"type":"string","description":"LinkedIn account ID to use. If not provided, uses the default account."},"profileId":{"type":"string","minLength":1,"description":"LinkedIn profile vanity URL slug (the part after linkedin.com/in/, e.g., \"john-doe-123\")"},"includeGeoLocation":{"type":"boolean","default":false,"description":"Include detailed geo location data (city, country, coordinates)"},"includeContact":{"type":"boolean","default":false,"description":"Include contact info (email, phone) if visible to viewer"},"includeExperience":{"type":"boolean","default":false,"description":"Include work experience history"},"includeEducation":{"type":"boolean","default":false,"description":"Include education history"},"includeSkills":{"type":"boolean","default":false,"description":"Include skills with endorsement counts"},"forceRefresh":{"type":"boolean","default":false,"description":"Skip cache and fetch fresh data from LinkedIn. Use sparingly as it counts against rate limit."}}},"example":{"profileId":"john-doe-123","includeGeoLocation":false,"includeContact":false,"includeExperience":false,"includeEducation":false,"includeSkills":false,"forceRefresh":false}}}},"responses":{"200":{"description":"Profile information retrieved successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Whether the request was successful"},"profileId":{"type":"string","description":"The requested profile vanity URL slug"},"accountId":{"type":"string","description":"LinkedIn account ID used for the request"},"profile":{"type":"object","description":"Profile data (empty object if profile not found)","properties":{"firstName":{"type":"string","description":"First name"},"lastName":{"type":"string","description":"Last name"},"headline":{"type":"string","description":"Professional headline"},"location":{"type":"object","nullable":true,"description":"Basic location details from profile","properties":{"countryCode":{"type":"string","nullable":true,"description":"ISO country code (e.g., \"us\", \"in\")"},"postalCode":{"type":"string","nullable":true,"description":"Postal code if available"},"geoLocationName":{"type":"string","nullable":true,"description":"Location name if available"}}},"entityUrn":{"type":"string","description":"Full profile URN (urn:li:fsd_profile:...)"},"publicIdentifier":{"type":"string","description":"Public profile ID (URL slug)"},"profilePicture":{"type":"string","nullable":true,"description":"Profile picture URL (100x100 thumbnail). May be null for some influencer/creator profiles."},"isPremium":{"type":"boolean","description":"Whether user has LinkedIn Premium"},"isVerified":{"type":"boolean","description":"Whether profile is verified by LinkedIn"},"supportsFreeEmail":{"type":"boolean","description":"Whether you can send free InMail to this user"},"connectionCount":{"type":"number","nullable":true,"description":"Number of connections"},"followerCount":{"type":"number","nullable":true,"description":"Number of followers"},"isConnected":{"type":"boolean","description":"Whether you are 1st-degree connected. False while an invitation in either direction is still pending — check relationshipStatus for which."},"connectionDegree":{"type":"string","nullable":true,"description":"\"1st\" when connected, otherwise null. LinkedIn does not attribute 2nd/3rd reliably in this payload, so it is reported as null rather than guessed."},"relationshipStatus":{"type":"string","enum":["CONNECTED","INVITATION_SENT","INVITATION_RECEIVED","NOT_CONNECTED"],"description":"The single source of truth for the relationship. INVITATION_RECEIVED means they invited YOU and are not a connection yet."},"invitationSent":{"type":"boolean","description":"You invited them and they have not responded"},"invitationReceived":{"type":"boolean","description":"They invited YOU and you have not responded. Respond via POST /invitations/received/respond."},"geoLocation":{"type":"object","nullable":true,"description":"Detailed geo location (only included when includeGeoLocation=true)","properties":{"city":{"type":"string","nullable":true,"description":"City name (e.g., \"Delhi\", \"Seattle\")"},"state":{"type":"string","nullable":true,"description":"State/region name (e.g., \"Washington\", \"Delhi\")"},"country":{"type":"string","nullable":true,"description":"Country name (e.g., \"India\", \"United States\")"},"fullLocation":{"type":"string","nullable":true,"description":"Full location string (e.g., \"South Delhi, Delhi, India\")"},"birthDate":{"type":"object","nullable":true,"description":"Birth date if publicly available","properties":{"month":{"type":"number","nullable":true},"day":{"type":"number","nullable":true},"year":{"type":"number","nullable":true}}}}}}},"experience":{"type":"array","nullable":true,"description":"Work experience history (only included when includeExperience=true)","items":{"type":"object","properties":{"title":{"type":"string","description":"Job title"},"companyName":{"type":"string","description":"Company name"},"employmentType":{"type":"string","nullable":true,"description":"Employment type (e.g., \"Full-time\", \"Part-time\")"},"duration":{"type":"string","nullable":true,"description":"Duration at the position"},"location":{"type":"string","nullable":true,"description":"Work location"},"description":{"type":"string","nullable":true,"description":"Job description"},"companyUrl":{"type":"string","nullable":true,"description":"LinkedIn company page URL"},"companyLogoUrl":{"type":"string","nullable":true,"description":"Company logo image URL"}}}},"skills":{"type":"array","nullable":true,"description":"Skills with endorsement counts (only included when includeSkills=true)","items":{"type":"object","properties":{"skillId":{"type":"string","description":"Skill ID"},"name":{"type":"string","description":"Skill name (e.g., \"JavaScript\", \"Project Management\")"},"endorsementCount":{"type":"number","description":"Number of endorsements for this skill"},"isEndorsed":{"type":"boolean","description":"Whether you have endorsed this skill"}}}},"education":{"type":"array","nullable":true,"description":"Education history (only included when includeEducation=true)","items":{"type":"object","properties":{"schoolName":{"type":"string","description":"Name of the school or university"},"degree":{"type":"string","nullable":true,"description":"Degree type (e.g., \"Bachelor\", \"Master\", \"PhD\", \"Diploma\")"},"fieldOfStudy":{"type":"string","nullable":true,"description":"Field of study or major"},"dateRange":{"type":"string","nullable":true,"description":"Date range string (e.g., \"2005 – 2008\")"},"startYear":{"type":"string","nullable":true,"description":"Start year"},"endYear":{"type":"string","nullable":true,"description":"End year (null if ongoing)"},"grade":{"type":"string","nullable":true,"description":"Grade or GPA if available"},"activities":{"type":"string","nullable":true,"description":"Activities and societies"},"description":{"type":"string","nullable":true,"description":"Education description"}}}},"cached":{"type":"boolean","description":"Whether the response was served from cache"},"cachedAt":{"type":"string","format":"date-time","description":"When the profile was cached (only if cached=true)"},"expiresAt":{"type":"string","format":"date-time","description":"When the cache expires"},"message":{"type":"string","description":"Status message"}}},"example":{"success":true,"profileId":"anandi-devi","accountId":"696ce9e780e0483585e4e553","profile":{"firstName":"Anandi","lastName":"Devi","headline":"Go-To-Market (GTM) Engineer @ ConnectSafely | MBA","location":{"countryCode":"in","postalCode":null,"geoLocationName":null},"entityUrn":"urn:li:fsd_profile:ACoAAFpqSoMB8vTqRbg4mN_wbabO8w0gjgFu-6o","publicIdentifier":"anandi-devi","profilePicture":"https://media.licdn.com/dms/image/v2/D5603AQF2Iu7GGz-H2A/profile-displayphoto-shrink_100_100/...","isPremium":true,"isVerified":true,"supportsFreeEmail":true,"connectionCount":958,"followerCount":1243,"isConnected":true,"relationshipStatus":"CONNECTED","connectionDegree":"1st","invitationSent":false,"invitationReceived":false,"geoLocation":{"city":"Delhi","state":null,"country":"India","fullLocation":"Delhi, India","birthDate":null}},"message":"Profile information retrieved successfully"}}}},"400":{"description":"Bad request - Missing or invalid profileId","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":"object","properties":{"issues":{"type":"array","items":{"type":"object"}},"name":{"type":"string"}}}}},"example":{"success":false,"error":{"issues":[{"code":"invalid_type","expected":"string","received":"undefined","path":["profileId"],"message":"Required"}],"name":"ZodError"}}}}},"401":{"description":"Unauthorized - Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unauthorized - Invalid credentials"}}}},"429":{"description":"Rate limit exceeded - Maximum 120 unique profiles per day","headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Maximum unique profiles allowed per day (120)"},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Remaining unique profile fetches for today"},"X-RateLimit-Reset":{"schema":{"type":"string","format":"date-time"},"description":"When the rate limit resets (midnight UTC)"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimitError"},"example":{"error":"Rate limit exceeded for PROFILE: 120/120 used. Resets at 2024-01-16T00:00:00.000Z","success":false}}}}}}},"/endorse-skill":{"post":{"operationId":"endorse-skill","tags":["Profile"],"summary":"Endorse a skill on a LinkedIn profile","description":"Endorse a specific skill on a LinkedIn profile, or randomly select and endorse an unendorsed skill. When random=true, the endpoint fetches all skills on the profile, filters out already-endorsed ones, and randomly endorses one.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["profileId"],"properties":{"accountId":{"type":"string","description":"LinkedIn account ID to use. If not provided, uses the default account."},"profileId":{"type":"string","minLength":1,"description":"LinkedIn profile vanity URL slug (the part after linkedin.com/in/, e.g., \"john-doe-123\")"},"skillId":{"type":"string","description":"Specific skill ID to endorse (from skills list)"},"random":{"type":"boolean","default":false,"description":"If true, randomly select an unendorsed skill to endorse"}}},"example":{"profileId":"john-doe-123","random":true}}}},"responses":{"200":{"description":"Skill endorsed successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Whether the endorsement was successful"},"profileId":{"type":"string","description":"The profile vanity URL slug"},"memberId":{"type":"string","description":"The profile member ID"},"skillId":{"type":"string","description":"The ID of the endorsed skill"},"skillName":{"type":"string","nullable":true,"description":"Name of the endorsed skill (only when random=true)"},"random":{"type":"boolean","description":"Whether a random skill was selected"},"message":{"type":"string","description":"Success message"}}},"example":{"success":true,"profileId":"john-doe-123","memberId":"ACoAAABpGQcMBI08myTal7qDJ5zb9lJiM24nFjJI","skillId":"5","skillName":"JavaScript","random":true,"message":"Successfully endorsed skill: JavaScript"}}}},"400":{"description":"Bad request - Missing skillId/random, or all skills already endorsed","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":"string"},"skills":{"type":"array","nullable":true,"description":"List of skills if all are already endorsed"}}},"example":{"success":false,"error":"Either skillId or random=true must be provided"}}}},"404":{"description":"Profile not found or no skills available","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":"string"}}},"example":{"success":false,"error":"No skills found on this profile"}}}}}}},"/organizations":{"get":{"operationId":"get-organizations","tags":["User"],"summary":"Get user organizations","description":"Fetch all LinkedIn organizations (company pages) the authenticated user has admin or posting access to. Returns organization details including URN, name, logo, follower count, and visitor count. Use the organization URN for posting content as a company or commenting as a company page.","responses":{"200":{"description":"Organizations retrieved successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Whether the request was successful"},"accountId":{"type":"string","description":"LinkedIn account ID used for the request"},"organizations":{"type":"array","items":{"$ref":"#/components/schemas/Organization"}},"count":{"type":"number","description":"Total number of organizations returned"}}},"example":{"success":true,"accountId":"696ce9e780e0483585e4e553","organizations":[{"entityUrn":"urn:li:fsd_company:105672170","name":"ConnectSafely.AI","universalName":"connectsafelyai","logoUrl":"https://media.licdn.com/dms/image/v2/D560BAQExfwnRu-WH9g/company-logo_200_200/...","coverImageUrl":"https://media.licdn.com/dms/image/v2/D4D3DAQEZladqoLpujg/image-scale_191_1128/...","followerCount":1823,"visitorsCount":500,"pageType":"COMPANY","isFollowing":true},{"entityUrn":"urn:li:fsd_company:102246628","name":"DCoderAI","universalName":"dcoderai","logoUrl":"https://media.licdn.com/dms/image/v2/D560BAQG68RrbqHxeZA/company-logo_200_200/...","coverImageUrl":"https://media.licdn.com/dms/image/v2/D563DAQGhK5ci_zeTog/image-scale_191_1128/...","followerCount":213,"visitorsCount":21,"pageType":"COMPANY","isFollowing":true}],"count":2}}}},"401":{"description":"Unauthorized - Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unauthorized - Invalid credentials"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Failed to get organizations"}}}}}}},"/organizations/{companyId}/followers":{"get":{"operationId":"get-company-followers","tags":["Profile"],"summary":"Get company/organization followers","description":"Fetch followers of a LinkedIn company page. Returns profile information for each follower including name, headline, profile picture, connection degree, and follow date. Requires admin access to the company page. Use the companyId from the organization URN (e.g., 105672170 from urn:li:fsd_company:105672170).","parameters":[{"name":"companyId","in":"path","required":true,"description":"LinkedIn company ID (numeric ID from organization URN)","schema":{"type":"string"},"example":"105672170"},{"name":"accountId","in":"query","required":false,"description":"LinkedIn account ID to use. If not provided, uses the default account.","schema":{"type":"string"}},{"name":"start","in":"query","required":false,"description":"Pagination offset (0-indexed)","schema":{"type":"number","minimum":0,"default":0}},{"name":"count","in":"query","required":false,"description":"Number of followers to return per page (max 100)","schema":{"type":"number","minimum":1,"maximum":100,"default":10}},{"name":"followerType","in":"query","required":false,"description":"Type of followers to retrieve: MEMBER (people) or PAGE (company pages)","schema":{"type":"string","enum":["MEMBER","PAGE"],"default":"MEMBER"}}],"responses":{"200":{"description":"Company followers retrieved successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Whether the request was successful"},"accountId":{"type":"string","description":"LinkedIn account ID used for the request"},"companyId":{"type":"string","description":"The company ID that was queried"},"followers":{"type":"array","items":{"$ref":"#/components/schemas/CompanyFollower"}},"paging":{"type":"object","properties":{"start":{"type":"number","description":"Current pagination offset"},"count":{"type":"number","description":"Number of results returned"},"total":{"type":"number","nullable":true,"description":"Total number of followers (if available)"}}}}},"example":{"success":true,"accountId":"696ce9e780e0483585e4e553","companyId":"105672170","followers":[{"profileUrn":"urn:li:fsd_profile:ACoAABJefVoBrz2LR3f...","publicIdentifier":"john-doe-123","firstName":"John","lastName":"Doe","headline":"Software Engineer at Tech Company","profilePictureUrl":"https://media.licdn.com/dms/image/v2/...","connectionDegree":"DISTANCE_2","followDate":"February 2026"},{"profileUrn":"urn:li:fsd_profile:ACoAABJefVoBrz3XY4g...","publicIdentifier":"jane-smith-456","firstName":"Jane","lastName":"Smith","headline":"Product Manager | B2B SaaS","profilePictureUrl":"https://media.licdn.com/dms/image/v2/...","connectionDegree":"DISTANCE_3","followDate":"February 2026"}],"paging":{"start":0,"count":10,"total":1823}}}}},"401":{"description":"Unauthorized - Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unauthorized - Invalid credentials"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Failed to fetch company followers"}}}}}}},"/organizations/{companyId}/follow-invitations":{"post":{"operationId":"send-company-follow-invitations","tags":["Profile"],"summary":"Send company follow invitations","description":"Send invitations to LinkedIn users to follow a company page. This allows company page administrators to invite people to follow their company. Requires Super Admin access to the company page. Maximum 50 invitations per request.","parameters":[{"name":"companyId","in":"path","required":true,"description":"LinkedIn company ID (numeric ID from organization URN)","schema":{"type":"string"},"example":"105672170"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["profileUrns"],"properties":{"accountId":{"type":"string","description":"LinkedIn account ID to use. If not provided, uses the default account."},"companyId":{"type":"string","description":"LinkedIn company ID (can also be specified in path)"},"profileUrns":{"type":"array","items":{"type":"string"},"minItems":1,"maxItems":50,"description":"Array of LinkedIn profile URNs to invite (max 50 per request)"}}},"example":{"companyId":"105672170","profileUrns":["urn:li:fsd_profile:ACoAABJefVoBrz2LR3f...","urn:li:fsd_profile:ACoAABJefVoBrz3XY4g..."]}}}},"responses":{"200":{"description":"Invitations processed (check individual results for success/failure)","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Whether at least one invitation was successful"},"accountId":{"type":"string","description":"LinkedIn account ID used for the request"},"companyId":{"type":"string","description":"The company ID for which invitations were sent"},"totalRequested":{"type":"number","description":"Total number of invitations requested"},"totalSuccessful":{"type":"number","description":"Number of successful invitations"},"totalFailed":{"type":"number","description":"Number of failed invitations"},"results":{"type":"array","items":{"$ref":"#/components/schemas/CompanyFollowInvitationResult"},"description":"Individual results for each invitation"}}},"example":{"success":true,"accountId":"696ce9e780e0483585e4e553","companyId":"105672170","totalRequested":2,"totalSuccessful":2,"totalFailed":0,"results":[{"profileUrn":"urn:li:fsd_profile:ACoAABJefVoBrz2LR3f...","success":true,"status":"SENT"},{"profileUrn":"urn:li:fsd_profile:ACoAABJefVoBrz3XY4g...","success":true,"status":"SENT"}]}}}},"401":{"description":"Unauthorized - Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unauthorized - Invalid credentials"}}}},"403":{"description":"Forbidden - LinkedIn account does not have Super Admin access to the company page","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Request failed with status code 403"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Failed to send company follow invitations"}}}}}}},"/groups/members":{"post":{"operationId":"get-group-members","tags":["Groups"],"summary":"Get group members by group ID","description":"Retrieve members of a LinkedIn group by its ID. Returns profile information for group members including name, headline, profile picture, and membership status. Useful for lead generation, networking, and community analysis. Requires membership in the group to access member list. **Rate limit: 1000 members per day per LinkedIn account.**","x-rateLimit":{"limit":1000,"period":"day","scope":"linkedinAccount","description":"1000 group members per day per LinkedIn account. The count is based on total members fetched across all requests."},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["groupId"],"properties":{"accountId":{"type":"string","description":"LinkedIn account ID to use. If not provided, uses the default account."},"groupId":{"type":"string","description":"LinkedIn group ID (numeric ID from group URL)"},"count":{"type":"number","minimum":1,"maximum":100,"default":50,"description":"Number of members to return per page (max 100)"},"start":{"type":"number","minimum":0,"default":0,"description":"Pagination offset (0-indexed)"},"membershipStatuses":{"type":"array","items":{"type":"string","enum":["OWNER","MANAGER","MEMBER"]},"default":["OWNER","MANAGER","MEMBER"],"description":"Filter by membership status: OWNER (group owners), MANAGER (group admins), MEMBER (regular members)"},"typeaheadQuery":{"type":"string","default":"","description":"Search query to filter members by name"}}}}}},"responses":{"200":{"description":"Group members retrieved successfully","headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Daily rate limit (1000)"},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Remaining requests for the day"},"X-RateLimit-Reset":{"schema":{"type":"string","format":"date-time"},"description":"When the rate limit resets (midnight UTC)"}},"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"accountId":{"type":"string","description":"LinkedIn account ID used for the request"},"groupId":{"type":"string","description":"The group ID that was queried"},"members":{"type":"array","items":{"type":"object","properties":{"entityUrn":{"type":"string","description":"LinkedIn entity URN for the member"},"publicIdentifier":{"type":"string","description":"LinkedIn public identifier (vanity name)"},"firstName":{"type":"string"},"lastName":{"type":"string"},"headline":{"type":"string"},"profilePicture":{"type":"string","nullable":true,"description":"URL to profile picture"},"membershipStatus":{"type":"string","enum":["OWNER","MANAGER","MEMBER"],"description":"Role in the group"}}}},"pagination":{"type":"object","properties":{"start":{"type":"number"},"count":{"type":"number"},"total":{"type":"number"}}},"hasMore":{"type":"boolean","description":"Whether more members are available"},"count":{"type":"number","description":"Number of members returned in this response"}}}}}},"400":{"description":"Bad request - invalid group ID or parameters","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"success":{"type":"boolean","example":false}}}}}},"429":{"description":"Rate limit exceeded - 1000 members per day","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","example":"Rate limit exceeded for GROUP_MEMBERS: 1000/1000 used. Resets at 2024-01-02T00:00:00.000Z"},"success":{"type":"boolean","example":false}}}}}}}}},"/groups/members-by-url":{"post":{"operationId":"get-group-members-by-url","tags":["Groups"],"summary":"Get group members by group URL","description":"Retrieve members of a LinkedIn group using the group URL. Automatically extracts the group ID from the URL. Convenient alternative to /groups/members when you have the full URL instead of just the ID. **Rate limit: 1000 members per day per LinkedIn account.**","x-rateLimit":{"limit":1000,"period":"day","scope":"linkedinAccount","description":"1000 group members per day per LinkedIn account. The count is based on total members fetched across all requests."},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["groupUrl"],"properties":{"accountId":{"type":"string","description":"LinkedIn account ID to use. If not provided, uses the default account."},"groupUrl":{"type":"string","format":"uri","description":"Full LinkedIn group URL (e.g., \"https://www.linkedin.com/groups/12345\")"},"count":{"type":"number","minimum":1,"maximum":100,"default":50,"description":"Number of members to return per page (max 100)"},"start":{"type":"number","minimum":0,"default":0,"description":"Pagination offset (0-indexed)"},"membershipStatuses":{"type":"array","items":{"type":"string","enum":["OWNER","MANAGER","MEMBER"]},"default":["OWNER","MANAGER","MEMBER"],"description":"Filter by membership status: OWNER (group owners), MANAGER (group admins), MEMBER (regular members)"},"typeaheadQuery":{"type":"string","default":"","description":"Search query to filter members by name"}}}}}},"responses":{"200":{"description":"Group members retrieved successfully","headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Daily rate limit (1000)"},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Remaining requests for the day"},"X-RateLimit-Reset":{"schema":{"type":"string","format":"date-time"},"description":"When the rate limit resets (midnight UTC)"}},"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"accountId":{"type":"string","description":"LinkedIn account ID used for the request"},"groupUrl":{"type":"string","description":"The original group URL provided"},"groupId":{"type":"string","description":"The extracted group ID"},"members":{"type":"array","items":{"type":"object","properties":{"entityUrn":{"type":"string","description":"LinkedIn entity URN for the member"},"publicIdentifier":{"type":"string","description":"LinkedIn public identifier (vanity name)"},"firstName":{"type":"string"},"lastName":{"type":"string"},"headline":{"type":"string"},"profilePicture":{"type":"string","nullable":true,"description":"URL to profile picture"},"membershipStatus":{"type":"string","enum":["OWNER","MANAGER","MEMBER"],"description":"Role in the group"}}}},"pagination":{"type":"object","properties":{"start":{"type":"number"},"count":{"type":"number"},"total":{"type":"number"}}},"hasMore":{"type":"boolean","description":"Whether more members are available"},"count":{"type":"number","description":"Number of members returned in this response"}}}}}},"400":{"description":"Bad request - invalid group URL or could not extract group ID","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","example":"Invalid LinkedIn group URL - could not extract group ID"},"success":{"type":"boolean","example":false}}}}}},"429":{"description":"Rate limit exceeded - 1000 members per day","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","example":"Rate limit exceeded for GROUP_MEMBERS: 1000/1000 used. Resets at 2024-01-02T00:00:00.000Z"},"success":{"type":"boolean","example":false}}}}}}}}},"/messaging/check-email-support":{"post":{"operationId":"check-email-support","tags":["Messaging"],"summary":"Check if profile supports email messaging","description":"Check whether a LinkedIn profile has email messaging enabled (open profile). Some LinkedIn members allow direct email contact through their profile. Use this to verify before attempting email outreach.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"accountId":{"type":"string","description":"LinkedIn account ID to use. If not provided, uses the default account."},"profileId":{"type":"string","description":"LinkedIn profile vanity URL slug (e.g., \"john-doe-123\"). Prefer profileUrn when available."},"profileUrn":{"type":"string","description":"LinkedIn profile URN (e.g., \"urn:li:fsd_profile:ACoAAFpqSoMB8vTqRbg4mN...\"). Preferred over profileId."}}},"example":{"profileUrn":"urn:li:fsd_profile:ACoAAFpqSoMB8vTqRbg4mN_wbabO8w0gjgFu-6o"}}}},"responses":{"200":{"description":"Email support status retrieved successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"accountId":{"type":"string"},"profileId":{"type":"string","nullable":true},"profileUrn":{"type":"string"},"supportsFreeEmail":{"type":"boolean"},"message":{"type":"string"}}},"example":{"success":true,"accountId":"696ce9e780e0483585e4e553","profileId":null,"profileUrn":"urn:li:fsd_profile:ACoAAFpqSoMB8vTqRbg4mN_wbabO8w0gjgFu-6o","supportsFreeEmail":true,"message":"Profile supports free email messaging (open profile)"}}}},"400":{"description":"Bad request - Must provide profileId or profileUrn","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"},"example":{"success":false,"error":"Either profileId or profileUrn is required"}}}},"401":{"description":"Unauthorized - Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnauthorizedError"},"example":{"error":"Unauthorized - Invalid credentials"}}}},"500":{"description":"Server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServerError"},"example":{"error":"Failed to get LinkedIn authentication credentials","success":false}}}}}}},"/relationship/{profileId}":{"get":{"operationId":"check-relationship","tags":["Relationships"],"summary":"Check relationship status with profile","description":"Check the relationship between your default LinkedIn account and a target profile. Useful for determining which actions are available (message, connect, etc.).\n\n`status` is one of four mutually exclusive states:\n\n| status | meaning | what you can do |\n|---|---|---|\n| `CONNECTED` | 1st-degree connection | message them |\n| `INVITATION_SENT` | you invited them, still pending | withdraw via `POST /invitations/withdraw` |\n| `INVITATION_RECEIVED` | **they invited you, waiting on you** | accept/ignore via `POST /invitations/received/respond` |\n| `NOT_CONNECTED` | no relationship | invite via `POST /connections/invite` |\n\n`INVITATION_RECEIVED` is NOT a connection: the person is still 2nd/3rd degree and cannot be messaged. Check `status`, not just `connected`.","parameters":[{"name":"profileId","in":"path","required":true,"schema":{"type":"string"},"description":"LinkedIn profile vanity URL slug (the part after linkedin.com/in/, e.g., \"john-doe-123\")"}],"responses":{"200":{"description":"Relationship status retrieved successfully","content":{"application/json":{"schema":{"type":"object","properties":{"connected":{"type":"boolean","description":"Whether you are 1st-degree connected. False while an invitation in either direction is still pending."},"invitationSent":{"type":"boolean","description":"You invited them and they have not responded"},"invitationReceived":{"type":"boolean","description":"They invited YOU and you have not responded. They are not a connection yet and cannot be messaged."},"status":{"type":"string","enum":["CONNECTED","INVITATION_SENT","INVITATION_RECEIVED","NOT_CONNECTED"],"description":"Connection status — exactly one of the four states; see the endpoint description."},"profileUrn":{"type":"string","description":"LinkedIn profile URN of the target user"},"accountId":{"type":"string","description":"LinkedIn account ID used for the check"}}},"example":{"connected":false,"invitationSent":false,"invitationReceived":false,"status":"NOT_CONNECTED","profileUrn":"urn:li:fsd_profile:ACoAAA24A-MBVEvT49xpVF2gnWrhvmUIPDJshSM","accountId":"acc_12345"}}}},"404":{"description":"Profile not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"success":{"type":"boolean"}}},"example":{"error":"LinkedIn profile not found: invalid-profile-id","success":false}}}}}}},"/relationship/{accountId}/{profileId}":{"get":{"operationId":"check-relationship-specific-account","tags":["Relationships"],"summary":"Check relationship status with specific account","description":"Check the relationship between a specific LinkedIn account and a target profile. Useful for multi-account setups to check relationships from different accounts. Returns connection degree, follow status, and connection status.","parameters":[{"name":"accountId","in":"path","required":true,"schema":{"type":"string"},"description":"Unique identifier for the LinkedIn account to check from"},{"name":"profileId","in":"path","required":true,"schema":{"type":"string"},"description":"Target LinkedIn profile vanity URL slug (the part after linkedin.com/in/, e.g., \"john-doe-123\")"}],"responses":{"200":{"description":"Relationship status retrieved successfully","content":{"application/json":{"schema":{"type":"object","properties":{"connected":{"type":"boolean","description":"Whether you are 1st-degree connected. False while an invitation in either direction is still pending."},"invitationSent":{"type":"boolean","description":"You invited them and they have not responded"},"invitationReceived":{"type":"boolean","description":"They invited YOU and you have not responded. They are not a connection yet and cannot be messaged."},"status":{"type":"string","enum":["CONNECTED","INVITATION_SENT","INVITATION_RECEIVED","NOT_CONNECTED"],"description":"Connection status — exactly one of the four states; see the endpoint description."},"profileUrn":{"type":"string","description":"LinkedIn profile URN of the target user"},"accountId":{"type":"string","description":"LinkedIn account ID used for the check"}}},"example":{"connected":false,"invitationSent":false,"invitationReceived":false,"status":"NOT_CONNECTED","profileUrn":"urn:li:fsd_profile:ACoAAA24A-MBVEvT49xpVF2gnWrhvmUIPDJshSM","accountId":"acc_12345"}}}},"404":{"description":"Profile not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"success":{"type":"boolean"}}},"example":{"error":"LinkedIn profile not found: invalid-profile-id","success":false}}}}}}},"/reply":{"post":{"operationId":"reply-to-comment","tags":["Posts"],"summary":"Reply to a comment","description":"Post a reply to an existing comment on a LinkedIn post. Replies appear threaded under the original comment. Optionally tag a member with a real @mention that notifies them (e.g. the original commenter) by passing `mention`. Useful for continuing conversations and engaging with community discussions.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["commentId","reply"],"properties":{"accountId":{"type":"string","description":"LinkedIn account ID to use. If not provided, uses the default account."},"commentId":{"type":"string","description":"ID of the comment to reply to (from /posts/comments response)"},"reply":{"type":"string","description":"Reply text content. Supports hashtags. When `mention` is provided, place a {{user}} placeholder where the tag should appear — otherwise the tagged member's name is prepended.","example":"Thanks for the thoughtful comment, {{user}} — totally agree!"},"mention":{"type":"object","description":"Optional @mention to tag a member in the reply. Produces a real LinkedIn tag that notifies the member (e.g. tag the original commenter). The visible tag replaces the {{user}} placeholder in `reply` (or is prepended if no placeholder is present).","required":["name","profileUrn"],"properties":{"name":{"type":"string","description":"Display name of the member to tag (used as the visible tag text).","example":"Jane Smith"},"profileUrn":{"type":"string","description":"LinkedIn profile URN of the member to tag — accepts a full \"urn:li:fsd_profile:ACoAA...\" URN or the bare \"ACoAA...\" member id.","example":"urn:li:fsd_profile:ACoAABcdefgh"}},"example":{"name":"Jane Smith","profileUrn":"urn:li:fsd_profile:ACoAABcdefgh"}}}}}}},"responses":{"200":{"description":"Reply posted successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"replyId":{"type":"string"}}}}}}}}},"/like-comment":{"post":{"operationId":"like-comment","tags":["Posts"],"summary":"Like a comment","description":"Like an existing comment on a LinkedIn post. Sends a pre-signal to LinkedIn before the actual like action for realistic engagement simulation.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["commentId"],"properties":{"accountId":{"type":"string","description":"LinkedIn account ID to use. If not provided, uses the default account."},"commentId":{"type":"string","description":"ID of the comment to like (same format as used in reply endpoint, from /posts/comments response)"},"postUrl":{"type":"string","description":"Optional post URL for referer header (helps with browser simulation)"},"companyUrn":{"type":"string","description":"Optional company URN if liking as a company page"}}},"example":{"commentId":"7441783492948054016","postUrl":"https://www.linkedin.com/feed/update/urn:li:activity:7441028442856251392"}}}},"responses":{"200":{"description":"Comment liked successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"commentId":{"type":"string"},"originalCommentId":{"type":"string"},"accountId":{"type":"string"}}},"example":{"success":true,"message":"Successfully liked comment","commentId":"urn:li:comment:(activity:7441028442856251392,7441783492948054016)","originalCommentId":"7441783492948054016","accountId":"acc_123"}}}},"400":{"description":"Invalid comment URN format","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimitError"}}}}}}},"/search/jobs":{"post":{"operationId":"search-jobs","tags":["Search"],"summary":"Search LinkedIn jobs","description":"Search for job postings on LinkedIn with various filters. Supports pagination and multiple filter criteria including location, employment type, experience level, and more.\n\n**Filters changed when LinkedIn rebuilt its job search.** `industry`, `locationId`, `geoUrn` and `jobType` are rejected with a 400 rather than ignored — LinkedIn no longer offers those filters. Use `employmentType`, `experienceLevel`, `geoId`, and `segmentIds` (filter-pill ids, e.g. `\"225001:272001\"` for Remote).\n\n| Old filter | Use instead |\n| --- | --- |\n| `jobType: [\"F\"]` | `employmentType: [\"full-time\"]` |\n| `experienceLevel: [\"4\",\"5\",\"6\"]` | `experienceLevel: [\"senior\",\"director\",\"executive\"]` |\n| `workplaceType: [\"2\"]` (Remote) | `workplaceType: [\"remote\"]` |\n| `industry` | `segmentIds` (the ids LinkedIn offers vary by keyword) |\n| `locationId` / `geoUrn` | `geoId` |\n\n**`workplaceType` is applied to the returned cards, not by LinkedIn.** LinkedIn honours the Remote segment, but when the fully-filtered pool is thin it BACKFILLS the page with rows that do not match and still answers 200. Measured on one account with keywords \"VP of Marketing\" and the Remote segment held constant: alone 10/10 remote, plus `experienceLevel` 9/10, plus `past-month` 8/10, plus `past-week` 5/10, plus `past-24h` 5/10, plus `past-24h` and `experienceLevel` 4/10. It tracks how narrow the filter set is, not the keyword, so no segment id avoids it. Filtering on each card's own pill is therefore the only reliable option, with two consequences: a page may return fewer than `count` rows (check `hasMore` and page on), and jobs LinkedIn labels with no workplace pill are excluded rather than assumed.\n\n**Rate limit:** 1,000 search calls per account per day (resets at midnight UTC), and 30 calls per minute. One request counts as one call regardless of `count`.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"accountId":{"type":"string","description":"LinkedIn account ID to use for the search. If not provided, uses the default account."},"keywords":{"type":"string","default":"","description":"Search keywords for job title, company, or description"},"count":{"type":"number","minimum":1,"maximum":100,"default":25,"description":"Number of results to return per page"},"start":{"type":"number","minimum":0,"default":0,"description":"Pagination offset (0-indexed)"},"filters":{"type":"object","description":"Optional filters to narrow down search results","properties":{"datePosted":{"type":"string","enum":["past-24h","past-week","past-month","any-time"],"description":"Filter by when the job was posted. `any-time` means no date filter and is accepted for backward compatibility."},"companyIds":{"type":"array","items":{"type":"string"},"description":"Filter by specific company IDs (use /search/companies to find IDs)"},"easyApply":{"type":"boolean","description":"Filter for Easy Apply jobs only"},"earlyApplicant":{"type":"boolean","description":"Filter for postings with under 10 applicants"},"inYourNetwork":{"type":"boolean","description":"Filter for jobs at companies where you have connections"},"employmentType":{"type":"array","items":{"type":"string","enum":["full-time","part-time","contract","internship","volunteer"]},"description":"Employment type. Replaces the old `jobType` codes; LinkedIn dropped Temporary and Other."},"experienceLevel":{"type":"array","items":{"type":"string","enum":["entry","senior","manager","director","executive"]},"description":"Experience level. Replaces the old numeric codes; LinkedIn dropped Associate and Mid-Senior and added Senior and Manager."},"workplaceType":{"type":"array","items":{"type":"string","enum":["remote","hybrid","on-site"]},"description":"Workplace arrangement. Applied to the returned cards rather than sent to LinkedIn, which has no such facet — so a page can come back shorter than `count`, and jobs with no workplace pill are excluded."},"segmentIds":{"type":"array","items":{"type":"string"},"description":"Raw `f_SAL` segment ids for filters with no named field. Ids are `group:value`, and the groups are stable LinkedIn taxonomy: `225001` workplace (Remote = `225001:272001`), `226001` employment type, `227001` experience level — the last two are already covered by `employmentType` and `experienceLevel`. Topical groups (e.g. SaaS `230001:285004`) do vary by keyword. Note LinkedIn backfills a thin result set with non-matching rows, so a segment narrows results without guaranteeing them; prefer `workplaceType`, which is enforced on the returned cards."},"geoId":{"type":"string","description":"LinkedIn geo location ID (use /search/geo to find IDs), e.g. \"102713980\" for India. Replaces `locationId`/`geoUrn`. Omitting it does NOT mean \"anywhere\" — LinkedIn anchors the search to the account holder's own profile location, so pass a geoId whenever location matters."},"distance":{"type":"string","description":"Search radius in miles around geoId, e.g. \"25\". Format is UNCONFIRMED — no browser capture has ever set one, so treat a radius as best-effort."}}}}}}}},"responses":{"200":{"description":"Jobs retrieved successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"jobs":{"type":"array","items":{"$ref":"#/components/schemas/JobSearchResult"}},"pagination":{"$ref":"#/components/schemas/JobSearchPagination"},"hasMore":{"type":"boolean","description":"Whether more results are available"}}},"example":{"success":true,"jobs":[{"jobId":"4367156030","title":"Founding Software Engineer - AI and Backend","companyName":"Dexicon","companyLogo":"https://media.licdn.com/dms/image/v2/D560BAQ.../company-logo_100_100/...","location":"Bengaluru, Karnataka, India","isRemote":true,"isHybrid":false,"postedDate":"Posted 4 days ago","jobUrl":"https://www.linkedin.com/jobs/view/4367156030/","easyApply":false},{"jobId":"4321502503","title":"Software Engineer (backend)","companyName":"Kodo","companyLogo":"https://media.licdn.com/dms/image/v2/C4D0BAQ.../company-logo_100_100/...","location":"Mumbai Metropolitan Region","isRemote":false,"isHybrid":false,"postedDate":"Posted 2 days ago","salary":"30K INR/month - 45K INR/month","jobUrl":"https://www.linkedin.com/jobs/view/4321502503/","easyApply":true}],"pagination":{"count":25,"start":0},"hasMore":true}}}},"400":{"description":"Bad Request - Invalid request parameters, or a filter LinkedIn no longer supports. Retired filters are rejected rather than ignored, so a request never silently returns unfiltered results.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"},"example":{"issues":[{"received":"4","code":"invalid_enum_value","options":["entry","senior","manager","director","executive"],"path":["filters","experienceLevel",0],"message":"Invalid enum value. Expected 'entry' | 'senior' | 'manager' | 'director' | 'executive', received '4'"},{"code":"custom","message":"'industry' has no equivalent — the flagship SRP exposes industries only as per-query pills; use segmentIds","path":["filters","industry"]},{"code":"custom","message":"'jobType' has no equivalent — renamed to employmentType with values full-time|part-time|contract|internship|volunteer","path":["filters","jobType"]}],"name":"ZodError"}}}},"401":{"description":"Unauthorized - Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unauthorized - Invalid credentials"}}}},"429":{"description":"Rate limit exceeded - the account has spent its monthly job-search budget","headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Maximum searches allowed"},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Remaining searches (0 when exceeded)"},"X-RateLimit-Reset":{"schema":{"type":"string","format":"date-time"},"description":"When the rate limit resets"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimitError"},"example":{"error":"Rate limit exceeded for SEARCH_JOBS: 300/300 used. Resets at 2026-09-01T00:00:00.000Z","success":false}}}},"500":{"description":"Internal Server Error - Failed to get LinkedIn credentials or search failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Failed to search jobs","success":false}}}}}}},"/search/companies":{"post":{"operationId":"search-companies","tags":["Search"],"summary":"Search LinkedIn companies","description":"Search for companies on LinkedIn by keywords and filters. Filter by headquarters location, industry, and company size. Useful for lead generation, market research, and finding potential business partners.\n\nHandles LinkedIn's page-based pagination internally: ask for the total `count` you want and the API walks as many ~10-result pages as it takes (max 11).\n\n`filters.companyType` and `filters.followedCompanies` are still accepted but **no longer narrow the result set** — LinkedIn's current company search offers no such facet.\n\n**Sales Navigator Support:** Pass a Sales Navigator company search URL in the `url` parameter to search using Sales Navigator filters (revenue, employees, etc.). **Requires Sales Navigator license on the LinkedIn account.**\n\n**Rate limit:** 1,000 search calls per account per day (resets at midnight UTC), and 30 calls per minute. Pagination is billed per page walked, so a `count` of 25 spends about 3 calls.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"accountId":{"type":"string","description":"LinkedIn account ID to use for the search. If not provided, uses the default account."},"keywords":{"type":"string","default":"","description":"Search keywords for company name or description (used for regular LinkedIn search)"},"url":{"type":"string","format":"uri","description":"Sales Navigator company search URL (e.g., https://www.linkedin.com/sales/search/company?query=...). **Requires Sales Navigator license.** When provided, uses Sales Navigator API with advanced filters like revenue and employee count."},"count":{"type":"number","minimum":1,"maximum":100,"default":25,"description":"Total number of results to return. Pages are walked internally to reach it — see the rate-limit note."},"start":{"type":"number","minimum":0,"default":0,"description":"Row offset to start from (0-indexed). Need not land on a page boundary."},"filters":{"type":"object","description":"Optional filters to narrow down search results","properties":{"locationId":{"oneOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Company HQ geo location ID(s). Use /search/geo to find IDs."},"industry":{"type":"array","items":{"type":"string"},"description":"LinkedIn industry IDs to filter by"},"companySize":{"type":"array","items":{"type":"string","enum":["A","B","C","D","E","F","G","H","I"]},"description":"Company sizes: A=1, B=2-10, C=11-50, D=51-200, E=201-500, F=501-1000, G=1001-5000, H=5001-10000, I=10001+"},"companyType":{"type":"array","items":{"type":"string"},"description":"**Not applied.** LinkedIn's current company search offers no company-type facet. Still accepted so existing requests keep working, but it does not narrow the results."},"followedCompanies":{"type":"boolean","description":"**Not applied.** LinkedIn's current company search offers no followed-companies facet. Still accepted so existing requests keep working, but it does not narrow the results."}}}}}}}},"responses":{"200":{"description":"Companies retrieved successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"companies":{"type":"array","items":{"$ref":"#/components/schemas/CompanySearchResult"}},"pagination":{"$ref":"#/components/schemas/CompanySearchPagination"},"hasMore":{"type":"boolean","description":"Whether more results are available"}}},"example":{"success":true,"companies":[{"companyId":"1441","name":"Google","universalName":"google","headline":"Software Development • Mountain View, CA","description":"A problem isn't truly solved until it's solved for all.","logoUrl":"https://media.licdn.com/dms/image/.../google_logo","industry":"Software Development","followerCount":41000000,"companyUrl":"https://www.linkedin.com/company/google/","location":"Mountain View, CA"},{"companyId":"1594050","name":"Google DeepMind","universalName":"googledeepmind","headline":"Research Services • London, London","description":"We're committed to solving intelligence.","logoUrl":"https://media.licdn.com/dms/image/.../googledeepmind_logo","industry":"Research Services","followerCount":1000000,"companyUrl":"https://www.linkedin.com/company/googledeepmind/","location":"London, London"}],"pagination":{"count":25,"start":0},"hasMore":true}}}},"400":{"description":"Bad Request - Invalid request parameters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}},"401":{"description":"Unauthorized - Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unauthorized - Invalid credentials"}}}},"500":{"description":"Internal Server Error - Failed to get LinkedIn credentials or search failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Failed to search companies","success":false}}}}}}},"/search/groups":{"post":{"operationId":"search-groups","tags":["Search"],"summary":"Search LinkedIn groups","description":"Search for LinkedIn groups by keywords. Groups are communities where professionals discuss industry topics, share insights, and network.\n\nLinkedIn's group search offers a keyword box and nothing else — there are **no filters** — so `filters.memberCount` is accepted for backward compatibility but never applied; sort/filter the returned `memberCount` client-side instead.\n\nResults are paged internally to satisfy `count`. Page sizes vary (8-10 rows), so a short page is not the end of the result set — use `hasMore`, never the length of the returned array, to decide whether to keep paging.\n\n**Rate limit:** no per-account search quota is enforced on this endpoint — only the general 30-calls-per-minute velocity limit.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"accountId":{"type":"string","description":"LinkedIn account ID to use for the search. If not provided, uses the default account."},"keywords":{"type":"string","default":"","description":"Search keywords for group name or description"},"count":{"type":"number","minimum":1,"maximum":100,"default":25,"description":"Number of results to return per page"},"start":{"type":"number","minimum":0,"default":0,"description":"Pagination offset (0-indexed)"},"filters":{"type":"object","description":"Optional filters to narrow down search results","properties":{"memberCount":{"type":"string","enum":["small","medium","large"],"description":"Accepted but **not applied** — LinkedIn's group search screen has no size facet. Kept so existing callers do not break; filter the returned `memberCount` yourself."}}}}}}}},"responses":{"200":{"description":"Groups retrieved successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"groups":{"type":"array","items":{"$ref":"#/components/schemas/GroupSearchResult"}},"pagination":{"$ref":"#/components/schemas/GroupSearchPagination"},"hasMore":{"type":"boolean","description":"Whether more results are available past this batch. Derived by fetching one row beyond the requested `count`, so it is exact — not a guess from the page being full."}}},"example":{"success":true,"groups":[{"groupId":"9121382","name":"AI & GTM (News, Jobs, Tools and everything in between)","memberCount":1000,"logoUrl":"https://media.licdn.com/dms/image/.../group-logo","groupUrl":"https://www.linkedin.com/groups/9121382/","isPrivate":false},{"groupId":"8645802","name":"GTM AI","memberCount":813,"logoUrl":"https://media.licdn.com/dms/image/.../group-logo","groupUrl":"https://www.linkedin.com/groups/8645802/","isPrivate":true}],"pagination":{"count":10,"start":0,"total":580},"hasMore":true}}}},"400":{"description":"Bad Request - Invalid request parameters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}},"401":{"description":"Unauthorized - Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unauthorized - Invalid credentials"}}}},"500":{"description":"Internal Server Error - Failed to get LinkedIn credentials or search failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Failed to search groups","success":false}}}}}}},"/search/people":{"post":{"operationId":"search-people","tags":["Search"],"summary":"Search LinkedIn people","description":"Search for LinkedIn members/professionals with extensive filtering options. Ideal for recruiting, sales prospecting, and networking. Filter by name, job title, company, location, connection degree, and more.\n\nSupports Sales Navigator search URLs — pass a Sales Navigator search URL in the `url` field to use Sales Navigator Lead Search instead of regular LinkedIn search.\n\n### Filtering by location\n\nTwo steps: look the location up with `POST /search/geo`, then pass the returned `geoId` as `filters.locationId`.\n\n1. `POST /search/geo` with `{ \"keywords\": \"Ohio\" }` returns `{ \"geoId\": \"106981407\", \"name\": \"Ohio, United States\" }`.\n2. `POST /search/people` with that id:\n\n```json\n{\n  \"accountId\": \"<your-account-id>\",\n  \"keywords\": \"Mechanical\",\n  \"count\": 10,\n  \"filters\": { \"locationId\": \"106981407\" }\n}\n```\n\nNotes:\n- Use `filters.locationId` (or its alias `filters.geoUrn`) — there is **no** `filters.location` key. Unknown filter keys are silently ignored and the search runs unfiltered (global results).\n- Pass the plain numeric `geoId`. The `urn:li:fsd_geo:<id>` form is also accepted and normalized.\n- Any geo level works: country, state, metro area, county, or city. A narrower id gives a narrower result set.\n- Location filtering composes with `title`, `industry`, `connectionDegree` and pagination (`start`).\n\n**Recommended:** For `connectionOf` or `followerOf` filters, prefer `search-people-v2` which natively supports these filters and returns more accurate results.\n\n**Rate limit:** 1,000 search calls per account per day (resets at midnight UTC), and 30 calls per minute. Pagination is billed per page walked, so a `count` of 25 spends about 3 calls.\n\nSeparately, LinkedIn applies its own **commercial use limit** to people search: roughly 100 searches a month on a free account, ~300 on Premium Career, ~500 on Premium Business, and effectively unlimited with Sales Navigator. That ceiling belongs to LinkedIn, is computed from search and browsing history rather than published, resets on the 1st of the month, and is **not** enforced or reported by this API. Maximum 1000 results per search.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"accountId":{"type":"string","description":"LinkedIn account ID to use for the search. If not provided, uses the default account."},"keywords":{"type":"string","default":"","description":"Search keywords for name, title, company, or skills"},"count":{"type":"number","minimum":1,"maximum":100,"default":25,"description":"Number of results to return per page"},"start":{"type":"number","minimum":0,"default":0,"description":"Pagination offset (0-indexed)"},"url":{"type":"string","format":"uri","description":"Sales Navigator search URL. When provided, the search will use Sales Navigator Lead Search instead of regular LinkedIn search. The URL should contain `/sales/search/` path (e.g., https://www.linkedin.com/sales/search/people?query=...).","example":"https://www.linkedin.com/sales/search/people?query=(filters:List(...))"},"filters":{"type":"object","description":"Optional filters to narrow down search results","properties":{"firstName":{"type":"string","description":"Filter by first name"},"lastName":{"type":"string","description":"Filter by last name"},"title":{"type":"string","description":"Filter by current job title"},"company":{"type":"string","description":"Filter by current company name"},"school":{"type":"string","description":"Filter by education/school attended"},"locationId":{"type":"string","description":"Geo location filter. Pass the plain numeric `geoId` returned by POST /search/geo (the `urn:li:fsd_geo:<id>` form is also accepted). Works at any level — country, state, metro, county or city. Note: the key is `locationId`, not `location`.","example":"106981407"},"geoUrn":{"type":"string","description":"Alias for `locationId` — same geo id, same behaviour. Set one or the other, not both.","example":"106981407"},"industry":{"type":"array","items":{"type":"string"},"description":"LinkedIn industry IDs to filter by"},"connectionDegree":{"type":"array","items":{"type":"string","enum":["F","S","O"]},"description":"Filter by connection degree. F = 1st degree connections (direct contacts), S = 2nd degree connections (friends of friends), O = 3rd+ degree connections (everyone else). Combine multiple values to broaden results, e.g. [\"F\", \"S\"] for 1st and 2nd degree.","example":["F","S"]},"currentCompanyIds":{"type":"array","items":{"type":"string"},"description":"Filter by current employer company IDs"},"pastCompanyIds":{"type":"array","items":{"type":"string"},"description":"Filter by past employer company IDs"},"schoolIds":{"type":"array","items":{"type":"string"},"description":"Filter by school IDs. Look one up with `POST /search/schools`, which returns the `schoolId` this filter expects.","example":["1792"]},"profileLanguage":{"type":"array","items":{"type":"string"},"description":"Filter by profile language codes (e.g., en, es, fr)"},"serviceCategories":{"type":"array","items":{"type":"string"},"description":"Filter by service provider category ids. Look them up with `POST /search/service-categories` — the ids are opaque (Financial Analysis is `826`) and an unrecognised one returns 200 with zero results rather than an error.","example":["826"]},"openToVolunteer":{"type":"boolean","description":"Filter to only show people open to volunteering. Replaces the former `openToWork` filter — LinkedIn's current people search has no open-to-work facet, so `openToWork` now returns 400 rather than being silently ignored."},"connectionOf":{"type":"array","items":{"type":"string"},"description":"Find people connected to specific profile IDs (ACoAA format or vanity names)"},"followerOf":{"type":"array","items":{"type":"string"},"description":"Find people who follow specific profile or company URNs"}}}}}}}},"responses":{"200":{"description":"People retrieved successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"people":{"type":"array","items":{"$ref":"#/components/schemas/PeopleSearchResult"}},"pagination":{"$ref":"#/components/schemas/PeopleSearchPagination"},"hasMore":{"type":"boolean","description":"Whether more results are available past this batch. Derived by fetching one row beyond the requested `count`, so it is exact — not a guess from the page being full."}}},"example":{"success":true,"people":[{"profileId":"abhishek-onkar","profileUrn":"urn:li:fsd_profile:ACoAABdttMcBYTOrkGpTYrhL6waE6Tu8UhpZdeo","firstName":"Abhishek","lastName":"Onkar","headline":"Software Engineer @ Google","profilePicture":"https://media.licdn.com/dms/image/.../profile-displayphoto","location":"Bengaluru","connectionDegree":"2nd","currentPosition":"Software Engineer @ Google","profileUrl":"https://www.linkedin.com/in/abhishek-onkar/","isPremium":false,"isOpenToWork":false},{"profileId":"sheetal-lalwani-0601","profileUrn":"urn:li:fsd_profile:ACoAAC11JdABZvl_riyT7he7WnF3OXXr6THQ274","firstName":"Sheetal","lastName":"Lalwani","headline":"Software Engineer at Microsoft","profilePicture":"https://media.licdn.com/dms/image/.../profile-displayphoto","location":"India","connectionDegree":"2nd","currentPosition":"Software Engineer at Microsoft","profileUrl":"https://www.linkedin.com/in/sheetal-lalwani-0601/","isPremium":false,"isOpenToWork":false}],"pagination":{"count":25,"start":0},"hasMore":true}}}},"400":{"description":"Bad Request - Invalid request parameters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}},"401":{"description":"Unauthorized - Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unauthorized - Invalid credentials"}}}},"500":{"description":"Internal Server Error - Failed to get LinkedIn credentials or search failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Failed to search people","success":false}}}}}}},"/search/service-categories":{"post":{"operationId":"search-service-categories","tags":["Search"],"summary":"Search service categories","description":"Search LinkedIn service-category ids by name, for `POST /search/people` `filters.serviceCategories`.\n\n1. `POST /search/service-categories` with `{ \"keywords\": \"Fina\" }` returns `{ \"serviceCategoryId\": \"826\", \"name\": \"Financial Analysis\" }`.\n2. `POST /search/people` with `{ \"filters\": { \"serviceCategories\": [\"826\"] } }`.\n\nThe ids are opaque and not guessable (Financial Analysis is `826`, Accounting is `71`), and LinkedIn answers an unrecognised id with 200 and zero results rather than an error — so without this lookup a wrong id is indistinguishable from \"nobody matched\". Returns up to 10 matches in LinkedIn's ranking order.\n\n**Rate limit:** no per-account search quota is enforced on this lookup — only the general 30-calls-per-minute velocity limit. Resolve an id once and reuse it rather than looking it up on every request.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["keywords"],"properties":{"accountId":{"type":"string","description":"LinkedIn account ID to use for the search. If not provided, uses the default account."},"keywords":{"type":"string","minLength":1,"description":"Service category name to search for (e.g., \"Fina\", \"Marketing\", \"Accounting\")"}}}}}},"responses":{"200":{"description":"Service categories retrieved successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"serviceCategories":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","description":"Service category display name"},"serviceCategoryId":{"type":"string","description":"Id to pass as `filters.serviceCategories` on people search"}}}},"count":{"type":"number","description":"Number of categories returned"}}},"example":{"success":true,"serviceCategories":[{"name":"Financial Analysis","serviceCategoryId":"826"},{"name":"Financial Reporting","serviceCategoryId":"219"}],"count":2}}}},"400":{"description":"Bad Request - Missing required keywords parameter","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"},"example":{"success":false,"error":{"issues":[{"code":"invalid_type","expected":"string","received":"undefined","path":["keywords"],"message":"Required"}],"name":"ZodError"}}}}},"401":{"description":"Unauthorized"},"500":{"description":"Failed to search service categories"}}}},"/search/schools":{"post":{"operationId":"search-schools","tags":["Search"],"summary":"Search schools","description":"Search LinkedIn school IDs by name. Use it to find the `schoolId` that `POST /search/people` accepts in `filters.schoolIds`.\n\n1. `POST /search/schools` with `{ \"keywords\": \"Stanford\" }` returns `{ \"schoolId\": \"1792\", \"name\": \"Stanford University\" }`.\n2. `POST /search/people` with `{ \"filters\": { \"schoolIds\": [\"1792\"] } }`.\n\nResults come from the same typeahead the LinkedIn people-search \"Schools\" filter uses, so the ids are exactly the ones the filter accepts. Returns up to 10 matches, in LinkedIn's own ranking order.\n\n**Rate limit:** no per-account search quota is enforced on this lookup — only the general 30-calls-per-minute velocity limit. Resolve an id once and reuse it rather than looking it up on every request.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["keywords"],"properties":{"accountId":{"type":"string","description":"LinkedIn account ID to use for the search. If not provided, uses the default account."},"keywords":{"type":"string","minLength":1,"description":"School name to search for (e.g., \"Stanford\", \"Pune\", \"MIT\")"}}}}}},"responses":{"200":{"description":"Schools retrieved successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"schools":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","description":"School display name"},"schoolId":{"type":"string","description":"Numeric id to pass as `filters.schoolIds` on people search"}}}},"count":{"type":"number","description":"Number of schools returned"}}},"example":{"success":true,"schools":[{"name":"Stanford University","schoolId":"1792"},{"name":"Stanford Graduate School of Business","schoolId":"2742"}],"count":2}}}},"400":{"description":"Bad Request - Missing required keywords parameter","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"},"example":{"success":false,"error":{"issues":[{"code":"invalid_type","expected":"string","received":"undefined","path":["keywords"],"message":"Required"}],"name":"ZodError"}}}}},"401":{"description":"Unauthorized"},"500":{"description":"Failed to search schools"}}}},"/search/geo":{"post":{"operationId":"search-geo-locations","tags":["Search"],"summary":"Search geo locations","description":"Search for LinkedIn geo location IDs by place name. Use this endpoint to find location IDs that can be used as filters in other search endpoints (jobs, companies, people). Returns matching cities, regions, and countries.\n\n**Rate limit:** no per-account search quota is enforced on this lookup — only the general 30-calls-per-minute velocity limit. Resolve an id once and reuse it rather than looking it up on every request.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["keywords"],"properties":{"accountId":{"type":"string","description":"LinkedIn account ID to use for the search. If not provided, uses the default account."},"keywords":{"type":"string","minLength":1,"description":"Location name to search for (e.g., \"San Francisco\", \"United States\", \"London\")"},"countryCodes":{"type":"array","items":{"type":"string"},"description":"Limit results to specific countries using ISO country codes (e.g., [\"US\", \"GB\"])"}}}}}},"responses":{"200":{"description":"Geo locations retrieved successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"locations":{"type":"array","items":{"$ref":"#/components/schemas/GeoLocation"}},"count":{"type":"number","description":"Number of locations returned"}}},"example":{"success":true,"locations":[{"name":"San Francisco Bay Area","geoId":"90000084","countryCode":"US"},{"name":"San Francisco, California, United States","geoId":"102277331","countryCode":"US"}],"count":2}}}},"400":{"description":"Bad Request - Missing required keywords parameter","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"},"example":{"success":false,"error":{"issues":[{"code":"invalid_type","expected":"string","received":"undefined","path":["keywords"],"message":"Required"}],"name":"ZodError"}}}}},"401":{"description":"Unauthorized - Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unauthorized - Invalid credentials"}}}},"500":{"description":"Internal Server Error - Failed to get LinkedIn credentials or search failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Failed to search geo locations","success":false}}}}}}},"/search/jobs/details":{"post":{"operationId":"get-job-details","tags":["Search"],"summary":"Get job details","description":"Retrieve a job posting: its top card (title, company, location, pills, applicant count, hiring status) and its full description. Use the jobId from search results, or any jobId — the endpoint does not need a prior search.\n\n`postedOn` is not returned. LinkedIn shows the posting's age rather than a date, so `postedText` carries it verbatim (\"2 weeks ago\").","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["jobId"],"properties":{"accountId":{"type":"string","description":"LinkedIn account ID to use. If not provided, uses the default account."},"jobId":{"type":"string","minLength":1,"description":"LinkedIn job ID from search results"}}}}}},"responses":{"200":{"description":"Job details retrieved successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"jobId":{"type":"string","description":"LinkedIn job ID"},"title":{"type":"string","description":"Job title"},"companyName":{"type":"string","description":"Hiring company"},"companyLogo":{"type":"string","description":"Company logo URL"},"location":{"type":"string","description":"Job location, without the workplace suffix"},"workplaceType":{"type":"string","enum":["On-site","Remote","Hybrid"],"description":"Workplace pill. Absent when LinkedIn shows none."},"employmentType":{"type":"string","description":"Employment pill as LinkedIn labels it: \"Full-time\", \"Internship\", \"Contract\"…"},"postedText":{"type":"string","description":"How long ago the job was posted, as LinkedIn words it (\"2 weeks ago\")"},"applicantsText":{"type":"string","description":"The applicant metric LinkedIn chose to show, verbatim — it varies per posting (\"100 applicants\", \"Over 100 people clicked apply\")"},"hiringStatus":{"type":"string","description":"Hiring-team signal under the applicant count (\"Actively reviewing applicants\", \"Responses managed off LinkedIn\")"},"promoted":{"type":"boolean","description":"True when the posting is promoted by the hirer"},"verified":{"type":"boolean","description":"True when LinkedIn shows its \"Verified job\" shield"},"easyApply":{"type":"boolean","description":"True when the application is completed on LinkedIn rather than the company site"},"description":{"type":"string","description":"Full job description with requirements and responsibilities"},"jobUrl":{"type":"string","description":"Direct URL to the job posting"},"linkedinUrl":{"type":"string","description":"Direct URL to the job posting (same as `jobUrl`)"}}},"example":{"success":true,"jobId":"4445130437","title":"Demand Generation Executive","companyName":"BeFiSc","companyLogo":"https://media.licdn.com/dms/image/v2/D560BAQF0ZsoNx5Jm4A/company-logo_100_100/...","location":"Delhi, India","workplaceType":"On-site","employmentType":"Internship","postedText":"2 weeks ago","applicantsText":"100 applicants","hiringStatus":"Actively reviewing applicants","promoted":true,"verified":true,"easyApply":true,"description":"What you will do • Execute high velocity outbound prospecting...","jobUrl":"https://www.linkedin.com/jobs/view/4445130437/","linkedinUrl":"https://www.linkedin.com/jobs/view/4445130437/"}}}},"400":{"description":"Bad Request - Missing required jobId parameter","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"},"example":{"success":false,"error":{"issues":[{"code":"invalid_type","expected":"string","received":"undefined","path":["jobId"],"message":"Required"}],"name":"ZodError"}}}}},"401":{"description":"Unauthorized - Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unauthorized - Invalid credentials"}}}},"404":{"description":"Not Found - the job id is invalid, or the posting has been removed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Job details not found","success":false}}}},"500":{"description":"Internal Server Error - Failed to get LinkedIn credentials or job details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Failed to get job details","success":false}}}}}}},"/search/companies/details":{"post":{"operationId":"get-company-details","tags":["Search"],"summary":"Get company details","description":"Retrieve detailed information about a specific LinkedIn company page including full description, specialties, employee count, headquarters location, and industry classification.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["companyId"],"properties":{"accountId":{"type":"string","description":"LinkedIn account ID to use. If not provided, uses the default account."},"companyId":{"type":"string","minLength":1,"description":"LinkedIn company ID from search results or company page URL"}}}}}},"responses":{"200":{"description":"Company details retrieved successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"company":{"type":"object","description":"Detailed company information","properties":{"id":{"type":"string","description":"LinkedIn company ID"},"name":{"type":"string","description":"Company name"},"universalName":{"type":"string","description":"Company URL slug"},"tagline":{"type":"string","nullable":true,"description":"Company tagline"},"description":{"type":"string","description":"Full company description"},"websiteUrl":{"type":"string","description":"Company website URL"},"phone":{"type":"string","nullable":true,"description":"Company phone number"},"companyType":{"type":"string","description":"Type of company (COMPANY, EDUCATIONAL, etc.)"},"headquarters":{"type":"object","description":"Headquarters location details"},"employeeRange":{"type":"object","properties":{"start":{"type":"number","description":"Minimum employees"},"end":{"type":"number","nullable":true,"description":"Maximum employees (null for 10001+)"}}},"staffCount":{"type":"number","description":"Actual staff count on LinkedIn"},"specialities":{"type":"array","items":{"type":"string"},"description":"Company specialties and focus areas"},"logoUrl":{"type":"string","description":"Company logo URL"},"coverImageUrl":{"type":"string","description":"Company cover image URL"},"linkedinUrl":{"type":"string","description":"Direct URL to company page"},"isActive":{"type":"boolean","description":"Whether the company page is active"},"isVerified":{"type":"boolean","description":"Whether the company is verified"}}}}},"example":{"success":true,"company":{"id":"1441","name":"Google","universalName":"google","tagline":null,"description":"A problem isn't truly solved until it's solved for all...","websiteUrl":"https://goo.gle/3DLEokh","phone":null,"companyType":"COMPANY","headquarters":{},"employeeRange":{"start":10001,"end":null},"staffCount":334483,"specialities":["search","ads","mobile","android","machine learning"],"logoUrl":"https://media.licdn.com/dms/image/.../google_logo","coverImageUrl":"https://media.licdn.com/dms/image/.../google_cover","linkedinUrl":"https://www.linkedin.com/company/google/","isActive":true,"isVerified":false}}}}},"400":{"description":"Bad Request - Missing required companyId parameter","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"},"example":{"success":false,"error":{"issues":[{"code":"invalid_type","expected":"string","received":"undefined","path":["companyId"],"message":"Required"}],"name":"ZodError"}}}}},"401":{"description":"Unauthorized - Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unauthorized - Invalid credentials"}}}},"500":{"description":"Internal Server Error - Failed to get LinkedIn credentials or company details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Failed to get company details","success":false}}}}}}},"/search/groups/details":{"post":{"operationId":"get-group-details","tags":["Search"],"summary":"Get group details","description":"Retrieve detailed information about a specific LinkedIn group including full description, rules, member count, activity level, and admin information.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["groupId"],"properties":{"accountId":{"type":"string","description":"LinkedIn account ID to use. If not provided, uses the default account."},"groupId":{"type":"string","minLength":1,"description":"LinkedIn group ID from search results or group page URL"}}}}}},"responses":{"200":{"description":"Group details retrieved successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"group":{"type":"object","description":"Group details with member insights","properties":{"memberInsights":{"type":"array","description":"Insights about group members (education, location, industry)","items":{"type":"object","properties":{"insight":{"type":"string","description":"Description of the insight (e.g., \"Are located in India\")"},"count":{"type":"number","description":"Number of members matching this insight"}}}},"totalInsights":{"type":"number","description":"Total number of insights returned"}}}}},"example":{"success":true,"group":{"memberInsights":[{"insight":"Attended Pune University","count":55},{"insight":"Are located in India","count":30295},{"insight":"Are in the Computer Software industry","count":14488}],"totalInsights":3}}}}},"400":{"description":"Bad Request - Missing required groupId parameter","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"},"example":{"success":false,"error":{"issues":[{"code":"invalid_type","expected":"string","received":"undefined","path":["groupId"],"message":"Required"}],"name":"ZodError"}}}}},"401":{"description":"Unauthorized - Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unauthorized - Invalid credentials"}}}},"500":{"description":"Internal Server Error - Failed to get LinkedIn credentials or group details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Failed to get group details","success":false}}}}}}},"/search/people/v2":{"post":{"operationId":"search-people-v2","tags":["Search"],"summary":"Search LinkedIn people (V2 - with auto-pagination)","description":"Search for LinkedIn members with enhanced filtering options and automatic pagination. Supports additional filters like connectionOf, followerOf, and schoolIds. Automatically handles LinkedIn page-based pagination internally - specify the total count you want and the API will fetch multiple pages as needed (max 10 pages, ~10 results per page). Better for finding people connected to specific profiles or following specific accounts.\n\n**Important:** This V2 endpoint uses LinkedIn's newer search, which is rolling out gradually by region. If this endpoint does not return results for your account, use the V1 `/search/people` endpoint instead.\n\n**Rate limit:** 1,000 search calls per account per day (resets at midnight UTC), and 30 calls per minute. Pagination is billed per page walked, so a `count` of 25 spends about 3 calls.\n\nSeparately, LinkedIn applies its own **commercial use limit** to people search: roughly 100 searches a month on a free account, ~300 on Premium Career, ~500 on Premium Business, and effectively unlimited with Sales Navigator. That ceiling belongs to LinkedIn, is computed from search and browsing history rather than published, resets on the 1st of the month, and is **not** enforced or reported by this API. Maximum 1000 results per search.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"accountId":{"type":"string","description":"LinkedIn account ID to use. If not provided, uses the default account."},"keywords":{"type":"string","default":"","description":"Search keywords for name, title, company, or skills"},"count":{"type":"number","minimum":1,"maximum":100,"default":25,"description":"Total number of results to return. API will automatically paginate through LinkedIn pages (each page ~10 results) to collect this many."},"start":{"type":"number","minimum":0,"default":0,"description":"Starting offset (0-indexed). Used to calculate which page to start from."},"filters":{"type":"object","description":"Optional filters to narrow down search results","properties":{"connectionDegree":{"type":"array","items":{"type":"string","enum":["F","S","O"]},"description":"Filter by connection degree. F = 1st degree connections (direct contacts), S = 2nd degree connections (friends of friends), O = 3rd+ degree connections (everyone else). Combine multiple values to broaden results, e.g. [\"F\", \"S\"] for 1st and 2nd degree.","example":["F","S"]},"geoUrn":{"type":"array","items":{"type":"string"},"description":"LinkedIn geo URNs for location filtering (supports multiple)"},"firstName":{"type":"string","description":"Filter by first name"},"lastName":{"type":"string","description":"Filter by last name"},"title":{"type":"string","description":"Filter by current job title"},"company":{"type":"string","description":"Filter by current company name"},"school":{"type":"string","description":"Filter by education/school attended"},"currentCompanyIds":{"type":"array","items":{"type":"string"},"description":"Filter by current employer company IDs"},"pastCompanyIds":{"type":"array","items":{"type":"string"},"description":"Filter by past employer company IDs"},"industry":{"type":"array","items":{"type":"string"},"description":"LinkedIn industry IDs to filter by"},"schoolIds":{"type":"array","items":{"type":"string"},"description":"Filter by specific school IDs"},"profileLanguage":{"type":"array","items":{"type":"string"},"description":"Filter by profile language codes (e.g., en, es, fr)"},"serviceCategories":{"type":"array","items":{"type":"string"},"description":"Filter by service provider category ids. Look them up with `POST /search/service-categories` — the ids are opaque (Financial Analysis is `826`) and an unrecognised one returns 200 with zero results rather than an error.","example":["826"]},"openToVolunteer":{"type":"boolean","description":"Filter to only show people open to volunteering"},"connectionOf":{"type":"array","items":{"type":"string"},"description":"Find people connected to specific profile URNs"},"followerOf":{"type":"array","items":{"type":"string"},"description":"Find people who follow specific profile or company URNs"}}}}}}}},"responses":{"200":{"description":"People retrieved successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"people":{"type":"array","items":{"$ref":"#/components/schemas/PeopleSearchResult"}},"count":{"type":"number","description":"Number of people returned in this response"},"totalCount":{"type":"number","description":"Total number of matching results available on LinkedIn (may be 0 if unknown)"}}},"example":{"success":true,"people":[{"profileId":"shaarifalam","profileUrn":"urn:li:fsd_profile:ACoAACVNMaoBXNhxhJNadDzmfn6cX1bbEa8DFZI","firstName":"Shaarif","lastName":"Alam","headline":"Google Certified UI UX Designer","profilePicture":"https://media.licdn.com/dms/image/.../profile-displayphoto","location":"Delhi, India","connectionDegree":"2nd","profileUrl":"https://www.linkedin.com/in/shaarifalam/","isPremium":false,"isOpenToWork":false}],"count":3,"totalCount":0}}}},"400":{"description":"Bad Request - Invalid request parameters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}},"401":{"description":"Unauthorized - Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unauthorized - Invalid credentials"}}}},"500":{"description":"Internal Server Error - Failed to get LinkedIn credentials or search failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Failed to search people","success":false}}}}}}},"/search/posts/v2":{"post":{"operationId":"search-posts-v2","tags":["Search"],"summary":"Search LinkedIn posts (V2 - with auto-pagination)","description":"Search for LinkedIn posts with enhanced filtering options and automatic pagination. Automatically handles LinkedIn pagination internally - specify the total count you want (max 50) and the API will fetch multiple pages as needed (max 10 pages, 10 results per page). Supports filtering by date, content type, author, and more.\n\n**Important:** This V2 endpoint uses LinkedIn's newer search, which is rolling out gradually by region. If this endpoint does not return results for your account, use the V1 `/search/posts` endpoint instead.\n\n**Rate limit:** 1,000 search calls per account per day (resets at midnight UTC), and 30 calls per minute. One request counts as one call regardless of `count`.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"accountId":{"type":"string","description":"LinkedIn account ID to use. If not provided, uses the default account."},"keywords":{"type":"string","default":"","description":"Search keywords for post content"},"count":{"type":"number","minimum":1,"maximum":50,"default":10,"description":"Total number of posts to return. API will automatically paginate (10 per page) to collect this many."},"start":{"type":"number","minimum":0,"default":0,"description":"Starting offset (0-indexed) for pagination."},"exactSearch":{"type":"boolean","default":true,"description":"When true (default), the keyword is wrapped in double quotes for an exact-phrase match. Set to false for a broader search that also matches individual terms."},"filters":{"type":"object","description":"Optional filters to narrow down search results","properties":{"sortBy":{"type":"string","enum":["relevance","date_posted"],"description":"Sort order: relevance (best match) or date_posted (newest first)"},"datePosted":{"type":"array","items":{"type":"string","enum":["past-24h","past-week","past-month"]},"description":"Filter by when post was published"},"contentType":{"type":"array","items":{"type":"string","enum":["images","videos","documents","articles"]},"description":"Filter by content type attached to posts"},"postedBy":{"type":"array","items":{"type":"string","enum":["first","followed"]},"description":"Filter by relationship: first=1st connections, followed=people you follow"},"fromMember":{"type":"array","items":{"type":"string"},"description":"Filter by specific author profile URNs"},"fromOrganization":{"type":"array","items":{"type":"string"},"description":"Filter by specific company/organization URNs"},"mentionsMember":{"type":"array","items":{"type":"string"},"description":"Filter posts that mention specific profile URNs"},"mentionsOrganization":{"type":"array","items":{"type":"string"},"description":"Filter posts that mention specific company URNs"},"authorCompany":{"type":"array","items":{"type":"string"},"description":"Filter by author current employer company URNs"},"authorIndustry":{"type":"array","items":{"type":"string"},"description":"Filter by author industry URNs"},"authorJobTitle":{"type":"array","items":{"type":"string"},"description":"Filter by author job titles"}}}}}}}},"responses":{"200":{"description":"Posts retrieved successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"posts":{"type":"array","items":{"$ref":"#/components/schemas/Post"}},"count":{"type":"number","description":"Number of posts returned in this response"},"totalCount":{"type":"number","description":"Total number of matching results available on LinkedIn (may be 0 if unknown)"}}},"example":{"success":true,"posts":[{"activityUrn":"urn:li:ugcPost:7431688584019111936","postUrl":"https://www.linkedin.com/feed/update/urn:li:ugcPost:7431688584019111936/","author":{"profileId":"shiv-pandya-2ab25817","profileUrl":"https://www.linkedin.com/in/shiv-pandya-2ab25817/","name":"Shiv Pandya","headline":"Business Head- Kashyap Solar (Renewable Energy)","profilePicture":"","connectionDegree":"2nd","isVerified":false,"isPremium":false},"content":"AI (Artificial intelligence) is rapidly transforming major industries...","timestamp":"1d","visibility":"public","engagement":{"reactions":12,"comments":2,"reposts":0},"hashtags":["#solar","#AI","#power"],"isPeopleUpdate":false,"isArticle":false,"canComment":true}],"count":3,"totalCount":0}}}},"400":{"description":"Bad Request - Invalid request parameters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}},"401":{"description":"Unauthorized - Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unauthorized - Invalid credentials"}}}},"500":{"description":"Internal Server Error - Failed to get LinkedIn credentials or search failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Failed to search posts","success":false}}}}}}},"/feed":{"post":{"operationId":"get-feed","tags":["Posts"],"summary":"Get the current user's LinkedIn home feed","description":"Fetch posts from the authenticated user's LinkedIn home feed. Returns a mix of organic and promoted posts with author info, engagement metrics, and feed context (why the post appeared). Promoted posts are flagged with isPromoted: true.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"accountId":{"type":"string","description":"LinkedIn account ID to use. If not provided, uses the default account."},"count":{"type":"number","minimum":1,"maximum":50,"default":5,"description":"Number of feed posts to retrieve (max 50)"},"start":{"type":"number","minimum":0,"default":0,"description":"Starting offset for pagination"},"sortOrder":{"type":"string","enum":["relevance","recent"],"default":"relevance","description":"Feed sort order: relevance (default algorithm) or recent (newest first)"}}}}}},"responses":{"200":{"description":"Feed posts retrieved successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"posts":{"type":"array","items":{"type":"object","properties":{"activityUrn":{"type":"string","description":"LinkedIn activity URN of the post"},"postUrl":{"type":"string","description":"Direct URL to the post"},"author":{"type":"object","properties":{"name":{"type":"string","description":"Author display name"},"profileUrl":{"type":"string","description":"Author profile or company URL"},"profileId":{"type":"string","description":"Author vanity name or company slug"},"headline":{"type":"string","description":"Author professional headline"},"connectionDegree":{"type":"string","description":"Connection degree (1st, 2nd, 3rd+, Following)"},"isPremium":{"type":"boolean","description":"Whether the author has a Premium badge"},"isCompany":{"type":"boolean","description":"Whether the author is a company/organization"}}},"content":{"type":"string","description":"Post text content"},"timestamp":{"type":"string","description":"Relative timestamp (e.g., \"5h\", \"3d\", \"1w\")"},"engagement":{"type":"object","properties":{"reactions":{"type":"number","description":"Total reaction count"},"comments":{"type":"number","description":"Comment count"},"reposts":{"type":"number","description":"Repost count"}}},"hashtags":{"type":"array","items":{"type":"string"},"description":"Hashtags extracted from the post content"},"isPromoted":{"type":"boolean","description":"Whether this is a promoted/sponsored post"},"feedContext":{"type":"string","description":"Why this post appeared in the feed (e.g., \"Suggested\", \"John commented on this\")"}}}},"count":{"type":"number","description":"Number of posts returned"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Failed to fetch feed","success":false}}}}}}},"/posts/reactions":{"post":{"operationId":"get-post-reactions","tags":["Posts"],"summary":"Get reactions from a post","description":"Retrieve reactions (likes, celebrates, etc.) from a LinkedIn post. Identical to /posts/reactions/v2 — both return vanity names in actorPublicIdentifier. Pages by token like /posts/comments: pass the previous response's pagination.nextPaginationToken back as paginationToken.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["postUrl"],"properties":{"accountId":{"type":"string","description":"LinkedIn account ID to use. If not provided, uses the default account."},"postUrl":{"type":"string","description":"Full URL of the LinkedIn post to get reactions from"},"count":{"type":"number","minimum":1,"maximum":100,"description":"Number of reactions per page (default 10)"},"paginationToken":{"type":"string","description":"Token from previous response to fetch next page"},"pageToken":{"type":"string","description":"Deprecated alias for paginationToken, kept for existing v2 clients"},"start":{"type":"number","minimum":0,"description":"Deprecated. LinkedIn pages this endpoint by token only, so this value is ignored. Use paginationToken."}}}}}},"responses":{"200":{"description":"Reactions retrieved successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"postUrl":{"type":"string","description":"URL of the post"},"accountId":{"type":"string","description":"LinkedIn account ID used for the request"},"reactions":{"type":"array","items":{"type":"object","properties":{"actorUrn":{"type":"string","nullable":true,"description":"Always null — LinkedIn does not expose profile URNs here. Use actorPublicIdentifier."},"postUrn":{"type":"string","description":"URN of the post"},"reactionType":{"type":"string","description":"Type of reaction (LIKE, CELEBRATE, SUPPORT, etc.)"},"actorType":{"type":"string","description":"Type of actor (profile, company)"},"actorName":{"type":"string","description":"Name of the person who reacted"},"actorHeadline":{"type":"string","description":"Headline of the person who reacted"},"actorPublicIdentifier":{"type":"string","nullable":true,"description":"Vanity name for profile URL"},"actorProfilePicture":{"type":"string","description":"URL to profile picture"},"actorProfileUrl":{"type":"string","description":"Full URL to the profile"},"connectionDegree":{"type":"string","nullable":true,"description":"Connection degree (1st, 2nd, 3rd)"}}}},"pagination":{"type":"object","properties":{"count":{"type":"number","description":"Number of reactions in this page"},"hasNextPage":{"type":"boolean","description":"True when LinkedIn handed back a token for another page"},"nextPaginationToken":{"type":"string","nullable":true,"description":"Pass back as paginationToken to fetch the next page"}}},"postDetails":{"type":"object","properties":{"activityUrn":{"type":"string"},"ugcPostUrn":{"type":"string","nullable":true},"shareUrn":{"type":"string","nullable":true},"featuredActivityUrn":{"type":"string","nullable":true},"content":{"type":"string"}}}}},"example":{"success":true,"postUrl":"https://www.linkedin.com/feed/update/urn:li:activity:7430667226199830528/","accountId":"696ce9e780e0483585e4e553","reactions":[{"actorUrn":null,"postUrn":"urn:li:activity:7430667226199830528","reactionType":"LIKE","actorType":"profile","actorName":"John Doe","actorHeadline":"Software Engineer at Tech Company","actorPublicIdentifier":"john-doe-123","actorProfilePicture":"https://media.licdn.com/dms/image/...","actorProfileUrl":"https://www.linkedin.com/in/john-doe-123","connectionDegree":"1st"}],"pagination":{"count":5,"hasNextPage":false,"nextPaginationToken":null},"postDetails":{"activityUrn":"urn:li:activity:7430667226199830528","ugcPostUrn":null,"shareUrn":"urn:li:share:7430667225633550337","featuredActivityUrn":null,"content":"Post content here..."}}}}},"400":{"description":"Bad request - Missing required fields","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":"object"}}},"example":{"success":false,"error":{"issues":[{"code":"invalid_type","expected":"string","received":"undefined","path":["postUrl"],"message":"Required"}],"name":"ZodError"}}}}},"401":{"description":"Unauthorized - Invalid or missing API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}},"example":{"error":"Unauthorized - Invalid credentials"}}}}}}},"/posts/reactions/v2":{"post":{"operationId":"get-post-reactions-v2","tags":["Posts"],"summary":"Get reactions from a post (v2 - with vanity names)","description":"Retrieve reactions from a LinkedIn post. Returns vanity names (actorPublicIdentifier) and proper profile URLs. Identical to /posts/reactions — pages by token like /posts/comments: pass the previous response's pagination.nextPaginationToken back as paginationToken.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["postUrl"],"properties":{"accountId":{"type":"string","description":"LinkedIn account ID to use. If not provided, uses the default account."},"postUrl":{"type":"string","description":"Full URL of the LinkedIn post to get reactions from"},"count":{"type":"number","minimum":1,"maximum":100,"description":"Number of reactions per page (default 10)"},"paginationToken":{"type":"string","description":"Token from previous response to fetch next page"},"pageToken":{"type":"string","description":"Deprecated alias for paginationToken, kept for existing v2 clients"},"start":{"type":"number","minimum":0,"description":"Deprecated. LinkedIn pages this endpoint by token only, so this value is ignored. Use paginationToken."}}}}}},"responses":{"200":{"description":"Reactions retrieved successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"postUrl":{"type":"string","description":"URL of the post"},"accountId":{"type":"string","description":"LinkedIn account ID used for the request"},"reactions":{"type":"array","items":{"type":"object","properties":{"actorUrn":{"type":"string","nullable":true,"description":"URN of the person who reacted (null in v2)"},"postUrn":{"type":"string","description":"URN of the post"},"reactionType":{"type":"string","description":"Type of reaction (LIKE, PRAISE, APPRECIATION, EMPATHY, INTEREST, ENTERTAINMENT)"},"actorType":{"type":"string","description":"Type of actor (profile)"},"actorName":{"type":"string","description":"Name of the person who reacted"},"actorHeadline":{"type":"string","nullable":true,"description":"Headline of the person who reacted"},"actorPublicIdentifier":{"type":"string","description":"Vanity name / public identifier (e.g. john-doe-123)"},"actorProfilePicture":{"type":"string","nullable":true,"description":"URL to profile picture"},"actorProfileUrl":{"type":"string","description":"Full URL to the profile (linkedin.com/in/vanityName)"},"connectionDegree":{"type":"string","nullable":true,"description":"Connection degree (1st, 2nd, 3rd+)"}}}},"pagination":{"type":"object","properties":{"count":{"type":"number","description":"Number of reactions in this page"},"hasNextPage":{"type":"boolean","description":"True when LinkedIn handed back a token for another page"},"nextPaginationToken":{"type":"string","nullable":true,"description":"Pass back as paginationToken to fetch the next page"}}},"postDetails":{"type":"object","properties":{"activityUrn":{"type":"string"},"ugcPostUrn":{"type":"string","nullable":true},"shareUrn":{"type":"string","nullable":true},"featuredActivityUrn":{"type":"string","nullable":true},"content":{"type":"string"}}}}},"example":{"success":true,"postUrl":"https://www.linkedin.com/feed/update/urn:li:activity:7430667226199830528/","accountId":"696ce9e780e0483585e4e553","reactions":[{"actorUrn":null,"postUrn":"urn:li:activity:7430667226199830528","reactionType":"LIKE","actorType":"profile","actorName":"John Doe","actorHeadline":"Software Engineer at Tech Company","actorPublicIdentifier":"john-doe-123","actorProfilePicture":"https://media.licdn.com/dms/image/...","actorProfileUrl":"https://www.linkedin.com/in/john-doe-123/","connectionDegree":"1st"}],"pagination":{"count":10,"hasNextPage":true,"nextPaginationToken":"CiAyMDhhZjk1Y2E4MDE5YWQwNDI2ZTZhZTJhNTM2NzkzMBAK"},"postDetails":{"activityUrn":"urn:li:activity:7430667226199830528","ugcPostUrn":null,"shareUrn":"urn:li:share:7430667225633550337","featuredActivityUrn":null,"content":"Post content here..."}}}}},"400":{"description":"Bad request - Missing required fields or no valid URN found","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized - Invalid or missing API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}},"example":{"error":"Unauthorized - Invalid credentials"}}}}}}},"/posts/reposts":{"post":{"operationId":"get-reposts","tags":["Posts"],"summary":"Get the list of people who reposted a post","description":"Retrieve the list of people who reposted a LinkedIn post. Returns each reposter's name, vanity name (actorPublicIdentifier), profile URL and profile picture, plus a repostType (\"simple\" = plain repost, \"quote\" = repost with commentary). Uses cursor-based pagination: pass the returned pagination.token together with nextStartIndex to fetch the next page.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["postUrl"],"properties":{"accountId":{"type":"string","description":"LinkedIn account ID to use. If not provided, uses the default account."},"postUrl":{"type":"string","description":"Full URL of the LinkedIn post to get reposters from"},"count":{"type":"number","minimum":1,"maximum":100,"description":"Number of reposters to request per page (default 50)"},"startIndex":{"type":"number","minimum":0,"description":"Offset for pagination (default 0). Use pagination.nextStartIndex from the previous response."},"token":{"type":"string","description":"Cursor token from a previous response (pagination.token). Required together with startIndex for subsequent pages."}}}}}},"responses":{"200":{"description":"Reposters retrieved successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"postUrl":{"type":"string","description":"URL of the post"},"accountId":{"type":"string","description":"LinkedIn account ID used for the request"},"reposters":{"type":"array","items":{"type":"object","properties":{"actorType":{"type":"string","description":"Type of actor (profile, company). A company page can repost as itself."},"actorName":{"type":"string","description":"Name of the person or company page that reposted"},"actorPublicIdentifier":{"type":"string","description":"Vanity name / public identifier — the member vanity for a profile (e.g. himansuuverma), the company universal name for a company (e.g. multioutreachagency)"},"actorProfilePicture":{"type":"string","nullable":true,"description":"URL to profile picture"},"actorProfileUrl":{"type":"string","description":"Full URL to the profile (linkedin.com/in/vanityName)"},"repostType":{"type":"string","enum":["simple","quote"],"description":"\"simple\" = plain repost, \"quote\" = repost with commentary"}}}},"pagination":{"type":"object","properties":{"startIndex":{"type":"number"},"count":{"type":"number","description":"Number of reposters returned on this page"},"hasNextPage":{"type":"boolean"},"nextStartIndex":{"type":"number","description":"Pass as startIndex (with token) to fetch the next page"},"token":{"type":"string","nullable":true,"description":"Cursor token for next page. Pass as token (with nextStartIndex as startIndex) in next request."}}},"postDetails":{"type":"object","properties":{"activityUrn":{"type":"string"},"ugcPostUrn":{"type":"string","nullable":true},"shareUrn":{"type":"string","nullable":true},"featuredActivityUrn":{"type":"string","nullable":true},"content":{"type":"string"}}}}},"example":{"success":true,"postUrl":"https://www.linkedin.com/feed/update/urn:li:activity:7468554531233185792/","accountId":"696ce9e780e0483585e4e553","reposters":[{"actorType":"profile","actorName":"Himanshu Verma","actorPublicIdentifier":"himansuuverma","actorProfilePicture":"https://media.licdn.com/dms/image/...","actorProfileUrl":"https://www.linkedin.com/in/himansuuverma","repostType":"simple"}],"pagination":{"startIndex":0,"count":10,"hasNextPage":true,"nextStartIndex":10,"token":"Cjc4MDcxMjk3My0xNzgwOTg1MTY1OTk0LTVmNmY2NzMzMzZjZTg0ZjA1MzExMGU4ZDdmZGUwYTdl"},"postDetails":{"activityUrn":"urn:li:activity:7468554531233185792","ugcPostUrn":null,"shareUrn":"urn:li:share:7468554530276720640","featuredActivityUrn":null,"content":"Claude just became a Wall Street analyst..."}}}}},"400":{"description":"Bad request - Missing required fields or no valid URN found","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized - Invalid or missing API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}},"example":{"error":"Unauthorized - Invalid credentials"}}}}}}},"/messaging/recent-messages":{"get":{"operationId":"get-recent-messages","tags":["Messaging"],"summary":"Get recent messages","deprecated":true,"description":"**DEPRECATED: Use GET /conversations instead.** This endpoint now redirects (307) to GET /conversations. The /conversations endpoint provides DB-first caching, multi-account support, and automatic Sales Navigator thread merging. --- Retrieve recent LinkedIn messages/conversations for the authenticated account. Supports filtering by keywords and read status. Returns simplified conversation list with latest message preview. No daily quota applies — that quota governs sending, and this is a read.","parameters":[{"name":"accountId","in":"query","schema":{"type":"string"},"description":"LinkedIn account ID to use. If not provided, uses the default account."},{"name":"nextCursor","in":"query","schema":{"type":"string"},"description":"Cursor for pagination from previous response"},{"name":"count","in":"query","schema":{"type":"number"},"description":"Number of conversations to return"},{"name":"keywords","in":"query","schema":{"type":"string"},"description":"Search keywords to filter conversations"},{"name":"read","in":"query","schema":{"type":"string"},"description":"Filter by read status: \"true\" or \"false\""}],"responses":{"200":{"description":"Recent messages retrieved successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"accountId":{"type":"string"},"profileUrn":{"type":"string"},"conversations":{"type":"array","items":{"type":"object","properties":{"conversationId":{"type":"string"},"conversationUrn":{"type":"string"},"backendConversationUrn":{"type":"string"},"conversationUrl":{"type":"string"},"participants":{"type":"array","items":{"type":"object","properties":{"profileId":{"type":"string"},"name":{"type":"string"},"headline":{"type":"string"},"profileUrl":{"type":"string"},"profilePicture":{"type":"string"},"distance":{"type":"string"},"memberBadgeType":{"type":"string","nullable":true},"participantUrn":{"type":"string"},"backendUrn":{"type":"string"},"hostIdentityUrn":{"type":"string"}}}},"unreadCount":{"type":"number"},"lastActivityAt":{"type":"number"},"createdAt":{"type":"number"},"lastReadAt":{"type":"number"},"isRead":{"type":"boolean"},"state":{"type":"string","nullable":true},"latestMessage":{"type":"object","properties":{"messageUrn":{"type":"string"},"backendMessageUrn":{"type":"string"},"text":{"type":"string"},"sentAt":{"type":"number"},"senderName":{"type":"string"},"senderProfileUrl":{"type":"string"},"senderUrn":{"type":"string"},"hasAttachment":{"type":"boolean"}}}}}},"total":{"type":"number"},"nextCursor":{"type":"string"}}},"example":{"success":true,"accountId":"696ce9e780e0483585e4e553","profileUrn":"urn:li:fsd_profile:ACoAAATPlQ0Bc8wdg-Iy8wZgEDuSdIVWJiE1Aio","conversations":[{"conversationId":"2-ODFkYjU4M2ItZDlmYS00YTVjLThlNTQtZjI5YTE4ZWUyZDk5XzEwMA==","conversationUrn":"urn:li:msg_conversation:(urn:li:fsd_profile:ACoAAATPlQ0Bc8wdg-Iy8wZgEDuSdIVWJiE1Aio,2-ODFkYjU4M2ItZDlmYS00YTVjLThlNTQtZjI5YTE4ZWUyZDk5XzEwMA==)","backendConversationUrn":"urn:li:messagingThread:2-ODFkYjU4M2ItZDlmYS00YTVjLThlNTQtZjI5YTE4ZWUyZDk5XzEwMA==","conversationUrl":"https://www.linkedin.com/messaging/thread/2-ODFkYjU4M2ItZDlmYS00YTVjLThlNTQtZjI5YTE4ZWUyZDk5XzEwMA==/","participants":[{"profileId":"ACoAADSoo60BM6hxemTbnN2aA_Z1-WlNFRhRYhU","name":"John Doe","headline":"Software Engineer at Tech Company","profileUrl":"https://www.linkedin.com/in/ACoAADSoo60BM6hxemTbnN2aA_Z1-WlNFRhRYhU","profilePicture":"https://media.licdn.com/dms/image/...","distance":"DISTANCE_1","memberBadgeType":null,"participantUrn":"urn:li:msg_messagingParticipant:urn:li:fsd_profile:ACoAADSoo60BM6hxemTbnN2aA_Z1-WlNFRhRYhU","backendUrn":"urn:li:member:883467181","hostIdentityUrn":"urn:li:fsd_profile:ACoAADSoo60BM6hxemTbnN2aA_Z1-WlNFRhRYhU"}],"unreadCount":0,"lastActivityAt":1771950346603,"createdAt":1771317337868,"lastReadAt":1771950726332,"isRead":true,"state":null,"latestMessage":{"messageUrn":"urn:li:msg_message:(urn:li:fsd_profile:ACoAAATPlQ0Bc8wdg-Iy8wZgEDuSdIVWJiE1Aio,2-MTc3MTk1MDM0NjYwM2I5NzY0NS0xMDA=)","backendMessageUrn":"urn:li:messagingMessage:2-MTc3MTk1MDM0NjYwM2I5NzY0NS0xMDA=","text":"Hello, how are you?","sentAt":1771950346603,"senderName":"John Doe","senderProfileUrl":"https://www.linkedin.com/in/ACoAADSoo60BM6hxemTbnN2aA_Z1-WlNFRhRYhU","senderUrn":"urn:li:msg_messagingParticipant:urn:li:fsd_profile:ACoAADSoo60BM6hxemTbnN2aA_Z1-WlNFRhRYhU","hasAttachment":false}}]}}}},"401":{"description":"Unauthorized - Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnauthorizedError"},"example":{"error":"Unauthorized - Invalid credentials"}}}},"500":{"description":"Server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServerError"},"example":{"error":"Failed to get LinkedIn authentication credentials","success":false}}}}}}},"/messaging/conversation-details":{"get":{"operationId":"get-conversation-details","tags":["Messaging"],"summary":"Get conversation details","deprecated":true,"description":"**DEPRECATED: Use GET /conversations/{conversationUrn}/messages instead.** This endpoint now redirects (307) to the new conversations messages endpoint. The new endpoint provides DB-first caching, cursor pagination, sync mode, and auto-detects Sales Navigator threads. --- Retrieve detailed message history for a specific LinkedIn conversation. Supports pagination via deliveredAt timestamp to load older/newer messages. Returns full message content, sender info, and timestamps.","parameters":[{"name":"accountId","in":"query","schema":{"type":"string"},"description":"LinkedIn account ID to use. If not provided, uses the default account."},{"name":"profileId","in":"query","schema":{"type":"string"},"description":"Profile ID of conversation participant (optional)"},{"name":"conversationUrn","in":"query","required":true,"schema":{"type":"string"},"description":"LinkedIn conversation URN from recent-messages response"},{"name":"deliveredAt","in":"query","schema":{"type":"number"},"description":"Timestamp (milliseconds) for pagination anchor point"},{"name":"countBefore","in":"query","schema":{"type":"number"},"description":"Number of messages before deliveredAt timestamp"},{"name":"countAfter","in":"query","schema":{"type":"number"},"description":"Number of messages after deliveredAt timestamp"}],"responses":{"200":{"description":"Conversation details retrieved successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"accountId":{"type":"string"},"profileUrn":{"type":"string"},"conversationUrn":{"type":"string"},"messages":{"type":"array","items":{"type":"object","properties":{"messageId":{"type":"string"},"backendMessageUrn":{"type":"string"},"text":{"type":"string"},"subject":{"type":"string","nullable":true},"sentAt":{"type":"number"},"sender":{"type":"object","properties":{"profileId":{"type":"string"},"name":{"type":"string"},"profileUrl":{"type":"string"},"profilePicture":{"type":"string"},"participantUrn":{"type":"string"}}},"attachments":{"type":"array","nullable":true},"reactions":{"type":"array","nullable":true},"hasAttachment":{"type":"boolean"}}}},"total":{"type":"number"}}},"example":{"success":true,"accountId":"696ce9e780e0483585e4e553","profileUrn":"urn:li:fsd_profile:ACoAAATPlQ0Bc8wdg-Iy8wZgEDuSdIVWJiE1Aio","conversationUrn":"urn:li:msg_conversation:(urn:li:fsd_profile:ACoAAATPlQ0Bc8wdg-Iy8wZgEDuSdIVWJiE1Aio,2-NzkzMDFlNzAtZjU2OS00MjIwLWE2ZDctYzZkMWE1ZDljZDAyXzEwMA==)","messages":[{"messageId":"urn:li:msg_message:(urn:li:fsd_profile:ACoAAATPlQ0Bc8wdg-Iy8wZgEDuSdIVWJiE1Aio,2-MTc2MDc3MzMxMDQyOWI2OTc1NC0xMDA=)","backendMessageUrn":"urn:li:messagingMessage:2-MTc2MDc3MzMxMDQyOWI2OTc1NC0xMDA=","text":"Hello","subject":null,"sentAt":1760773310429,"sender":{"profileId":"ACoAAFpqSoMB8vTqRbg4mN_wbabO8w0gjgFu-6o","name":"Jane Smith","profileUrl":"https://www.linkedin.com/in/ACoAAFpqSoMB8vTqRbg4mN_wbabO8w0gjgFu-6o","profilePicture":"https://media.licdn.com/dms/image/...","participantUrn":"urn:li:msg_messagingParticipant:urn:li:fsd_profile:ACoAAFpqSoMB8vTqRbg4mN_wbabO8w0gjgFu-6o"},"attachments":null,"reactions":null,"hasAttachment":false}]}}}},"400":{"description":"Bad request - Missing required parameter","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"},"example":{"success":false,"error":"conversationUrn is required"}}}},"401":{"description":"Unauthorized - Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnauthorizedError"},"example":{"error":"Unauthorized - Invalid credentials"}}}},"500":{"description":"Server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServerError"},"example":{"error":"Failed to get LinkedIn authentication credentials","success":false}}}}}}},"/messaging/typing-indicator":{"post":{"operationId":"send-typing-indicator","tags":["Messaging"],"summary":"Send typing indicator","deprecated":true,"description":"**DEPRECATED: Will be moved to /conversations API.** --- Send a typing indicator to a LinkedIn conversation. Shows the recipient that you are typing a message. Useful for creating a more natural conversation experience before sending a message.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["conversationUrn"],"properties":{"accountId":{"type":"string","description":"LinkedIn account ID to use. If not provided, uses the default account."},"conversationUrn":{"type":"string","description":"LinkedIn conversation URN to send typing indicator to"}}},"example":{"conversationUrn":"urn:li:msg_conversation:(urn:li:fsd_profile:ACoAAATPlQ0Bc8wdg-Iy8wZgEDuSdIVWJiE1Aio,2-NzkzMDFlNzAtZjU2OS00MjIwLWE2ZDctYzZkMWE1ZDljZDAyXzEwMA==)"}}}},"responses":{"200":{"description":"Typing indicator sent successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"accountId":{"type":"string"},"conversationUrn":{"type":"string"},"message":{"type":"string"}}},"example":{"success":true,"accountId":"696ce9e780e0483585e4e553","conversationUrn":"urn:li:msg_conversation:(urn:li:fsd_profile:ACoAAATPlQ0Bc8wdg-Iy8wZgEDuSdIVWJiE1Aio,2-NzkzMDFlNzAtZjU2OS00MjIwLWE2ZDctYzZkMWE1ZDljZDAyXzEwMA==)","message":"Typing indicator sent successfully"}}}},"400":{"description":"Bad request - Missing required parameter","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"},"example":{"success":false,"error":"conversationUrn is required"}}}},"401":{"description":"Unauthorized - Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnauthorizedError"},"example":{"error":"Unauthorized - Invalid credentials"}}}},"500":{"description":"Server error or failed to send typing indicator","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServerError"},"example":{"success":false,"accountId":"696ce9e780e0483585e4e553","conversationUrn":"urn:li:msg_conversation:...","message":"Failed to send typing indicator"}}}}}}},"/messaging/mark-seen":{"post":{"operationId":"mark-conversation-seen","tags":["Messaging"],"summary":"Mark conversation as seen","deprecated":true,"description":"**DEPRECATED: Will be moved to /conversations API.** --- Mark a LinkedIn conversation as seen/read. This updates the read status for the sender and removes the unread indicator. Useful for managing inbox state programmatically.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["conversationUrn"],"properties":{"accountId":{"type":"string","description":"LinkedIn account ID to use. If not provided, uses the default account."},"conversationUrn":{"type":"string","description":"LinkedIn conversation URN to mark as seen"}}},"example":{"conversationUrn":"urn:li:msg_conversation:(urn:li:fsd_profile:ACoAAATPlQ0Bc8wdg-Iy8wZgEDuSdIVWJiE1Aio,2-NzkzMDFlNzAtZjU2OS00MjIwLWE2ZDctYzZkMWE1ZDljZDAyXzEwMA==)"}}}},"responses":{"200":{"description":"Conversation marked as seen successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"accountId":{"type":"string"},"conversationUrn":{"type":"string"},"message":{"type":"string"}}},"example":{"success":true,"accountId":"696ce9e780e0483585e4e553","conversationUrn":"urn:li:msg_conversation:(urn:li:fsd_profile:ACoAAATPlQ0Bc8wdg-Iy8wZgEDuSdIVWJiE1Aio,2-NzkzMDFlNzAtZjU2OS00MjIwLWE2ZDctYzZkMWE1ZDljZDAyXzEwMA==)","message":"Conversation marked as seen successfully"}}}},"400":{"description":"Bad request - Missing required parameter","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"},"example":{"success":false,"error":"conversationUrn is required"}}}},"401":{"description":"Unauthorized - Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnauthorizedError"},"example":{"error":"Unauthorized - Invalid credentials"}}}},"500":{"description":"Server error or failed to mark conversation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServerError"},"example":{"success":false,"accountId":"696ce9e780e0483585e4e553","conversationUrn":"urn:li:msg_conversation:...","message":"Failed to mark conversation as seen"}}}}}}},"/messaging/mark-all-read":{"post":{"operationId":"mark-all-messages-read","tags":["Messaging"],"summary":"Mark all messages as read","deprecated":true,"description":"**DEPRECATED: Will be moved to /conversations API.** --- Mark all LinkedIn messages as read for the authenticated account. Clears all unread indicators in the inbox. Useful for bulk inbox management.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"accountId":{"type":"string","description":"LinkedIn account ID to use. If not provided, uses the default account."}}},"example":{}}}},"responses":{"200":{"description":"All messages marked as read successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"accountId":{"type":"string"},"message":{"type":"string"}}},"example":{"success":true,"accountId":"696ce9e780e0483585e4e553","message":"All messages marked as read successfully"}}}},"401":{"description":"Unauthorized - Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnauthorizedError"},"example":{"error":"Unauthorized - Invalid credentials"}}}},"500":{"description":"Server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServerError"},"example":{"error":"Failed to get LinkedIn authentication credentials","success":false}}}}}}},"/messaging/send":{"post":{"operationId":"messaging-send","tags":["Messaging"],"summary":"Send a message (messaging wrapper)","deprecated":true,"description":"**DEPRECATED: Use POST /conversations/send instead.** The /conversations/send endpoint auto-detects Sales Navigator accounts, supports attachments, and handles both standard and Sales Nav messaging transparently. --- Send a LinkedIn message using the messaging endpoint. Wrapper around the core messaging functionality with a cleaner interface. Supports regular messages and InMail. **Rate limit: 150 NEW conversations per day per account** (resets at midnight UTC). The quota applies to cold outreach only — a message that starts a conversation with someone this account has no existing thread with. Replying inside an existing conversation does not count, whether you pass a `conversationUrn` or a recipient this account has already messaged. **Do not read the rate-limit headers on a reply as the daily quota:** an exempt reply never touches the daily counter, so its headers come from the general per-minute API throttle instead (write endpoints: 15 requests/minute per user). A `X-RateLimit-Limit` of 15 resetting within a minute therefore means the send was NOT counted against the 150/day.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["message"],"properties":{"accountId":{"type":"string","description":"LinkedIn account ID to use. If not provided, uses the default account."},"recipientProfileId":{"type":"string","description":"Recipient LinkedIn profile vanity URL slug (e.g., \"john-doe-123\"). Prefer recipientProfileUrn when available."},"recipientProfileUrn":{"type":"string","description":"Recipient LinkedIn profile URN (e.g., \"urn:li:fsd_profile:ACoAABJefVoBrz2LR3f...\"). Preferred over recipientProfileId."},"message":{"type":"string","description":"Message content to send"},"subject":{"type":"string","description":"Subject line (required for InMail)"},"messageType":{"type":"string","enum":["normal","inmail"],"default":"normal","description":"Message type"}}},"example":{"recipientProfileUrn":"urn:li:fsd_profile:ACoAAFpqSoMB8vTqRbg4mN_wbabO8w0gjgFu-6o","message":"Hello, I would like to connect with you!"}}}},"responses":{"200":{"description":"Message sent successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"recipientProfileUrn":{"type":"string"}}},"example":{"success":true,"message":"Message sent successfully","recipientProfileUrn":"urn:li:fsd_profile:ACoAAFpqSoMB8vTqRbg4mN_wbabO8w0gjgFu-6o"}}}},"400":{"description":"Bad request - Missing required parameter","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"},"example":{"success":false,"error":"message is required"}}}},"401":{"description":"Unauthorized - Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnauthorizedError"},"example":{"error":"Unauthorized - Invalid credentials"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimitError"},"example":{"error":"Rate limit exceeded for MESSAGE: 150/150 used. Resets at 2024-01-02T00:00:00.000Z","success":false}}}},"500":{"description":"Server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServerError"},"example":{"error":"Failed to get LinkedIn authentication credentials","success":false}}}}}}},"/messaging/send-with-typing":{"post":{"operationId":"send-message-with-typing","tags":["Messaging"],"summary":"Send message with typing indicator","deprecated":true,"description":"**DEPRECATED: Use POST /conversations/send instead.** --- Send a LinkedIn message after showing a typing indicator first. Creates a more natural conversation experience by simulating human-like behavior. Requires conversationUrn for existing conversations. **Rate limit: 150 NEW conversations per day per account** (resets at midnight UTC). The quota applies to cold outreach only — a message that starts a conversation with someone this account has no existing thread with. Replying inside an existing conversation does not count, whether you pass a `conversationUrn` or a recipient this account has already messaged. **Do not read the rate-limit headers on a reply as the daily quota:** an exempt reply never touches the daily counter, so its headers come from the general per-minute API throttle instead (write endpoints: 15 requests/minute per user). A `X-RateLimit-Limit` of 15 resetting within a minute therefore means the send was NOT counted against the 150/day.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["message"],"properties":{"accountId":{"type":"string","description":"LinkedIn account ID to use. If not provided, uses the default account."},"recipientProfileId":{"type":"string","description":"Recipient LinkedIn profile vanity URL slug (e.g., \"john-doe-123\"). Prefer recipientProfileUrn when available."},"recipientProfileUrn":{"type":"string","description":"Recipient LinkedIn profile URN (e.g., \"urn:li:fsd_profile:ACoAABJefVoBrz2LR3f...\"). Preferred over recipientProfileId."},"message":{"type":"string","description":"Message content to send"},"subject":{"type":"string","description":"Subject line (for InMail)"},"messageType":{"type":"string","enum":["normal","inmail"],"default":"normal","description":"Message type"},"conversationUrn":{"type":"string","description":"Conversation URN (required for typing indicator in existing conversations)"}}},"example":{"recipientProfileUrn":"urn:li:fsd_profile:ACoAAFpqSoMB8vTqRbg4mN_wbabO8w0gjgFu-6o","message":"Hello, I would like to connect with you!","conversationUrn":"urn:li:msg_conversation:(urn:li:fsd_profile:ACoAAATPlQ0Bc8wdg-Iy8wZgEDuSdIVWJiE1Aio,2-NzkzMDFlNzAtZjU2OS00MjIwLWE2ZDctYzZkMWE1ZDljZDAyXzEwMA==)"}}}},"responses":{"200":{"description":"Message sent successfully with typing indicator","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"recipientProfileUrn":{"type":"string"},"typingIndicatorSent":{"type":"boolean"}}},"example":{"success":true,"message":"Message sent successfully","recipientProfileUrn":"urn:li:fsd_profile:ACoAAFpqSoMB8vTqRbg4mN_wbabO8w0gjgFu-6o","typingIndicatorSent":true}}}},"400":{"description":"Bad request - Missing required parameter","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"},"example":{"success":false,"error":"message is required"}}}},"401":{"description":"Unauthorized - Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnauthorizedError"},"example":{"error":"Unauthorized - Invalid credentials"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimitError"},"example":{"error":"Rate limit exceeded for MESSAGE: 150/150 used","success":false}}}},"500":{"description":"Server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServerError"},"example":{"error":"Failed to get LinkedIn authentication credentials","success":false}}}}}}},"/messaging/send-with-ack":{"post":{"operationId":"send-message-with-ack","tags":["Messaging"],"summary":"Send message with delivery acknowledgment","deprecated":true,"description":"**DEPRECATED: Use POST /conversations/send instead.** --- Send a LinkedIn message while acknowledging receipt of previous messages. Useful for replying to conversations where you want to mark received messages as acknowledged. **Rate limit: 150 NEW conversations per day per account** (resets at midnight UTC). The quota applies to cold outreach only — a message that starts a conversation with someone this account has no existing thread with. Replying inside an existing conversation does not count, whether you pass a `conversationUrn` or a recipient this account has already messaged. **Do not read the rate-limit headers on a reply as the daily quota:** an exempt reply never touches the daily counter, so its headers come from the general per-minute API throttle instead (write endpoints: 15 requests/minute per user). A `X-RateLimit-Limit` of 15 resetting within a minute therefore means the send was NOT counted against the 150/day.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["message"],"properties":{"accountId":{"type":"string","description":"LinkedIn account ID to use. If not provided, uses the default account."},"recipientProfileId":{"type":"string","description":"Recipient LinkedIn profile vanity URL slug (e.g., \"john-doe-123\"). Prefer recipientProfileUrn when available."},"recipientProfileUrn":{"type":"string","description":"Recipient LinkedIn profile URN (e.g., \"urn:li:fsd_profile:ACoAABJefVoBrz2LR3f...\"). Preferred over recipientProfileId."},"message":{"type":"string","description":"Message content to send"},"subject":{"type":"string","description":"Subject line (for InMail)"},"messageType":{"type":"string","enum":["normal","inmail"],"default":"normal","description":"Message type"},"messageUrns":{"type":"array","items":{"type":"string"},"description":"Message URNs to acknowledge delivery of"}}},"example":{"recipientProfileUrn":"urn:li:fsd_profile:ACoAAFpqSoMB8vTqRbg4mN_wbabO8w0gjgFu-6o","message":"Thanks for your message!"}}}},"responses":{"200":{"description":"Message sent with acknowledgment successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"recipientProfileUrn":{"type":"string"},"acknowledgmentSent":{"type":"boolean"}}},"example":{"success":true,"message":"Message sent successfully","recipientProfileUrn":"urn:li:fsd_profile:ACoAAFpqSoMB8vTqRbg4mN_wbabO8w0gjgFu-6o","acknowledgmentSent":false}}}},"400":{"description":"Bad request - Missing required parameter","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"},"example":{"success":false,"error":"message is required"}}}},"401":{"description":"Unauthorized - Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnauthorizedError"},"example":{"error":"Unauthorized - Invalid credentials"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimitError"},"example":{"error":"Rate limit exceeded for MESSAGE: 150/150 used","success":false}}}},"500":{"description":"Server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServerError"},"example":{"error":"Failed to get LinkedIn authentication credentials","success":false}}}}}}},"/messaging/send-group":{"post":{"operationId":"send-group-message","tags":["Messaging"],"summary":"Send message with group context","description":"Send a LinkedIn message to someone using group membership context. Allows messaging non-connections who are members of the same LinkedIn group without using InMail credits. **Rate limit: 150 NEW conversations per day per account** (resets at midnight UTC). The quota applies to cold outreach only — a message that starts a conversation with someone this account has no existing thread with. Replying inside an existing conversation does not count, whether you pass a `conversationUrn` or a recipient this account has already messaged. **Do not read the rate-limit headers on a reply as the daily quota:** an exempt reply never touches the daily counter, so its headers come from the general per-minute API throttle instead (write endpoints: 15 requests/minute per user). A `X-RateLimit-Limit` of 15 resetting within a minute therefore means the send was NOT counted against the 150/day.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["recipientProfileUrn","groupId","message"],"properties":{"accountId":{"type":"string","description":"LinkedIn account ID to use. If not provided, uses the default account."},"recipientProfileUrn":{"type":"string","description":"Recipient LinkedIn profile URN (e.g., \"urn:li:fsd_profile:ACoAABJefVoBrz2LR3f...\"). Preferred over recipientProfileId."},"groupId":{"type":"string","description":"LinkedIn group ID that both sender and recipient are members of"},"message":{"type":"string","description":"Message content to send"}}},"example":{"recipientProfileUrn":"urn:li:fsd_profile:ACoAAFpqSoMB8vTqRbg4mN_wbabO8w0gjgFu-6o","groupId":"12345678","message":"Hello fellow group member!"}}}},"responses":{"200":{"description":"Group context message sent successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"recipientProfileUrn":{"type":"string"},"groupId":{"type":"string"}}},"example":{"success":true,"message":"Group context message sent successfully","recipientProfileUrn":"urn:li:fsd_profile:ACoAAFpqSoMB8vTqRbg4mN_wbabO8w0gjgFu-6o","groupId":"12345678"}}}},"400":{"description":"Bad request - Missing required parameter","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"},"example":{"success":false,"error":"recipientProfileUrn, groupId, and message are required"}}}},"401":{"description":"Unauthorized - Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnauthorizedError"},"example":{"error":"Unauthorized - Invalid credentials"}}}},"403":{"description":"Forbidden - Not authorized to message this user via group","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServerError"},"example":{"success":false,"message":"Error sending group context message: Request failed with status code 403","recipientProfileUrn":"urn:li:fsd_profile:ACoAAFpqSoMB8vTqRbg4mN_wbabO8w0gjgFu-6o","groupId":"12345678","errorDetails":{"status":403,"statusText":"Forbidden","data":{"code":"UNAUTHORIZED_VIEWER","message":"The viewer is not authorized to access this resource"}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimitError"},"example":{"error":"Rate limit exceeded for MESSAGE: 150/150 used","success":false}}}},"500":{"description":"Server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServerError"},"example":{"error":"Failed to get LinkedIn authentication credentials","success":false}}}}}}},"/messaging/send-group-with-typing":{"post":{"operationId":"send-group-message-with-typing","tags":["Messaging"],"summary":"Send group message with typing indicator","deprecated":true,"description":"**DEPRECATED: Use POST /conversations/send instead.** --- Send a LinkedIn message using group context after showing a typing indicator. Combines group messaging capability with natural typing simulation. **Rate limit: 150 NEW conversations per day per account** (resets at midnight UTC). The quota applies to cold outreach only — a message that starts a conversation with someone this account has no existing thread with. Replying inside an existing conversation does not count, whether you pass a `conversationUrn` or a recipient this account has already messaged. **Do not read the rate-limit headers on a reply as the daily quota:** an exempt reply never touches the daily counter, so its headers come from the general per-minute API throttle instead (write endpoints: 15 requests/minute per user). A `X-RateLimit-Limit` of 15 resetting within a minute therefore means the send was NOT counted against the 150/day.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["recipientProfileUrn","groupId","message"],"properties":{"accountId":{"type":"string","description":"LinkedIn account ID to use. If not provided, uses the default account."},"recipientProfileUrn":{"type":"string","description":"Recipient LinkedIn profile URN (e.g., \"urn:li:fsd_profile:ACoAABJefVoBrz2LR3f...\"). Preferred over recipientProfileId."},"groupId":{"type":"string","description":"LinkedIn group ID"},"message":{"type":"string","description":"Message content to send"},"conversationUrn":{"type":"string","description":"Conversation URN for existing conversations"}}},"example":{"recipientProfileUrn":"urn:li:fsd_profile:ACoAAFpqSoMB8vTqRbg4mN_wbabO8w0gjgFu-6o","groupId":"12345678","message":"Hello fellow group member!"}}}},"responses":{"200":{"description":"Group message sent with typing indicator successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"recipientProfileUrn":{"type":"string"},"groupId":{"type":"string"},"typingIndicatorSent":{"type":"boolean"}}},"example":{"success":true,"message":"Group context message sent successfully","recipientProfileUrn":"urn:li:fsd_profile:ACoAAFpqSoMB8vTqRbg4mN_wbabO8w0gjgFu-6o","groupId":"12345678","typingIndicatorSent":true}}}},"400":{"description":"Bad request - Missing required parameter","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"},"example":{"success":false,"error":"recipientProfileUrn, groupId, and message are required"}}}},"401":{"description":"Unauthorized - Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnauthorizedError"},"example":{"error":"Unauthorized - Invalid credentials"}}}},"403":{"description":"Forbidden - Not authorized to message this user via group","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServerError"},"example":{"success":false,"message":"Error sending group context message: Request failed with status code 403","recipientProfileUrn":"urn:li:fsd_profile:...","groupId":"12345678","typingIndicatorSent":false}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimitError"},"example":{"error":"Rate limit exceeded for MESSAGE: 150/150 used","success":false}}}},"500":{"description":"Server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServerError"},"example":{"error":"Failed to get LinkedIn authentication credentials","success":false}}}}}}},"/messaging/send-group-with-ack":{"post":{"operationId":"send-group-message-with-ack","tags":["Messaging"],"summary":"Send group message with delivery acknowledgment","deprecated":true,"description":"**DEPRECATED: Use POST /conversations/send instead.** --- Send a LinkedIn message using group context while acknowledging delivery of previous messages. Useful for group-based conversations where you want to track message delivery. **Rate limit: 150 NEW conversations per day per account** (resets at midnight UTC). The quota applies to cold outreach only — a message that starts a conversation with someone this account has no existing thread with. Replying inside an existing conversation does not count, whether you pass a `conversationUrn` or a recipient this account has already messaged. **Do not read the rate-limit headers on a reply as the daily quota:** an exempt reply never touches the daily counter, so its headers come from the general per-minute API throttle instead (write endpoints: 15 requests/minute per user). A `X-RateLimit-Limit` of 15 resetting within a minute therefore means the send was NOT counted against the 150/day.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["recipientProfileUrn","groupId","message"],"properties":{"accountId":{"type":"string","description":"LinkedIn account ID to use. If not provided, uses the default account."},"recipientProfileUrn":{"type":"string","description":"Recipient LinkedIn profile URN (e.g., \"urn:li:fsd_profile:ACoAABJefVoBrz2LR3f...\"). Preferred over recipientProfileId."},"groupId":{"type":"string","description":"LinkedIn group ID"},"message":{"type":"string","description":"Message content to send"},"messageUrns":{"type":"array","items":{"type":"string"},"description":"Message URNs to acknowledge"}}},"example":{"recipientProfileUrn":"urn:li:fsd_profile:ACoAAFpqSoMB8vTqRbg4mN_wbabO8w0gjgFu-6o","groupId":"12345678","message":"Thanks for your message in the group!"}}}},"responses":{"200":{"description":"Group message sent with acknowledgment successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"recipientProfileUrn":{"type":"string"},"groupId":{"type":"string"},"acknowledgmentSent":{"type":"boolean"}}},"example":{"success":true,"message":"Group context message sent successfully","recipientProfileUrn":"urn:li:fsd_profile:ACoAAFpqSoMB8vTqRbg4mN_wbabO8w0gjgFu-6o","groupId":"12345678","acknowledgmentSent":true}}}},"400":{"description":"Bad request - Missing required parameter","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"},"example":{"success":false,"error":"recipientProfileUrn, groupId, and message are required"}}}},"401":{"description":"Unauthorized - Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnauthorizedError"},"example":{"error":"Unauthorized - Invalid credentials"}}}},"403":{"description":"Forbidden - Not authorized to message this user via group","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServerError"},"example":{"success":false,"message":"Error sending group context message: Request failed with status code 403","recipientProfileUrn":"urn:li:fsd_profile:...","groupId":"12345678"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimitError"},"example":{"error":"Rate limit exceeded for MESSAGE: 150/150 used","success":false}}}},"500":{"description":"Server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServerError"},"example":{"error":"Failed to get LinkedIn authentication credentials","success":false}}}}}}},"/analytics/connections/count":{"get":{"operationId":"get-connection-count","tags":["Analytics"],"summary":"Get exact connection count","description":"Returns the connected account's EXACT total connection count via LinkedIn's connectionsSummary endpoint — the real number, not the \"500+\" value LinkedIn shows on profiles. This reflects the authenticated account's own connections (select which connected account with `accountId`); it cannot return another member's connection count.","parameters":[{"name":"accountId","in":"query","required":false,"schema":{"type":"string"},"description":"LinkedIn account ID to use. If omitted, uses the default account."}],"responses":{"200":{"description":"Connection count retrieved successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"accountId":{"type":"string","description":"LinkedIn account ID used"},"connectionCount":{"type":"integer","nullable":true,"description":"Exact total number of connections (uncapped), or null if LinkedIn did not return a count"}}},"example":{"success":true,"accountId":"696ce9e780e0483585e4e553","connectionCount":3754}}}},"401":{"description":"Unauthorized - invalid or missing API key"},"500":{"description":"Internal Server Error - could not resolve credentials, or the connectionsSummary fetch failed (the exact upstream error is returned)"}}}},"/analytics/network/summary":{"get":{"operationId":"get-network-summary","tags":["Analytics"],"summary":"Get \"Manage my network\" counts","description":"Returns the connected account's own network counts as shown in the \"Manage my network\" card: connections, groups, events, pages and newsletters. The connection count here is the EXACT number, not the \"500+\" value LinkedIn shows on profiles. Read live from the my-network SDUI screen on every call (select which connected account with `accountId`); it cannot return another member's counts.","parameters":[{"name":"accountId","in":"query","required":false,"schema":{"type":"string"},"description":"LinkedIn account ID to use. If omitted, uses the default account."}],"responses":{"200":{"description":"Network summary retrieved successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"accountId":{"type":"string","description":"LinkedIn account ID used"},"connections":{"type":"integer","nullable":true,"description":"Exact total number of connections (uncapped)"},"groups":{"type":"integer","nullable":true,"description":"Groups the account belongs to"},"events":{"type":"integer","nullable":true,"description":"Events the account is attending"},"pages":{"type":"integer","nullable":true,"description":"Company pages the account follows"},"newsletters":{"type":"integer","nullable":true,"description":"Newsletters the account subscribes to"}}},"example":{"success":true,"accountId":"696ce9e780e0483585e4e553","connections":1419,"groups":14,"events":1,"pages":17,"newsletters":7}}}},"401":{"description":"Unauthorized - invalid or missing API key"},"500":{"description":"Internal Server Error - could not resolve credentials, or the my-network fetch failed (the exact upstream error is returned)"}}}},"/analytics/creator/dashboard":{"get":{"operationId":"get-creator-dashboard","tags":["Analytics"],"summary":"Get creator dashboard overview","description":"Returns the connected account's own creator dashboard (linkedin.com/dashboard/): the \"Track performance\" cards — post impressions, total followers, profile viewers and search appearances, each with its period label, period-over-period change and trend direction — plus the \"Weekly progress\" post and comment counts. Read live from the dashboard SDUI screen on every call (select which connected account with `accountId`); it cannot return another member's dashboard.","parameters":[{"name":"accountId","in":"query","required":false,"schema":{"type":"string"},"description":"LinkedIn account ID to use. If omitted, uses the default account."}],"responses":{"200":{"description":"Creator dashboard retrieved successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"accountId":{"type":"string","description":"LinkedIn account ID used"},"postImpressions":{"$ref":"#/components/schemas/CreatorDashboardMetric"},"totalFollowers":{"$ref":"#/components/schemas/CreatorDashboardMetric"},"profileViewers":{"$ref":"#/components/schemas/CreatorDashboardMetric"},"searchAppearances":{"$ref":"#/components/schemas/CreatorDashboardMetric"},"weeklyProgress":{"type":"object","properties":{"range":{"type":"string","nullable":true,"description":"Week the counts cover, e.g. \"Aug 18–Aug 24\""},"posts":{"type":"integer","nullable":true,"description":"Posts published this week"},"comments":{"type":"integer","nullable":true,"description":"Comments made this week"}}}}},"example":{"success":true,"accountId":"696ce9e780e0483585e4e553","postImpressions":{"value":4124,"label":"Post impressions in 7 days","changePercent":9,"changeDirection":"up","comparison":"vs. prior 7 days"},"totalFollowers":{"value":461,"label":"Total followers","changePercent":0,"changeDirection":"flat","comparison":"vs. prior 7 days"},"profileViewers":{"value":192,"label":"Profile viewers in 90 days","changePercent":47,"changeDirection":"down","comparison":"vs. prior 7 days"},"searchAppearances":{"value":8,"label":"Search appearances Aug 11–17","changePercent":0,"changeDirection":"flat","comparison":"vs. Aug 4–10"},"weeklyProgress":{"range":"Aug 18–Aug 24","posts":6,"comments":102}}}}},"401":{"description":"Unauthorized - invalid or missing API key"},"500":{"description":"Internal Server Error - could not resolve credentials, or the dashboard fetch failed (the exact upstream error is returned)"}}}},"/analytics/followers":{"get":{"operationId":"get-followers-analytics","tags":["Analytics"],"summary":"Get followers analytics","description":"Retrieve LinkedIn followers analytics for the authenticated account. Includes engagement metrics, content performance, audience insights, and growth trends. Supports various time ranges and metric filters.","parameters":[{"name":"accountId","in":"query","required":false,"schema":{"type":"string"},"description":"LinkedIn account ID to use. If not provided, uses the default account."},{"name":"resultType","in":"query","required":false,"schema":{"type":"string","enum":["AUDIENCES","CONTENT","ENGAGEMENT","GROWTH"]},"description":"Type of analytics data to retrieve"},{"name":"timeRange","in":"query","required":false,"schema":{"type":"string","enum":["past_7_days","past_14_days","past_30_days","past_90_days","past_year","custom"]},"description":"Time range for analytics data"},{"name":"lineChartType","in":"query","required":false,"schema":{"type":"string","enum":["daily","weekly","monthly"]},"description":"Granularity for line chart data"},{"name":"metricType","in":"query","required":false,"schema":{"type":"string"},"description":"Specific metric to retrieve"},{"name":"startDate","in":"query","required":false,"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"description":"Start date for custom range (YYYY-MM-DD)"},{"name":"endDate","in":"query","required":false,"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"description":"End date for custom range (YYYY-MM-DD)"}],"responses":{"200":{"description":"Creator analytics retrieved successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"accountId":{"type":"string","description":"LinkedIn account ID used"},"profileId":{"type":"string","description":"LinkedIn profile URN"},"isLineChart":{"type":"boolean","description":"Whether line chart data was requested"},"filters":{"type":"object","description":"Filters applied to the request"},"data":{"type":"object","description":"Simplified analytics data","properties":{"summary":{"type":"object","properties":{"metrics":{"type":"array","items":{"type":"object","properties":{"title":{"type":"string","description":"Metric title"},"value":{"type":"string","description":"Metric value (formatted)"},"change":{"type":"number","description":"Percentage change from previous period"},"changeDescription":{"type":"string","description":"Description of comparison period"},"description":{"type":"string","description":"Metric explanation"}}}}}},"chart":{"type":"object","properties":{"title":{"type":"string"},"xAxisDescription":{"type":"string"},"yAxisDescription":{"type":"string"},"xValueUnit":{"type":"string"},"yValueUnit":{"type":"string"},"points":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string"},"value":{"type":"number"},"tooltip":{"type":"string"},"change":{"type":"number","nullable":true}}}}}},"availableFilters":{"type":"array","description":"Available filter options for the UI"},"headers":{"type":"array","description":"Column headers for data display"}}}}},"example":{"success":true,"accountId":"696ce9e780e0483585e4e553","profileId":"ACoAAATPlQ0Bc8wdg-Iy8wZgEDuSdIVWJiE1Aio","isLineChart":false,"filters":{"resultType":"ENGAGEMENT","timeRange":"past_7_days"},"data":{"summary":{"metrics":[{"title":"Total followers","value":"12,841","change":1,"changeDescription":"vs. prior 7 days","description":"The total number of people that currently follow you."}]},"chart":{"title":"New followers","xValueUnit":"day","yValueUnit":"New followers","points":[{"date":"Feb 18","value":9,"tooltip":"Wednesday, Feb 18, 2026","change":null},{"date":"Feb 19","value":16,"tooltip":"Thursday, Feb 19, 2026","change":77.8},{"date":"Feb 20","value":25,"tooltip":"Friday, Feb 20, 2026","change":56.3}]}}}}}},"400":{"description":"Bad Request - Invalid filters or missing required fields","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"},"example":{"success":false,"error":{"issues":[{"code":"custom","message":"startDate and endDate are required when timeRange is \"custom\"","path":["filters","startDate"]}],"name":"ZodError"}}}}},"401":{"description":"Unauthorized - Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unauthorized - Invalid credentials"}}}},"500":{"description":"Internal Server Error - Failed to get LinkedIn credentials or analytics","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Failed to get creator analytics","success":false}}}}}}},"/profile/visitors":{"post":{"operationId":"get-profile-visitors","tags":["User"],"summary":"Get profile visitors","description":"Retrieve a list of people who viewed your LinkedIn profile. Shows visitor name, headline, location, and visit timestamp. Some visitors may be anonymous depending on their privacy settings. Supports pagination and various time ranges.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["maxVisitors"],"properties":{"accountId":{"type":"string","description":"LinkedIn account ID to use. If not provided, uses the default account."},"timeRange":{"type":"string","enum":["past_7_days","past_30_days","past_90_days","past_1_year","PAST_7_DAYS","PAST_30_DAYS","PAST_90_DAYS","ALL_TIME"],"default":"past_90_days","description":"Time range to filter visitors"},"count":{"type":"number","minimum":1,"maximum":100,"default":20,"description":"Page size for pagination (visitors fetched per API request). Use maxVisitors to control the total limit."},"start":{"type":"number","minimum":0,"default":0,"description":"Pagination offset (0-indexed)"},"fetchAll":{"type":"boolean","default":false,"description":"When true, automatically paginates to fetch all visitors up to maxVisitors limit"},"maxVisitors":{"type":"number","minimum":1,"maximum":500,"default":20,"description":"Total number of visitors to retrieve. This is the primary limit - use this parameter to specify how many visitors you want (e.g., maxVisitors: 50 for 50 visitors)."}}}}}},"responses":{"200":{"description":"Profile visitors retrieved successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Whether the request was successful"},"accountId":{"type":"string","description":"LinkedIn account ID used for the request"},"visitors":{"type":"array","items":{"type":"object","properties":{"entityUrn":{"type":"string","description":"Profile URN (e.g., \"urn:li:fsd_profile:...\")"},"publicIdentifier":{"type":"string","description":"Profile URL slug (e.g., \"john-doe-123\")"},"name":{"type":"string","description":"Full name of the visitor"},"headline":{"type":"string","description":"Professional headline"},"profilePicture":{"type":"string","nullable":true,"description":"Profile picture URL (100x100 thumbnail)"},"viewedAt":{"type":"string","description":"Human-readable time since visit (e.g., \"Viewed 1h ago\", \"Viewed 2d ago\")"},"connectionDegree":{"type":"string","description":"Connection degree: \"1st\", \"2nd\", \"3rd\", or \"3rd+\""},"profileUrl":{"type":"string","description":"Full LinkedIn profile URL"}}}},"pagination":{"type":"object","properties":{"start":{"type":"number","description":"Current offset"},"count":{"type":"number","description":"Number of visitors in this response"},"total":{"type":"number","description":"Total number of visitors available"}}},"hasMore":{"type":"boolean","description":"Whether more visitors are available for pagination"},"timeRange":{"type":"string","nullable":true,"description":"Time range filter applied"},"filters":{"type":"object","description":"Filters applied to the request","properties":{"timeRange":{"type":"string"},"count":{"type":"number"},"start":{"type":"number"},"fetchAll":{"type":"boolean"},"maxVisitors":{"type":"number"}}}}},"example":{"success":true,"accountId":"696ce9e780e0483585e4e553","visitors":[{"entityUrn":"urn:li:fsd_profile:ACoAADlVtYMBLWFFOJ1ezKvVfycTiiAjoFmMK68","publicIdentifier":"ritika-kulkarni-85718922a","name":"Ritika Kulkarni","headline":"Full Stack Developer | Agentic AI | Python | Gen AI","profilePicture":"https://media.licdn.com/dms/image/v2/D4D03AQEi_YdnvAQoew/profile-displayphoto-shrink_100_100/...","viewedAt":"Viewed 47m ago","connectionDegree":"1st","profileUrl":"https://www.linkedin.com/in/ritika-kulkarni-85718922a"},{"entityUrn":"urn:li:fsd_profile:ACoAAFcWhRYB8fzl-VIyjFSFXfhToQE6M5bJiuw","publicIdentifier":"mamta-mishra-386398349","name":"Mamta Mishra","headline":"Student at Dr. Ram Manohar Lohia Awadh University","profilePicture":"https://media.licdn.com/dms/image/v2/D5603AQFdaHjJWpZbww/profile-displayphoto-shrink_100_100/...","viewedAt":"Viewed 52m ago","connectionDegree":"2nd","profileUrl":"https://www.linkedin.com/in/mamta-mishra-386398349"}],"pagination":{"start":0,"count":10,"total":8},"hasMore":false,"timeRange":null,"filters":{"timeRange":"past_90_days","count":20,"start":0,"fetchAll":false,"maxVisitors":10}}}}},"401":{"description":"Unauthorized - Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unauthorized - Invalid credentials"}}}},"403":{"description":"Access forbidden - LinkedIn Premium may be required to see all visitors","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"LinkedIn Premium required to view profile visitors"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Failed to fetch profile visitors"}}}}}}},"/people/search":{"post":{"operationId":"search-people-alias","tags":["Search"],"summary":"Search LinkedIn people (alias)","description":"Alias endpoint for /search/people. Search for LinkedIn members/professionals with extensive filtering options. Ideal for recruiting, sales prospecting, and networking. Filter by name, job title, company, location, connection degree, and more.\n\n**Recommended:** For `connectionOf` or `followerOf` filters, prefer `search-people-v2` which natively supports these filters and returns more accurate results.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"accountId":{"type":"string","description":"LinkedIn account ID to use for the search. If not provided, uses the default account."},"keywords":{"type":"string","default":"","description":"Search keywords for name, title, company, or skills"},"count":{"type":"number","minimum":1,"maximum":100,"default":25,"description":"Number of results to return per page"},"start":{"type":"number","minimum":0,"default":0,"description":"Pagination offset (0-indexed)"},"filters":{"type":"object","description":"Optional filters to narrow down search results","properties":{"firstName":{"type":"string","description":"Filter by first name"},"lastName":{"type":"string","description":"Filter by last name"},"title":{"type":"string","description":"Filter by current job title"},"company":{"type":"string","description":"Filter by current company name"},"school":{"type":"string","description":"Filter by education/school attended"},"locationId":{"type":"string","description":"Geo location filter. Pass the plain numeric `geoId` returned by POST /search/geo (the `urn:li:fsd_geo:<id>` form is also accepted). Works at any level — country, state, metro, county or city. Note: the key is `locationId`, not `location`.","example":"106981407"},"geoUrn":{"type":"string","description":"Alias for `locationId` — same geo id, same behaviour. Set one or the other, not both.","example":"106981407"},"industry":{"type":"array","items":{"type":"string"},"description":"LinkedIn industry IDs to filter by"},"connectionDegree":{"type":"array","items":{"type":"string","enum":["F","S","O"]},"description":"Filter by connection degree. F = 1st degree connections (direct contacts), S = 2nd degree connections (friends of friends), O = 3rd+ degree connections (everyone else). Combine multiple values to broaden results, e.g. [\"F\", \"S\"] for 1st and 2nd degree.","example":["F","S"]},"currentCompanyIds":{"type":"array","items":{"type":"string"},"description":"Filter by current employer company IDs"},"pastCompanyIds":{"type":"array","items":{"type":"string"},"description":"Filter by past employer company IDs"},"schoolIds":{"type":"array","items":{"type":"string"},"description":"Filter by school IDs. Look one up with `POST /search/schools`, which returns the `schoolId` this filter expects.","example":["1792"]},"profileLanguage":{"type":"array","items":{"type":"string"},"description":"Filter by profile language codes (e.g., en, es, fr)"},"serviceCategories":{"type":"array","items":{"type":"string"},"description":"Filter by service provider category ids. Look them up with `POST /search/service-categories` — the ids are opaque (Financial Analysis is `826`) and an unrecognised one returns 200 with zero results rather than an error.","example":["826"]},"openToVolunteer":{"type":"boolean","description":"Filter to only show people open to volunteering. Replaces the former `openToWork` filter — LinkedIn's current people search has no open-to-work facet, so `openToWork` now returns 400 rather than being silently ignored."},"connectionOf":{"type":"array","items":{"type":"string"},"description":"Find people connected to specific profile IDs (ACoAA format or vanity names)"},"followerOf":{"type":"array","items":{"type":"string"},"description":"Find people who follow specific profile or company URNs"}}}}}}}},"responses":{"200":{"description":"People retrieved successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"people":{"type":"array","items":{"$ref":"#/components/schemas/PeopleSearchResult"}},"totalResults":{"type":"number"}}}}}}}}},"/group-members":{"post":{"operationId":"get-group-members-legacy","tags":["Groups"],"summary":"Get group members (legacy endpoint)","description":"Legacy endpoint to retrieve members of a LinkedIn group. Supports multiple input formats: group URL, group URN, or group ID. Returns member profiles with filtering options for membership status and search. Consider using /groups/members or /groups/members-by-url for newer implementations.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"accountId":{"type":"string","description":"LinkedIn account ID to use. If not provided, uses the default account."},"groupUrl":{"type":"string","description":"Full LinkedIn group URL (e.g., \"https://www.linkedin.com/groups/12345\")"},"groupUrn":{"type":"string","description":"LinkedIn group URN (e.g., \"urn:li:fsd_group:12345\")"},"groupId":{"type":"string","description":"LinkedIn group ID (numeric ID from group URL)"},"count":{"type":"number","minimum":1,"maximum":50,"default":10,"description":"Number of members to return per page"},"start":{"type":"number","minimum":0,"default":0,"description":"Pagination offset (0-indexed)"},"membershipStatuses":{"type":"array","items":{"type":"string","enum":["OWNER","MANAGER","MEMBER"]},"default":["OWNER","MANAGER","MEMBER"],"description":"Filter by membership status: OWNER (group owners), MANAGER (group admins), MEMBER (regular members)"},"typeaheadQuery":{"type":"string","default":"","description":"Search query to filter members by name"}}}}}},"responses":{"200":{"description":"Group members retrieved successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"groupId":{"type":"string","description":"The group ID that was queried"},"accountId":{"type":"string","description":"LinkedIn account ID used for the request"},"members":{"type":"array","items":{"type":"object","properties":{"profileId":{"type":"string"},"name":{"type":"string"},"headline":{"type":"string"},"profileUrl":{"type":"string"},"profilePicture":{"type":"string"},"membershipStatus":{"type":"string"}}}},"pagination":{"type":"object","properties":{"start":{"type":"number"},"count":{"type":"number"},"returned":{"type":"number"}}},"filters":{"type":"object","properties":{"membershipStatuses":{"type":"array","items":{"type":"string"}},"typeaheadQuery":{"type":"string"}}},"message":{"type":"string"}}}}}},"400":{"description":"Invalid request - missing group identifier or invalid format","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Access forbidden - group may be private or user lacks permissions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Group not found or no active LinkedIn account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/invitations/sent":{"get":{"operationId":"get-sent-invitations","tags":["User"],"summary":"Get sent connection invitations","description":"Retrieves sent connection invitations from LinkedIn. Returns a list of pending invitations with profile information, timestamps, and invitation IDs.\n\n**Paginate with `nextStartIndex`, never by a page size of your own.** LinkedIn picks the page size and has changed it under us; a client that advances by an assumed constant skips whatever the difference is, silently, on a 200.\n\n**Stop on `endOfList: true`, not on an empty array** — an empty page with HTTP 502 / `code: INVITATIONS_UNAVAILABLE` means LinkedIn temporarily returned nothing while the account still has pending invitations, and the request should be retried.","parameters":[{"name":"accountId","in":"query","required":false,"schema":{"type":"string"},"description":"LinkedIn account ID to use. If not provided, uses the default account."},{"name":"startIndex","in":"query","required":false,"schema":{"type":"number","minimum":0,"default":0},"description":"Pagination start index (0-indexed), counted in invitations. **Advance using `nextStartIndex` from the previous response, not by a fixed step** — LinkedIn chooses the page size and it is not guaranteed to be 10."}],"responses":{"200":{"description":"Sent invitations retrieved successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"invitations":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Unique invitation identifier"},"invitationId":{"type":"string","description":"LinkedIn invitation ID"},"invitationUrn":{"type":"string","description":"LinkedIn invitation URN (e.g., \"urn:li:invitation:123456\")"},"memberId":{"type":"string","description":"LinkedIn member ID (ACoAAA format)"},"memberUrn":{"type":"string","description":"LinkedIn member URN (e.g., \"urn:li:fsd_profile:ACoAAA...\")"},"invitationType":{"type":"string","enum":["SENT","RECEIVED"],"description":"Type of invitation"},"sentAt":{"type":"string","description":"Relative timestamp (e.g., \"2 days ago\", \"1 week ago\")"},"profile":{"type":"object","properties":{"firstName":{"type":"string","description":"First name of the invited person"},"lastName":{"type":"string","description":"Last name of the invited person"},"headline":{"type":"string","description":"Professional headline"},"profileUrl":{"type":"string","description":"Full LinkedIn profile URL"},"publicIdentifier":{"type":"string","description":"LinkedIn public profile identifier"}}}}}},"count":{"type":"number","description":"Number of invitations returned"},"endOfList":{"type":"boolean","description":"true only when this page is empty AND the payload genuinely contained no invitation cards. Paginate until this is true — never stop on an empty array alone."},"startIndex":{"type":"number","description":"Current pagination start index"},"nextStartIndex":{"type":"number","description":"startIndex for the next page: where this page ended (startIndex + count). Use this and nothing else to advance — LinkedIn chooses the page size, so advancing by an assumed constant skips records."},"accountId":{"type":"string","description":"LinkedIn account ID used for the request"}}},"example":{"success":true,"invitations":[{"id":"7897046780350316544","invitationId":"7897046780350316544","invitationUrn":"urn:li:invitation:7897046780350316544","memberId":"ACoAADPy0YkBl72yFV0nJCqXd2F9OBxiI7Tvhg4","memberUrn":"urn:li:fsd_profile:ACoAADPy0YkBl72yFV0nJCqXd2F9OBxiI7Tvhg4","invitationType":"SENT","sentAt":"2 days ago","profile":{"firstName":"John","lastName":"Doe","headline":"CEO | Entrepreneur | Tech Founder","profileUrl":"https://www.linkedin.com/in/johndoe","publicIdentifier":"johndoe"}}],"count":10,"endOfList":false,"startIndex":0,"accountId":"696ce9e780e0483585e4e553"}}}},"401":{"description":"Unauthorized - invalid or missing authentication","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No active LinkedIn account found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error while fetching invitations","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"Sent invitations temporarily unavailable — LinkedIn returned invitation cards at this offset that could not be read. Retryable; do NOT treat as the end of the list, and do NOT overwrite a cached list with the empty result.","content":{"application/json":{"example":{"success":false,"code":"INVITATIONS_UNAVAILABLE","error":"Sent invitations unavailable: LinkedIn returned 10 invitation cards at startIndex 10 but none could be parsed — their card structure has changed. Retry later — do NOT treat this as the end of the list.","startIndex":10,"candidateCount":10,"accountId":"696ce9e780e0483585e4e553"}}}}}}},"/invitations/received":{"get":{"operationId":"get-received-invitations","tags":["User"],"summary":"Get received invitations","description":"Retrieves received invitations from LinkedIn including connection requests, organization follow invites, and newsletter subscriptions. Returns invitation details with category classification (CONNECTION, ORGANIZATION, NEWSLETTER, EVENT, MEMBER_FOLLOW).\n\n**Paginate with `nextStartIndex`, never by a page size of your own.** LinkedIn picks the page size and has changed it under us; a client that advances by an assumed constant skips whatever the difference is, silently, on a 200.\n\n**Stop on `endOfList: true`, not on an empty array** — an empty page with HTTP 502 / `code: INVITATIONS_UNAVAILABLE` means LinkedIn temporarily returned nothing while the account still has pending invitations, and the request should be retried.\n\n`invitationCategory` lists every value the parser can emit. Which ones you actually see depends on what the account has pending — an inbox with no event invites never returns `EVENT`, and that is not a fault.\n\nTo act on an invitation, pass its `invitationId`, `validationToken` and `linkedInInvitationType` to `POST /invitations/received/respond`.","parameters":[{"name":"accountId","in":"query","required":false,"schema":{"type":"string"},"description":"LinkedIn account ID to use. If not provided, uses the default account."},{"name":"startIndex","in":"query","required":false,"schema":{"type":"number","minimum":0,"default":0},"description":"Pagination start index (0-indexed), counted in invitations. **Advance using `nextStartIndex` from the previous response, not by a fixed step** — LinkedIn chooses the page size (10 today) and it is not guaranteed."}],"responses":{"200":{"description":"Received invitations retrieved successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"invitations":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Unique invitation identifier"},"invitationId":{"type":"string","description":"LinkedIn invitation ID"},"invitationUrn":{"type":"string","description":"LinkedIn invitation URN"},"memberId":{"type":"string","description":"LinkedIn member ID. Empty for organization/newsletter invitations."},"memberUrn":{"type":"string","description":"LinkedIn member URN (e.g., \"urn:li:fsd_profile:ACoAAA...\"). Empty for organization/newsletter invitations."},"invitationType":{"type":"string","enum":["RECEIVED"],"description":"Always RECEIVED"},"invitationCategory":{"type":"string","enum":["CONNECTION","ORGANIZATION","NEWSLETTER","EVENT","MEMBER_FOLLOW","UNKNOWN"],"description":"Category of the invitation"},"linkedInInvitationType":{"type":"string","description":"Raw LinkedIn invitation type (e.g., GenericInvitationType_CONNECTION)"},"validationToken":{"type":"string","description":"Token required for accept/ignore actions"},"receivedAt":{"type":"string","description":"Relative timestamp (e.g., \"2 weeks ago\")"},"customMessage":{"type":"string","description":"Custom message included with the invitation"},"newsletterName":{"type":"string","description":"Newsletter name (only for NEWSLETTER category)"},"companyName":{"type":"string","description":"Company name (only for ORGANIZATION/NEWSLETTER categories)"},"profile":{"type":"object","properties":{"firstName":{"type":"string","description":"First name of the inviter"},"lastName":{"type":"string","description":"Last name of the inviter"},"profileUrl":{"type":"string","description":"Full LinkedIn profile URL"},"publicIdentifier":{"type":"string","description":"LinkedIn public profile identifier"}}}}}},"count":{"type":"number","description":"Number of invitations returned"},"endOfList":{"type":"boolean","description":"true only when this page is empty AND the page genuinely contained no invitation cards AND the account has no pending connection requests beyond startIndex. Paginate until this is true — never stop on an empty array alone."},"startIndex":{"type":"number","description":"Current pagination start index"},"nextStartIndex":{"type":"number","description":"startIndex for the next page: where this page ended (startIndex + count). Use this and nothing else to advance — LinkedIn chooses the page size, so advancing by an assumed constant skips records."},"accountId":{"type":"string","description":"LinkedIn account ID used"}}},"example":{"success":true,"endOfList":false,"invitations":[{"id":"7455891636745551899","invitationId":"7455891636745551899","invitationUrn":"urn:li:invitation:7455891636745551899","memberId":"","memberUrn":"","invitationType":"RECEIVED","invitationCategory":"ORGANIZATION","linkedInInvitationType":"GenericInvitationType_ORGANIZATION","validationToken":"C712w50Z","receivedAt":"2 weeks ago","companyName":"ZenX8Studio","profile":{"firstName":"Yash","lastName":"Jadhav","profileUrl":"https://www.linkedin.com/in/yash-jadhav-a03656326/","publicIdentifier":"yash-jadhav-a03656326"}},{"id":"7455163451166860117","invitationId":"7455163451166860117","invitationUrn":"urn:li:invitation:7455163451166860117","memberId":"","memberUrn":"","invitationType":"RECEIVED","invitationCategory":"NEWSLETTER","linkedInInvitationType":"GenericInvitationType_ORGANIZATION","validationToken":"EJx1vfKg","receivedAt":"2 weeks ago","newsletterName":"VAYUZ Insights","profile":{"firstName":"Natalya","lastName":"Singh","profileUrl":"","publicIdentifier":""}},{"id":"7455301624689844224","invitationId":"7455301624689844224","invitationUrn":"urn:li:invitation:7455301624689844224","memberId":"ACoAAAYHg8oBNy-PmYu4ZIXcv5yOcNldwpl8lj8","memberUrn":"urn:li:fsd_profile:ACoAAAYHg8oBNy-PmYu4ZIXcv5yOcNldwpl8lj8","invitationType":"RECEIVED","invitationCategory":"CONNECTION","linkedInInvitationType":"GenericInvitationType_CONNECTION","validationToken":"jywembME","receivedAt":"2 weeks ago","profile":{"firstName":"Deepika","lastName":"Khare","profileUrl":"https://www.linkedin.com/in/deepika-khare/","publicIdentifier":"deepika-khare"}}],"count":10,"startIndex":0,"accountId":"69da1eacf365891afa0426a4"}}}},"401":{"description":"Unauthorized - invalid or missing authentication","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error while fetching received invitations","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"Received invitations temporarily unavailable — the list came back empty but the account still has pending invitations. Retryable; do NOT treat as the end of the list. Two cases, told apart by which field is present: `candidateCount` means LinkedIn returned invitation cards we could not read, `pendingCount` means it returned no cards at all while the account has pending connection requests.","content":{"application/json":{"examples":{"unreadableCards":{"summary":"Cards present but unparseable (candidateCount)","value":{"success":false,"code":"INVITATIONS_UNAVAILABLE","error":"Received invitations unavailable: LinkedIn returned 10 invitation cards at startIndex 0 but none could be parsed — their card structure has changed. Retry later — do NOT treat this as the end of the list.","startIndex":0,"candidateCount":10,"accountId":"69da1eacf365891afa0426a4"}},"emptyPageWithPending":{"summary":"No cards returned while invitations are pending (pendingCount)","value":{"success":false,"code":"INVITATIONS_UNAVAILABLE","error":"Received invitations unavailable: LinkedIn returned no invitations at startIndex 0 but the account has 9 pending. Retry later — do NOT treat this as the end of the list.","startIndex":0,"pendingCount":9,"accountId":"69da1eacf365891afa0426a4"}}}}}}}}},"/invitations/received/respond":{"post":{"operationId":"respond-to-received-invitation","tags":["Actions"],"summary":"Accept or ignore a received invitation","description":"Accepts or ignores a received invitation on LinkedIn. Works for all invitation types: connection requests, organization follows, newsletter subscriptions, and events. Use the data from GET /invitations/received to populate the required fields.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["action","invitationId","validationToken","linkedInInvitationType","firstName","lastName"],"properties":{"accountId":{"type":"string","description":"LinkedIn account ID to use. If not provided, uses the default account."},"action":{"type":"string","enum":["ACCEPT","IGNORE"],"description":"Action to perform on the invitation"},"invitationId":{"type":"string","description":"LinkedIn invitation ID from the received invitation"},"validationToken":{"type":"string","description":"Validation token from the received invitation"},"linkedInInvitationType":{"type":"string","description":"LinkedIn invitation type (e.g., GenericInvitationType_CONNECTION, GenericInvitationType_ORGANIZATION)"},"firstName":{"type":"string","description":"First name of the inviter"},"lastName":{"type":"string","description":"Last name of the inviter"},"profileId":{"type":"string","description":"LinkedIn member ID (ACoAAA format). Optional, required for connection invitations."}}},"examples":{"accept-connection":{"summary":"Accept a connection request","value":{"action":"ACCEPT","invitationId":"7455301624689844224","validationToken":"jywembME","linkedInInvitationType":"GenericInvitationType_CONNECTION","firstName":"Deepika","lastName":"Khare","profileId":"ACoAAAYHg8oBNy-PmYu4ZIXcv5yOcNldwpl8lj8"}},"ignore-newsletter":{"summary":"Ignore a newsletter invitation","value":{"action":"IGNORE","invitationId":"7455163451166860117","validationToken":"EJx1vfKg","linkedInInvitationType":"GenericInvitationType_ORGANIZATION","firstName":"Natalya","lastName":"Singh"}},"ignore-organization":{"summary":"Ignore an organization follow invite","value":{"action":"IGNORE","invitationId":"7455891636745551899","validationToken":"C712w50Z","linkedInInvitationType":"GenericInvitationType_ORGANIZATION","firstName":"Yash","lastName":"Jadhav"}}}}}},"responses":{"200":{"description":"Invitation responded to successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Whether the action was successful"},"message":{"type":"string","description":"Success message with action details"},"accountId":{"type":"string","description":"LinkedIn account ID used"}}},"example":{"success":true,"message":"Invitation from Deepika Khare accepted","accountId":"69da1eacf365891afa0426a4"}}}},"401":{"description":"Unauthorized - invalid or missing authentication","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error while responding to invitation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/invitations/withdraw":{"post":{"operationId":"withdraw-invitation","tags":["Actions"],"summary":"Withdraw a sent connection invitation","description":"Withdraws a pending connection invitation that was previously sent to a LinkedIn user. Only the profileId is required - other fields will be auto-fetched from the profile if not provided. The API will verify that a pending invitation exists before attempting withdrawal.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["profileId"],"properties":{"accountId":{"type":"string","description":"LinkedIn account ID to use. If not provided, uses the default account."},"profileId":{"type":"string","description":"LinkedIn profile vanity URL slug (the part after linkedin.com/in/) of the person whose invitation to withdraw. This is the only required field."},"memberId":{"type":"string","description":"LinkedIn member ID (ACoAAA format). Optional - will be auto-fetched from profile if not provided."},"firstName":{"type":"string","description":"First name of the person. Optional - will be auto-fetched from profile if not provided."},"lastName":{"type":"string","description":"Last name of the person. Optional - will be auto-fetched from profile if not provided."},"profileUrn":{"type":"string","description":"LinkedIn profile URN. Optional - will be auto-fetched from profile if not provided."},"invitationId":{"type":"string","description":"LinkedIn invitation ID. Optional - will be auto-fetched from profile if not provided."}}},"example":{"profileId":"johndoe"}}}},"responses":{"200":{"description":"Invitation withdrawn successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Whether the withdrawal was successful"},"message":{"type":"string","description":"Success or error message"},"accountId":{"type":"string","description":"LinkedIn account ID used for the request"}}},"example":{"success":true,"message":"Successfully withdrew invitation to John Doe","accountId":"696ce9e780e0483585e4e553"}}}},"400":{"description":"Bad request - no pending invitation found or missing required fields","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"success":{"type":"boolean"},"profile":{"type":"object","description":"Profile information when no pending invitation exists","properties":{"firstName":{"type":"string"},"lastName":{"type":"string"},"profileId":{"type":"string"},"isConnected":{"type":"boolean"},"invitationReceived":{"type":"boolean"}}}}},"example":{"error":"No pending invitation found for this profile","success":false,"profile":{"firstName":"John","lastName":"Doe","profileId":"johndoe","isConnected":false,"invitationReceived":false}}}}},"401":{"description":"Unauthorized - invalid or missing authentication","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Profile not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Profile not found","success":false}}}},"500":{"description":"Server error while withdrawing invitation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/connections":{"get":{"operationId":"get-connections","tags":["User"],"summary":"Get connections list","description":"Retrieves the list of LinkedIn connections for the authenticated user. Returns connection details including name, headline, vanity name, and connected date.\n\n**Paginate with `nextStartIndex`, never by your requested `limit`.** LinkedIn serves a fixed page size of its own choosing; `limit` can narrow that page but never widen it, so advancing by the limit you asked for skips whatever you did not get.\n\n**Stop on `endOfList: true`, not on an empty array** — an empty page with HTTP 502 / `code: CONNECTIONS_UNAVAILABLE` means LinkedIn temporarily returned nothing and the request should be retried.","parameters":[{"name":"accountId","in":"query","required":false,"schema":{"type":"string"},"description":"LinkedIn account ID to use. If not provided, uses the default account."},{"name":"startIndex","in":"query","required":false,"schema":{"type":"number","minimum":0,"default":0},"description":"Starting index for pagination (0-indexed), counted in connections. **Advance using `nextStartIndex` from the previous response, never by your requested `limit`** — see `limit`."},{"name":"limit","in":"query","required":false,"schema":{"type":"number","minimum":1,"maximum":12,"default":10},"description":"Upper bound on how many connections to return. LinkedIn serves a fixed page (10 today) and offers no page-size control, so `limit` can only NARROW that page — asking for 12 returns 10. Read `count` for what you actually got and `nextStartIndex` for where to continue."},{"name":"sortBy","in":"query","required":false,"schema":{"type":"string","enum":["recentlyAdded","firstName","lastName"],"default":"recentlyAdded"},"description":"Sort order for connections. Options: recentlyAdded (default), firstName, lastName."}],"responses":{"200":{"description":"Connections list retrieved successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"connections":{"type":"array","items":{"type":"object","properties":{"firstName":{"type":"string","description":"First name of the connection"},"lastName":{"type":"string","description":"Last name of the connection"},"vanityName":{"type":"string","description":"LinkedIn vanity name (profile slug)"},"headline":{"type":"string","description":"Professional headline"},"connectedDate":{"type":"string","description":"Date when the connection was made"},"profileUrl":{"type":"string","description":"Full LinkedIn profile URL"}}}},"count":{"type":"number","description":"Number of connections actually returned on this page"},"endOfList":{"type":"boolean","description":"true only when this page is empty AND the account has no connections beyond startIndex (verified against the exact connection count). Paginate until this is true — never stop on an empty array alone."},"startIndex":{"type":"number","description":"Starting index used for this request"},"nextStartIndex":{"type":"number","description":"startIndex for the next page: where this page ended (startIndex + count). Use this and nothing else to advance — deriving the next offset from your requested `limit` skips the connections LinkedIn did not serve."},"limit":{"type":"number","description":"Number of items requested"},"sortBy":{"type":"string","enum":["recentlyAdded","firstName","lastName"],"description":"Sort order applied to connections"},"accountId":{"type":"string","description":"LinkedIn account ID used for the request"}}},"example":{"success":true,"endOfList":false,"connections":[{"firstName":"John","lastName":"Doe","vanityName":"johndoe","headline":"CEO | Entrepreneur | Tech Founder","connectedDate":"Connected on Feb 15, 2024","profileUrl":"https://www.linkedin.com/in/johndoe/"},{"firstName":"Jane","lastName":"Smith","vanityName":"janesmith","headline":"VP of Engineering at TechCorp","connectedDate":"Connected on Jan 10, 2024","profileUrl":"https://www.linkedin.com/in/janesmith/"}],"startIndex":0,"limit":10,"sortBy":"recentlyAdded","accountId":"696ce9e780e0483585e4e553"}}}},"401":{"description":"Unauthorized - invalid or missing authentication","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error while fetching connections","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"Connections list temporarily unavailable — LinkedIn returned no connections at this startIndex even though the account has more. Retryable; do NOT treat as the end of the list.","content":{"application/json":{"example":{"success":false,"code":"CONNECTIONS_UNAVAILABLE","error":"Connections list unavailable: LinkedIn returned no connections at startIndex 3324 but the account has 3419. Retry later — do NOT treat this as the end of the list.","startIndex":3324,"connectionCount":3419,"accountId":"696ce9e780e0483585e4e553"}}}}}}},"/profile/visit":{"post":{"operationId":"visit-profile","tags":["Profiles"],"summary":"Visit a LinkedIn profile","description":"Simulates visiting a LinkedIn profile to trigger the \"Who Viewed Your Profile\" notification for the target user. This sends the necessary telemetry events that LinkedIn uses to track profile views.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["profileId"],"properties":{"profileId":{"type":"string","description":"LinkedIn profile vanity URL slug of the user to visit (the part after linkedin.com/in/, e.g., \"john-doe-123\")"},"accountId":{"type":"string","description":"LinkedIn account ID to use for the visit. If not provided, uses the default account."}}},"example":{"profileId":"john-doe-123"}}}},"responses":{"200":{"description":"Profile visit registered successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Whether the profile visit was successful"},"profileId":{"type":"string","description":"The profile ID that was visited"},"profileUrn":{"type":"string","description":"The extracted profile URN (ACoAA format)"},"numericMemberId":{"type":"number","description":"The numeric member ID extracted from the profile URN"},"message":{"type":"string","description":"Success or status message"},"accountId":{"type":"string","description":"LinkedIn account ID that performed the visit"}}},"example":{"success":true,"profileId":"john-doe-123","profileUrn":"ACoAAF7lRPsBwzO9AMvoBVVioq4MmyJUyfiXEqY","numericMemberId":1592083707,"message":"Profile visit registered successfully","accountId":"696ce9e780e0483585e4e553"}}}},"400":{"description":"Failed to visit profile - could not extract profile URN or send telemetry","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":"string"},"profileUrn":{"type":"string"},"accountId":{"type":"string"}}},"example":{"success":false,"error":"Failed to fetch profile URN","accountId":"696ce9e780e0483585e4e553"}}}},"401":{"description":"Unauthorized - Invalid or missing authentication","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unauthorized"}}}},"500":{"description":"Server error - Failed to get LinkedIn credentials or internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Failed to get LinkedIn credentials","success":false}}}}}}},"/posts/upload-and-post":{"post":{"operationId":"upload-and-post","tags":["Posts"],"summary":"Upload media and create a LinkedIn post (server-side)","description":"Downloads a file from the provided URL, uploads it to LinkedIn server-side, and creates a post. This is a single-step alternative to the multi-step upload/init + upload + create flow. Maximum file size is 100MB. Designed for MCP and API clients that cannot perform direct pre-signed URL uploads.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["fileUrl","text","mediaType"],"properties":{"accountId":{"type":"string","description":"LinkedIn account ID. If omitted, uses the default account."},"fileUrl":{"type":"string","description":"Public URL of the file to download and upload to LinkedIn. Maximum 100MB.","example":"https://example.com/photo.jpg"},"text":{"type":"string","description":"Post text content.","example":"Check out this image!"},"mediaType":{"type":"string","enum":["image","video"],"description":"Type of media being uploaded."},"visibility":{"type":"string","enum":["ANYONE","CONNECTIONS_ONLY"],"default":"ANYONE","description":"Post visibility."},"altText":{"type":"string","default":"","description":"Alt text for images."}}}}}},"responses":{"200":{"description":"Media uploaded and post created successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"postUrn":{"type":"string","description":"URN of the created post."},"shareUrn":{"type":"string","description":"Share URN of the created post."},"accountId":{"type":"string","description":"LinkedIn account ID used."}}},"example":{"success":true,"postUrn":"urn:li:share:7123456789","shareUrn":"urn:li:share:7123456789","accountId":"696ce9e780e0483585e4e553"}}}},"400":{"description":"Bad request - file too large or post creation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error - failed to download file, upload to LinkedIn, or create post","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/posts/upload/init":{"post":{"operationId":"upload-init","tags":["Posts"],"summary":"Initialize media upload — get pre-signed URL(s)","description":"Returns pre-signed URL(s) from LinkedIn for direct client upload. For images, documents, and small videos: returns a single uploadUrl. For large videos: returns partUploadRequests (4MB chunks). The API never touches media bytes — the client uploads directly to LinkedIn. **Documents (PDF):** after PUTting the bytes, poll GET /posts/upload/document-status until ready before calling /posts/create. **Company page:** to author the post as a company page you administer, pass companyUrn here so the asset is registered to the organization — required, otherwise the company-authored post silently fails to publish.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["mediaType","fileSize","filename"],"properties":{"accountId":{"type":"string","description":"LinkedIn account ID."},"mediaType":{"type":"string","enum":["image","video","document"],"description":"image / video / document (PDF). Documents upload as SINGLE and require a processing poll (see /posts/upload/document-status)."},"fileSize":{"type":"number","description":"File size in bytes."},"filename":{"type":"string","description":"Filename with extension.","example":"photo.jpg"},"companyUrn":{"type":"string","description":"Optional. When the post will be authored by a company page you administer, pass the company URN here so the uploaded asset is registered with the organization as owner. Required for company-page image/video posts — without it LinkedIn accepts the later share (HTTP 200) but silently creates no post (no share URN). Must match the companyUrn passed to /posts/create.","example":"urn:li:fsd_company:107524277"}}}}}},"responses":{"200":{"description":"Upload initialized. Client should PUT media bytes directly to the returned URL(s).","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"uploadType":{"type":"string","enum":["SINGLE","MULTIPART"]},"assetUrn":{"type":"string","description":"Pass this to /posts/create."},"recipes":{"type":"array","items":{"type":"string"}},"uploadUrl":{"type":"string","description":"Pre-signed URL for SINGLE uploads."},"uploadHeaders":{"type":"object","description":"Headers to include in the PUT request."},"mediaArtifactUrn":{"type":"string","description":"For MULTIPART — pass to /upload/complete."},"partUploadRequests":{"type":"array","description":"For MULTIPART — chunk upload URLs."},"multipartMetadata":{"type":"string","description":"For MULTIPART — pass to /upload/complete."}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/posts/upload/complete":{"post":{"operationId":"upload-complete","tags":["Posts"],"summary":"Complete multipart video upload","description":"Call after all video chunks have been PUT to the pre-signed URLs. Only needed for MULTIPART uploads (large videos).","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["mediaArtifactUrn","partUploadResponses"],"properties":{"accountId":{"type":"string"},"mediaArtifactUrn":{"type":"string","description":"From /upload/init response."},"multipartMetadata":{"type":"string","description":"From /upload/init response."},"partUploadResponses":{"type":"array","items":{"type":"object","properties":{"httpStatusCode":{"type":"number"},"headers":{"type":"object"}}}}}}}}},"responses":{"200":{"description":"Upload finalized","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/posts/upload/document-status":{"get":{"operationId":"upload-document-status","tags":["Posts"],"summary":"Check document (PDF) processing status","description":"After PUTting a document to the pre-signed URL from /posts/upload/init, poll this until status is READY. LinkedIn rasterizes each page into preview images first; the document cannot be posted until it is READY. Only needed for mediaType=document.","parameters":[{"name":"assetUrn","in":"query","required":true,"schema":{"type":"string"},"description":"Document asset URN from /posts/upload/init.","example":"urn:li:digitalmediaAsset:D561FAQ..."},{"name":"accountId","in":"query","required":false,"schema":{"type":"string"},"description":"LinkedIn account ID. If omitted, uses the default account."}],"responses":{"200":{"description":"Current processing status.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status":{"type":"string","enum":["PROCESSING","READY","FAILED"]},"ready":{"type":"boolean","description":"true when status is READY (safe to call /posts/create)."}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/posts/create":{"post":{"operationId":"create-post","tags":["Posts"],"summary":"Create a LinkedIn post","description":"Publishes a LinkedIn post. The mediaType field decides what you post. Text (none): send text only, one call with no upload. Image (image): upload via /posts/upload/init, then pass assetUrn (and optional altText). Carousel (image): upload each image, then pass 2 to 9 of them in assetUrns. Video (video): upload, then pass assetUrn and recipes. Document/PDF (document): upload, poll /posts/upload/document-status until READY, then pass assetUrn, recipes, and title. New to the API? The step-by-step guide with cURL, JavaScript, and Python examples for each post type lives at /docs/api/posting. To publish as a company page you administer, pass companyUrn (the company URN).","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["text"],"properties":{"accountId":{"type":"string","description":"Which connected account posts. Omit to use the default account."},"text":{"type":"string","description":"The post body (max 3,000 characters).","example":"Try ConnectSafely.AI Today"},"visibility":{"type":"string","enum":["ANYONE","CONNECTIONS_ONLY"],"default":"ANYONE","description":"Who can see the post."},"mediaType":{"type":"string","enum":["none","image","video","document"],"default":"none","description":"What you are posting. Use \"image\" for a carousel too (with assetUrns)."},"assetUrn":{"type":"string","description":"Single uploaded asset from /posts/upload/init. Required for image / video / document (a single image, video, or PDF)."},"assetUrns":{"type":"array","description":"Carousel only: 2–9 uploaded images. Each item is { assetUrn, altText? }. Use instead of assetUrn with mediaType \"image\".","items":{"type":"object","properties":{"assetUrn":{"type":"string"},"altText":{"type":"string"}}}},"recipes":{"type":"array","items":{"type":"string"},"description":"From /posts/upload/init. Required for video and document."},"altText":{"type":"string","default":"","description":"Accessibility alt text for a single image (mediaType \"image\")."},"title":{"type":"string","description":"Document title shown on the post (mediaType \"document\").","example":"Our 2026 Product Roadmap"},"companyUrn":{"type":"string","description":"Optional. Post as a company page you administer instead of your personal profile. Provide the company URN (e.g., \"urn:li:fsd_company:105672170\").","example":"urn:li:fsd_company:105672170"}}}}}},"responses":{"200":{"description":"Post created","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"postUrn":{"type":"string"},"shareUrn":{"type":"string"},"accountId":{"type":"string"},"companyUrn":{"type":"string","description":"Company URN the post was authored as (only present when posting as a company page)."}}}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/posts/schedule":{"post":{"operationId":"schedule-post","tags":["Posts"],"summary":"Schedule a LinkedIn post for later","description":"Queues a post for publication, optionally boosting it once it goes live. Omit `scheduledAt` to publish as soon as possible; supply it to schedule for later. Prefer this over create-post whenever you want a first comment, reposts or a boost attached, since those only exist here.\n\n`scheduledAt` is an absolute UTC timestamp and must be in the future. `timezone` is an IANA name (for example \"Asia/Kolkata\"); it is stored with the post and decides which local day a boost slot falls on, but it never changes when the post publishes — `scheduledAt` alone does that.\n\n**Media the easy way:** set `mediaType` and pass `fileUrl` — a public URL to the image or video — and the server downloads it and uploads it to LinkedIn for you. No pre-signed upload, no multipart form, one call. Limit 30MB.\n\nIf you already uploaded through /posts/upload/init you can pass `assetUrn` instead (or 2-9 entries in `assetUrns` for a carousel, or `assetUrn` plus `recipes` for a video). Pass `fileUrl` or the pre-uploaded URNs, never both. Documents/PDFs and polls cannot be scheduled — publish a document immediately with create-post instead.\n\n**Boost** (optional) fires after the post is live and needs a paid plan: free plans get 403 and can only boost an already published post from the web app. Boost targets are clamped to the plan caps rather than rejected, so read the `boost` object in the response to see what was actually applied. A full boost quota returns 409 — call get-scheduled-week first to see which days still have a slot.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["text"],"properties":{"accountId":{"type":"string","description":"Which connected account posts. Omit to use the default account."},"text":{"type":"string","description":"The post body (max 3,000 characters).","example":"Three things we learned shipping our API this quarter."},"scheduledAt":{"type":"string","format":"date-time","description":"When to publish, as an absolute UTC timestamp in the future. OMIT IT TO PUBLISH NOW — which still gives you the first comment on its natural delay, plus reposts and boost, in a single call. The response returns status PENDING with an id; read list-scheduled-posts for publishedUrl once it goes live.","example":"2026-09-15T09:30:00.000Z"},"timezone":{"type":"string","description":"IANA timezone name stored with the post and used for boost day windows. Defaults to UTC.","example":"Asia/Kolkata"},"companyUrn":{"type":"string","description":"Optional. Publish as a company page you administer instead of your personal profile.","example":"urn:li:fsd_company:105672170"},"mediaType":{"type":"string","enum":["none","image","video"],"default":"none","description":"What you are posting. Use \"image\" for a carousel too (with assetUrns). Documents are not schedulable."},"fileUrl":{"type":"string","description":"Public URL of the image or video. The server downloads it and uploads it to LinkedIn, so you do not need to upload anything yourself. Max 30MB. Use this OR assetUrn/assetUrns, not both.","example":"https://example.com/chart.png"},"assetUrn":{"type":"string","description":"Alternative to fileUrl: an asset you already uploaded via /posts/upload/init."},"assetUrns":{"type":"array","description":"Carousel only: 2-9 uploaded images, each { assetUrn, altText? }. Use instead of assetUrn with mediaType \"image\".","items":{"type":"object","properties":{"assetUrn":{"type":"string"},"altText":{"type":"string"}}}},"recipes":{"type":"array","items":{"type":"string"},"description":"From /posts/upload/init. Required for a pre-uploaded video; not needed with fileUrl."},"thumbnailUrl":{"type":"string","description":"Preview image shown in the ConnectSafely composer. Generated for you when you pass an image fileUrl. Pass it yourself for video, or when supplying a pre-uploaded assetUrn — without one the composer shows no media for the post (the published post is unaffected).","example":"https://example.com/poster.jpg"},"altText":{"type":"string","default":"","description":"Accessibility alt text for the image. Always set this."},"firstComment":{"type":"string","description":"Posted as a comment 20-30 seconds after the post goes live."},"reposts":{"type":"array","description":"Other connected accounts that reshare this post after a delay.","items":{"type":"object","properties":{"accountId":{"type":"string"},"delayHours":{"type":"number","description":"Fractional hours, e.g. 3.2 = 3h 12m."},"companyUrn":{"type":"string"}}}},"boost":{"type":"object","description":"Optional engagement boost that runs after the post publishes. Requires a paid plan.","properties":{"enabled":{"type":"boolean","default":true},"boostAt":{"type":"string","format":"date-time","description":"When the boost fires. Defaults to the post time, and is always bumped to at least 2 minutes after it."},"targetEngagements":{"type":"integer","description":"Reactions to aim for. Clamped to your plan cap.","example":50},"targetLikes":{"type":"integer"},"targetComments":{"type":"integer"},"enableLikes":{"type":"boolean"},"enableComments":{"type":"boolean"},"customComments":{"type":"array","items":{"type":"string"}},"channelIds":{"type":"array","items":{"type":"string"},"description":"Pod ids to draw engagers from."},"locationFilter":{"type":"array","items":{"type":"object","properties":{"country":{"type":"string"},"countryName":{"type":"string"}}},"description":"Restrict engagers by country."},"reactionMix":{"type":"object","description":"Integer percentages summing to 100 across LIKE, PRAISE, EMPATHY, APPRECIATION, INTEREST. Omit for 100% LIKE."},"minDelaySeconds":{"type":"integer","default":60},"maxDelaySeconds":{"type":"integer","default":120},"commentLength":{"type":"string","example":"medium"}}}}}}}},"responses":{"200":{"description":"Post scheduled","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"id":{"type":"string","description":"Scheduled post id — pass it to delete-scheduled-post to cancel."},"status":{"type":"string","example":"PENDING"},"scheduledAt":{"type":"string","format":"date-time"},"timezone":{"type":"string"},"accountId":{"type":"string"},"boost":{"type":"object","description":"Present only when a boost was attached. Shows the targets actually applied after plan caps were enforced, which may be lower than requested.","properties":{"id":{"type":"string"},"targetLikes":{"type":"integer"},"targetComments":{"type":"integer"},"enableLikes":{"type":"boolean"},"enableComments":{"type":"boolean"},"scheduledFor":{"type":"string","format":"date-time"}}}}}}}},"400":{"description":"Validation failed, the time is in the past, the account cannot post, or fileUrl could not be downloaded / was over 30MB","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Boost requires a paid plan. Free plans cannot schedule a boost at all — this is not a quota that clears with time.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Boost quota for that window is already spent. The body carries quota usage; get-scheduled-week shows which days are still open.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The scheduler queue is unavailable and nothing was queued — retry.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/posts/schedule/list":{"get":{"operationId":"list-scheduled-posts","tags":["Posts"],"summary":"List scheduled posts","description":"Every scheduled post you can see — your own plus any in a workspace you belong to. A database-only read that makes no LinkedIn call, so it spends none of your LinkedIn quota. It is still paced like every other call on the account (roughly one every 3 seconds), so read it when you need it rather than polling it in a loop.\n\nUse it to check whether a post went out: `status` is DRAFT, PENDING, PUBLISHED or FAILED, `publishedUrl` is set once it is live, and `lastError` carries the reason a FAILED post did not publish.","security":[{"bearerAuth":[]}],"parameters":[{"name":"accountId","in":"query","required":false,"schema":{"type":"string"},"description":"Only posts for this connected account."},{"name":"from","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Earliest scheduledAt to include."},{"name":"to","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Latest scheduledAt to include."},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["DRAFT","PENDING","PUBLISHED","FAILED"]}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":50,"maximum":200}}],"responses":{"200":{"description":"Scheduled posts","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"count":{"type":"integer"},"posts":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"accountId":{"type":"string"},"content":{"type":"string"},"scheduledAt":{"type":"string","format":"date-time"},"timezone":{"type":"string"},"status":{"type":"string","enum":["DRAFT","PENDING","PUBLISHED","FAILED"]},"publishedUrl":{"type":"string"},"publishedAt":{"type":"string","format":"date-time"},"lastError":{"type":"string"},"attempts":{"type":"integer"},"boostPostId":{"type":"string"},"companyUrn":{"type":"string"}}}}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/posts/schedule/week":{"get":{"operationId":"get-scheduled-week","tags":["Posts"],"summary":"What is queued this week, and whether a boost slot is free","description":"One call that answers both \"what is going out this week\" and \"can I boost, and on which day\". Call it before schedule-post so you pick a boost time that has a slot instead of absorbing a 409.\n\nA database-only read: no LinkedIn call and no LinkedIn quota spent. It is still paced like every other call on the account (roughly one every 3 seconds), so call it before scheduling rather than polling it in a loop.\n\n`scheduledPosts` counts posts by status in the week and buckets them by local day. `boosts.days` gives `used`/`limit`/`hasSlot` for each of the seven days — this is the same per-day view the web calendar uses. `boosts.quota` and `boosts.earliestOpenSlot` describe the window right now: when `canBoostNow` is false, `earliestOpenSlot` is when the next slot frees up.\n\nNote that a boost window is NOT a calendar week. Depending on plan it is a local calendar day (multi-boost plans), a window centred on the boost time (Starter/Pro), or a rolling 7 days (free and trial) — so read `quota.limit` as the limit for that window, not for the week.","security":[{"bearerAuth":[]}],"parameters":[{"name":"accountId","in":"query","required":false,"schema":{"type":"string"},"description":"Scopes the post list and picks the workspace whose boost quota is reported. Omit to use the default account."},{"name":"weekStart","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Start of the 7-day window. Defaults to Monday 00:00 UTC of the current week."},{"name":"timezone","in":"query","required":false,"schema":{"type":"string"},"description":"IANA name deciding which local day each post and boost slot falls on. Defaults to UTC.","example":"Asia/Kolkata"}],"responses":{"200":{"description":"Week view","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"weekStart":{"type":"string","format":"date-time"},"weekEnd":{"type":"string","format":"date-time"},"timezone":{"type":"string"},"scheduledPosts":{"type":"object","properties":{"pending":{"type":"integer"},"published":{"type":"integer"},"failed":{"type":"integer"},"draft":{"type":"integer"},"byDay":{"type":"object","description":"Local YYYY-MM-DD to number of posts."},"posts":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"accountId":{"type":"string"},"scheduledAt":{"type":"string","format":"date-time"},"status":{"type":"string"},"preview":{"type":"string"},"publishedUrl":{"type":"string"},"lastError":{"type":"string"},"boostPostId":{"type":"string"}}}}}},"boosts":{"type":"object","properties":{"pendingThisWeek":{"type":"integer","description":"Boosts occupying this week — queued, running, or reserved for a future post."},"canBoostNow":{"type":"boolean"},"reason":{"type":"string","description":"Why not, when canBoostNow is false: QUOTA, PLAN, PLAN_SCHEDULE, PAUSED, WORKSPACE_MISSING."},"helpText":{"type":"string"},"earliestOpenSlot":{"type":"string","format":"date-time","description":"When the next boost slot opens. Null when the block is not a quota one."},"quota":{"type":"object","properties":{"used":{"type":"integer"},"limit":{"type":"integer"},"windowStart":{"type":"string","format":"date-time"},"windowEnd":{"type":"string","format":"date-time"},"currentPlan":{"type":"string"}}},"days":{"type":"array","description":"Per-day boost slots across the week.","items":{"type":"object","properties":{"date":{"type":"string","example":"2026-09-15"},"used":{"type":"integer"},"limit":{"type":"integer"},"hasSlot":{"type":"boolean"}}}}}}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/posts/schedule/{id}":{"delete":{"operationId":"delete-scheduled-post","tags":["Posts"],"summary":"Cancel a scheduled post","description":"Cancels a post that has not published yet and removes everything queued alongside it — the publish job, any reposts, and a reserved boost (which frees its quota slot).\n\nAn already published post cannot be deleted: the request returns 409, because removing the record would not unpublish the post from LinkedIn.","security":[{"bearerAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Scheduled post id, as returned by schedule-post or list-scheduled-posts.","example":"68f0a1b2c3d4e5f60718293a"}],"responses":{"200":{"description":"Cancelled","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"id":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not visible to you","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Already published — cannot be cancelled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/inmail/credits":{"get":{"summary":"Get InMail credits","description":"Get the InMail credits balance for a LinkedIn account. Automatically detects account type (Sales Navigator, Business Premium, Recruiter) and uses the appropriate API. Non-premium accounts return 0 credits.","operationId":"get-inmail-credits","tags":["InMail"],"security":[{"bearerAuth":[]}],"parameters":[{"name":"accountId","in":"query","required":false,"schema":{"type":"string"},"description":"LinkedIn account ID. If omitted, uses the default account."},{"name":"debug","in":"query","required":false,"schema":{"type":"string","enum":["true","false"]},"description":"Set to \"true\" to include raw API response in the result."}],"responses":{"200":{"description":"InMail credits balance","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"inMailCredits":{"type":"number","description":"Available InMail credits"},"totalCredits":{"type":"number","description":"Total credits (Sales Navigator only)"},"usedCredits":{"type":"number","description":"Used credits (Sales Navigator only)"},"entityUrn":{"type":"string","description":"Entity URN for the credits"},"accountId":{"type":"string","description":"LinkedIn account ID used"},"premiumType":{"type":"string","description":"Premium account type","enum":["SALES_NAVIGATOR","BUSINESS_PREMIUM","RECRUITER","FREE","UNKNOWN"]},"message":{"type":"string","description":"Additional info (e.g. for non-premium accounts)"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/events/{eventId}/attendees":{"get":{"summary":"Get event attendees","description":"Fetch the list of people who have RSVPed or are attending a LinkedIn event. Returns profile information for each attendee including name, headline, location, profile picture, and connection degree. Supports pagination (LinkedIn fixes page size at 10). Accepts a numeric event ID, a full LinkedIn event URL, or a search URL containing an `eventAttending` parameter.","operationId":"get-event-attendees","tags":["Events"],"security":[{"bearerAuth":[]}],"parameters":[{"name":"eventId","in":"path","required":true,"schema":{"type":"string"},"description":"LinkedIn event ID (numeric), full event URL, or search URL with eventAttending parameter."},{"name":"accountId","in":"query","required":false,"schema":{"type":"string"},"description":"LinkedIn account ID. If omitted, uses the default account."},{"name":"start","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0},"description":"Pagination offset (0-indexed). Increment by 10 to paginate."},{"name":"count","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":10},"description":"Number of attendees to return per page (max 100, LinkedIn typically returns 10)."}],"responses":{"200":{"description":"Event attendees retrieved successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"accountId":{"type":"string","description":"LinkedIn account ID used"},"eventId":{"type":"string","description":"Resolved numeric event ID"},"attendees":{"type":"array","items":{"type":"object","properties":{"profileUrn":{"type":"string","description":"LinkedIn tracking URN for the profile"},"fsdProfileUrn":{"type":"string","description":"FSD profile URN (e.g. urn:li:fsd_profile:ABCDE)"},"publicIdentifier":{"type":"string","description":"Profile vanity slug (e.g. john-doe)"},"fullName":{"type":"string","description":"Attendee full name"},"headline":{"type":"string","description":"Professional headline"},"location":{"type":"string","description":"Location from profile"},"profilePictureUrl":{"type":"string","description":"URL of the profile picture"},"profileUrl":{"type":"string","description":"Full LinkedIn profile URL"},"connectionDegree":{"type":"string","description":"Connection degree (DISTANCE_1, DISTANCE_2, DISTANCE_3)"}}}},"paging":{"type":"object","properties":{"start":{"type":"integer","description":"Current offset"},"count":{"type":"integer","description":"Requested page size"},"total":{"type":"integer","description":"Total number of attendees (may be absent)"}}}}},"example":{"success":true,"accountId":"acc_12345","eventId":"7453424948037070848","attendees":[{"profileUrn":"urn:li:member:123456789","fsdProfileUrn":"urn:li:fsd_profile:ABCDE","publicIdentifier":"jane-doe","fullName":"Jane Doe","headline":"VP of Marketing at Acme Corp","location":"San Francisco Bay Area","profilePictureUrl":"https://media.licdn.com/dms/image/example.jpg","profileUrl":"https://www.linkedin.com/in/jane-doe","connectionDegree":"DISTANCE_2"}],"paging":{"start":0,"count":10,"total":142}}}}},"400":{"description":"Bad request - invalid event ID or URL","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"success":{"type":"boolean"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"Temporarily unavailable - LinkedIn changed this endpoint and it needs to be refreshed on our side. Retry later or contact support.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"code":{"type":"string","enum":["QUERY_ID_STALE"]},"success":{"type":"boolean"}}}}}}}}},"/conversations":{"get":{"operationId":"list-conversations","tags":["Conversations"],"summary":"List conversations","description":"List recent LinkedIn conversations using DB-first approach with LinkedIn API fallback. Supports multi-account batch queries, label/unread filtering. Auto-detects Sales Navigator accounts and merges Sales Nav threads with standard conversations.","parameters":[{"name":"accountId","in":"query","schema":{"type":"string"},"description":"Single LinkedIn account ID"},{"name":"linkedinAccountIds","in":"query","schema":{"type":"string"},"description":"Comma-separated account IDs for multi-account batch"},{"name":"count","in":"query","schema":{"type":"number","default":20,"minimum":1,"maximum":100},"description":"Number of conversations. Served from our cache, so up to 100 is honoured. Pages that fall through to LinkedIn are capped at 25 by LinkedIn itself (a higher count is silently clamped there), so a deep page may return fewer rows than a shallow one for the same count."},{"name":"nextCursor","in":"query","schema":{"type":"string"},"description":"Pagination cursor — pass back the `nextCursor` from the previous response verbatim; never construct one. A plain timestamp pages our cache, a `linkedin:`-prefixed one continues the walk on LinkedIn. **Drive the walk by `nextCursor` (or `hasMore`, which now always agrees with it) and stop when it is null.** On a FILTERED listing (`unreadOnly`, `labeledOnly`, `labelId`, `archived`, `snoozed`, `source=sales_navigator`) the walk ends at the edge of our cache and returns `nextCursor: null` — LinkedIn cannot continue a filtered walk, so ending is deliberate rather than returning rows that do not match your filter."},{"name":"labelId","in":"query","schema":{"type":"string"},"description":"Filter by label ID"},{"name":"labeledOnly","in":"query","schema":{"type":"boolean"},"description":"Only labeled conversations"}],"responses":{"200":{"description":"Conversations list","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"conversations":{"type":"array","items":{"type":"object","properties":{"conversationUrn":{"type":"string"},"conversationId":{"type":"string"},"lastActivityAt":{"type":"string","format":"date-time"},"unreadCount":{"type":"number"},"participants":{"type":"array","items":{"type":"object","properties":{"profileId":{"type":"string"},"name":{"type":"string"},"headline":{"type":"string"},"profileUrl":{"type":"string"},"profilePicture":{"type":"string"},"distance":{"type":"string"}}}},"latestMessage":{"type":"object","properties":{"text":{"type":"string"},"sentAt":{"type":"number"},"senderName":{"type":"string"},"hasAttachment":{"type":"boolean"}}},"source":{"type":"string","enum":["linkedin","sales_navigator"],"description":"Source of the conversation (sales_navigator for Sales Nav threads)"},"salesNavThreadId":{"type":"string","nullable":true,"description":"Sales Navigator thread ID (only for Sales Nav conversations)"}}}},"accounts":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}}}},"nextCursor":{"type":"string","nullable":true},"hasMore":{"type":"boolean"},"fromCache":{"type":"boolean","description":"true when this response was served from our cache without contacting LinkedIn during the request. false when the request refreshed from LinkedIn first. The rows always come from our store either way — this reports how fresh they are, not where they were read from."}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Account not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/conversations/exists/{profileId}":{"get":{"operationId":"conversation-exists","tags":["Conversations"],"summary":"Check if a conversation exists with a profile","description":"Check whether the authenticated LinkedIn account already has an existing conversation with a target profile. Returns the conversation URN if one exists. Single LinkedIn API call (no message history fetched). Useful as a precheck before sending a connection request or message to avoid duplicate outreach.","parameters":[{"name":"profileId","in":"path","required":true,"schema":{"type":"string"},"description":"LinkedIn profile vanity URL slug (the part after linkedin.com/in/, e.g., \"john-doe-123\")"},{"name":"accountId","in":"query","required":false,"schema":{"type":"string"},"description":"LinkedIn account ID to check from. Omit to use the default account for the authenticated user."}],"responses":{"200":{"description":"Conversation existence check completed","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"hasConversation":{"type":"boolean","description":"Whether a conversation already exists with this profile"},"conversationUrn":{"type":"string","nullable":true,"description":"Existing conversation URN if one exists, otherwise null"},"profileUrn":{"type":"string","description":"Resolved LinkedIn profile URN of the target user"},"accountId":{"type":"string","description":"LinkedIn account ID used for the check"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Account or profile not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/conversations/{conversationUrn}/messages":{"get":{"operationId":"get-conversation-messages","tags":["Conversations"],"summary":"Get conversation messages","description":"Retrieve messages for a specific conversation. Auto-detects Sales Navigator thread URNs (urn:li:salesNav_thread:*) and routes to the Sales Nav API. Standard conversations use DB-first with LinkedIn API fallback for older messages via cursor pagination. Replaces GET /messaging/conversation-details.","parameters":[{"name":"conversationUrn","in":"path","required":true,"schema":{"type":"string"},"description":"Conversation URN (URL-encoded)"},{"name":"accountId","in":"query","schema":{"type":"string"},"description":"LinkedIn account ID. Honoured as a FILTER: if the conversation does not belong to this account you get an empty `messages` array, not another seat's thread. Omit it and the conversation's own account is used. Required for a thread we have not synced yet (and for Sales Navigator threads), since there is no stored account to infer."},{"name":"count","in":"query","schema":{"type":"number","default":50,"minimum":1,"maximum":100},"description":"Number of messages"},{"name":"cursor","in":"query","schema":{"type":"string"},"description":"Timestamp cursor for older messages"},{"name":"since","in":"query","schema":{"type":"number"},"description":"Timestamp to fetch messages newer than (polling). Returns oldest-first, unlike every other call here. **Ignored when `cursor` is also supplied** — paginating backwards and polling forwards are mutually exclusive, and `cursor` wins."},{"name":"sync","in":"query","schema":{"type":"boolean","default":true},"description":"Wait for LinkedIn response (sync mode)"}],"responses":{"200":{"description":"Messages list","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"messages":{"type":"array","items":{"type":"object","properties":{"messageUrn":{"type":"string"},"text":{"type":"string"},"sentAt":{"type":"number"},"senderProfileId":{"type":"string"},"senderName":{"type":"string"},"senderPhoto":{"type":"string"},"isSentByOwner":{"type":"boolean"},"hasAttachment":{"type":"boolean"},"attachments":{"type":"array","nullable":true,"items":{"type":"object"}}}}},"participants":{"type":"array","items":{"type":"object","properties":{"profileId":{"type":"string"},"name":{"type":"string"},"headline":{"type":"string"},"profilePicture":{"type":"string"}}}},"hasMore":{"type":"boolean"},"cursor":{"type":"string","nullable":true},"note":{"type":"string","nullable":true}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Conversation not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/conversations/{conversationUrn}/messages/{messageUrn}":{"delete":{"operationId":"conversations-delete-message","tags":["Conversations"],"summary":"Delete (recall) a message","description":"Recall a message you sent. The message is first recalled on LinkedIn (source of truth); only on success is it soft-deleted locally so a later sync never re-surfaces the recalled tombstone. Other connected clients/tabs are notified via WebSocket. Matching is by the prefix-independent normalized message id, so the stored row is removed even if it was synced under a different mailbox prefix.","parameters":[{"name":"conversationUrn","in":"path","required":true,"schema":{"type":"string"},"description":"Conversation URN (URL-encoded)"},{"name":"messageUrn","in":"path","required":true,"schema":{"type":"string"},"description":"URN of the message to delete (URL-encoded)"},{"name":"accountId","in":"query","schema":{"type":"string"},"description":"LinkedIn account ID"}],"responses":{"200":{"description":"Message deleted","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"messageUrn":{"type":"string"}}}}}},"400":{"description":"Recall failed on LinkedIn","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Account not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/conversations/{conversationUrn}/messages/{messageUrn}/react":{"post":{"operationId":"conversations-react-message","tags":["Conversations"],"summary":"React or unreact to a message","description":"Add or remove an emoji reaction on a message. The reaction is applied on LinkedIn first; other connected clients/tabs are notified via WebSocket. Set react=true to add the reaction, react=false to remove it.","parameters":[{"name":"conversationUrn","in":"path","required":true,"schema":{"type":"string"},"description":"Conversation URN (URL-encoded)"},{"name":"messageUrn","in":"path","required":true,"schema":{"type":"string"},"description":"URN of the message to react to (URL-encoded)"},{"name":"accountId","in":"query","schema":{"type":"string"},"description":"LinkedIn account ID"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["emoji","react"],"properties":{"emoji":{"type":"string","description":"Emoji to react with (e.g., \"👍\")"},"react":{"type":"boolean","description":"true to add the reaction, false to remove it"}}}}}},"responses":{"200":{"description":"Reaction applied","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"messageUrn":{"type":"string"},"emoji":{"type":"string"},"react":{"type":"boolean"}}}}}},"400":{"description":"Reaction failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Account not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/conversations/{conversationUrn}/mark-unread":{"patch":{"operationId":"conversations-mark-unread","tags":["Conversations"],"summary":"Mark a conversation as unread","description":"Mark a conversation as unread on LinkedIn (source of truth) and locally. The account is resolved from the conversation, so no accountId is required. Other connected clients/tabs are notified via WebSocket.","parameters":[{"name":"conversationUrn","in":"path","required":true,"schema":{"type":"string"},"description":"Conversation URN (URL-encoded)"}],"responses":{"200":{"description":"Marked as unread","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"conversationUrn":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Conversation or account not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/conversations/{conversationUrn}/star":{"patch":{"operationId":"conversations-star","tags":["Conversations"],"summary":"Star a conversation","description":"Add LinkedIn's STARRED category to a conversation (synced to LinkedIn first as the source of truth) and persist it locally. The account is resolved from the conversation. Other connected clients/tabs are notified via WebSocket.","parameters":[{"name":"conversationUrn","in":"path","required":true,"schema":{"type":"string"},"description":"Conversation URN (URL-encoded)"}],"responses":{"200":{"description":"Conversation starred","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"conversationUrn":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Conversation or account not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/conversations/{conversationUrn}/unstar":{"patch":{"operationId":"conversations-unstar","tags":["Conversations"],"summary":"Unstar a conversation","description":"Remove LinkedIn's STARRED category from a conversation (synced to LinkedIn first as the source of truth) and clear it locally. The account is resolved from the conversation. Other connected clients/tabs are notified via WebSocket.","parameters":[{"name":"conversationUrn","in":"path","required":true,"schema":{"type":"string"},"description":"Conversation URN (URL-encoded)"}],"responses":{"200":{"description":"Conversation unstarred","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"conversationUrn":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Conversation or account not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/conversations/send":{"post":{"operationId":"conversations-send-message","tags":["Conversations"],"summary":"Send message (with channel selection)","description":"Send a LinkedIn message. By default, messagingChannel is \"auto\" which auto-detects whether to use Sales Navigator or standard LinkedIn inbox based on the account premium status. Users can explicitly control the messaging channel by setting messagingChannel to: \"sales_navigator\" — forces the message through the Sales Navigator API (returns a 400 error if the account does not have an active Sales Navigator subscription), or \"linkedin_inbox\" — forces standard LinkedIn messaging even if the account has Sales Navigator. Example: to send via standard inbox on a Sales Nav account, pass { \"messagingChannel\": \"linkedin_inbox\" }. To explicitly use Sales Navigator, pass { \"messagingChannel\": \"sales_navigator\" }. If omitted or set to \"auto\", the system decides automatically. **File attachments:** first upload each file via POST /conversations/upload-attachment, then pass each returned attachment object in the `attachments` array, each wrapped in a `file` key — `{ \"attachments\": [{ \"file\": { \"assetUrn\": \"...\", \"byteSize\": 123, \"mediaType\": \"image/png\", \"name\": \"photo.png\" } }] }`. An attachment whose file was uploaded with an image/* content type renders inline as a photo; anything else renders as a downloadable file. **Rate limit: 150 NEW conversations per day per LinkedIn account** (resets at midnight UTC). The quota applies to cold outreach only — a message that starts a conversation with someone this account has no existing thread with. Replying inside an existing conversation is unlimited, whether you pass `conversationUrn` or a recipient this account has already messaged. Over-quota cold sends return 429 with `X-RateLimit-Limit`, `X-RateLimit-Remaining` and `X-RateLimit-Reset` headers, and are rejected before any LinkedIn request is made. **Do not read the rate-limit headers on a reply as the daily quota.** An exempt reply never touches the daily counter, so the headers it carries come from the general per-minute API throttle instead (write endpoints: 15 requests/minute per user, reset under a minute). A `X-RateLimit-Limit` of 15 with a reset about a minute out therefore means the request was NOT counted against the 150/day.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["message"],"properties":{"accountId":{"type":"string","description":"LinkedIn account ID to send from. Accepted in this body OR as a query parameter. **If omitted entirely, the most recently used account on the subscription is selected** — on a multi-seat subscription that may not be the one you intend, and a send from an account that is not connected to the recipient fails with NOT_ENOUGH_INMAIL_CREDIT. Always pass it, and check the `accountId` echoed in the response."},"recipientProfileId":{"type":"string","description":"Recipient LinkedIn profile vanity URL slug (e.g., \"john-doe-123\"). Prefer recipientProfileUrn when available."},"recipientProfileUrn":{"type":"string","description":"Recipient LinkedIn profile URN (e.g., \"urn:li:fsd_profile:ACoAABJefVoBrz2LR3f...\"). Preferred over recipientProfileId."},"conversationUrn":{"type":"string","description":"Existing conversation URN (skips recipient lookup)"},"message":{"type":"string","description":"Message text"},"subject":{"type":"string","description":"Message subject (optional)"},"attachments":{"type":"array","items":{"type":"object","properties":{"file":{"type":"object","properties":{"assetUrn":{"type":"string"},"byteSize":{"type":"number"},"mediaType":{"type":"string"},"name":{"type":"string"}}}}},"description":"File attachments from the upload-attachment endpoint. Each item must wrap the upload response in a `file` key: [{ \"file\": { assetUrn, byteSize, mediaType, name } }]. Files uploaded with an image/* content type render inline as photos; others render as downloadable files."},"messagingChannel":{"type":"string","enum":["auto","sales_navigator","linkedin_inbox"],"default":"auto","description":"Controls which messaging channel to use. Defaults to \"auto\" if not provided. Options: \"auto\" — automatically detects the best channel based on account premium status (Sales Navigator accounts use Sales Nav API, others use standard inbox). \"sales_navigator\" — explicitly send via Sales Navigator API. Use this when the user wants to send through Sales Navigator. Returns a 400 error with a clear message if the account does not have Sales Navigator. \"linkedin_inbox\" — explicitly send via standard LinkedIn inbox. Use this when the user wants to bypass Sales Navigator and send through the regular LinkedIn messaging, even if the account has a Sales Navigator subscription."}}}}}},"responses":{"200":{"description":"Message sent","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"recipientProfileUrn":{"type":"string"},"sentMessage":{"type":"object","properties":{"messageUrn":{"type":"string"},"text":{"type":"string"},"sentAt":{"type":"number"},"senderProfileId":{"type":"string"},"senderName":{"type":"string"},"senderPhoto":{"type":"string"},"isSentByOwner":{"type":"boolean"}}},"threadId":{"type":"string","description":"Sales Navigator thread ID (only for Sales Nav accounts)"}}}}}},"400":{"description":"Bad request or send failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Account not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/conversations/upload-attachment":{"post":{"operationId":"conversations-upload-attachment","tags":["Conversations"],"summary":"Upload message attachment","description":"Upload a file to attach to a LinkedIn message (used with POST /conversations/send). **Send the file as the RAW binary request body — do NOT use multipart/form-data.** Set the request `Content-Type` header to the file's actual MIME type, and pass `accountId` and `filename` in the query string. **To make an image render inline as a photo in the conversation, the `Content-Type` MUST be an image type** (`image/png`, `image/jpeg`, `image/gif`, `image/webp`) — this uploads it as a LinkedIn photo asset. Any other content type is stored as a downloadable file attachment. The response's `attachment` object must then be passed to POST /conversations/send wrapped in a `file` key: `{ \"attachments\": [{ \"file\": <attachment> }] }`. Example: `curl -X POST \"https://api.connectsafely.ai/linkedin/conversations/upload-attachment?accountId=acc_123&filename=photo.png\" -H \"Authorization: Bearer <api_key>\" -H \"Content-Type: image/png\" --data-binary @photo.png`","parameters":[{"name":"accountId","in":"query","required":false,"schema":{"type":"string"},"description":"LinkedIn account ID. If omitted, resolved from the authenticated user's default account."},{"name":"filename","in":"query","required":false,"schema":{"type":"string"},"example":"photo.png","description":"Original file name including extension (e.g. \"photo.png\"). Defaults to \"attachment\" if omitted."}],"requestBody":{"required":true,"description":"Raw file bytes. The request `Content-Type` header must be the file's MIME type (e.g. image/png, image/jpeg, application/pdf). NOT multipart/form-data.","content":{"application/octet-stream":{"schema":{"type":"string","format":"binary"}}}},"responses":{"200":{"description":"File uploaded","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"attachment":{"type":"object","description":"Pass this back to POST /conversations/send as { attachments: [{ file: <this> }] }.","properties":{"assetUrn":{"type":"string"},"byteSize":{"type":"number"},"mediaType":{"type":"string"},"name":{"type":"string"}}}}}}}},"400":{"description":"Upload failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Account not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/conversations/download-media":{"post":{"operationId":"conversations-download-media","tags":["Conversations"],"summary":"Download media from message","description":"Download media (images, files, videos, audio) from a LinkedIn messaging URL. Uses account session cookies for authentication. No proxy is used — media is fetched directly from LinkedIn CDN. Supports URLs from www.linkedin.com/dms/prv/ (private messaging media) and media.licdn.com (CDN-hosted media).","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["accountId","url"],"properties":{"accountId":{"type":"string","description":"LinkedIn account ID"},"url":{"type":"string","format":"uri","description":"Media URL from message attachment (www.linkedin.com or media.licdn.com)"}}}}}},"responses":{"200":{"description":"Media file binary","content":{"application/octet-stream":{"schema":{"type":"string","format":"binary"}},"image/*":{"schema":{"type":"string","format":"binary"}},"application/pdf":{"schema":{"type":"string","format":"binary"}},"video/*":{"schema":{"type":"string","format":"binary"}},"audio/*":{"schema":{"type":"string","format":"binary"}}},"headers":{"Content-Type":{"schema":{"type":"string"},"description":"MIME type of the media (e.g., image/png, application/pdf)"},"Content-Length":{"schema":{"type":"number"},"description":"Size in bytes"},"Content-Disposition":{"schema":{"type":"string"},"description":"Filename if available (e.g., attachment; filename=\"doc.pdf\")"}}},"400":{"description":"Invalid media URL host","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Account not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/conversations/sync":{"post":{"operationId":"conversations-sync","tags":["Conversations"],"summary":"Trigger conversation sync","description":"Trigger a full sync of conversations from LinkedIn API to local database.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"accountId":{"type":"string","description":"LinkedIn account ID"},"force":{"type":"boolean","default":false,"description":"Force sync even if recently synced"}}}}}},"responses":{"200":{"description":"Sync completed","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"accountId":{"type":"string"},"conversationsProcessed":{"type":"number"},"messagesProcessed":{"type":"number"},"durationMs":{"type":"number"},"durationSeconds":{"type":"string"},"error":{"type":"string","nullable":true}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Account not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/conversations/sync/status":{"get":{"operationId":"conversations-sync-status","tags":["Conversations"],"summary":"Get sync status","description":"Get sync status for one or all LinkedIn accounts.","parameters":[{"name":"accountId","in":"query","schema":{"type":"string"},"description":"Account ID (optional, returns all if omitted)"}],"responses":{"200":{"description":"Sync status","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"accounts":{"type":"array","items":{"type":"object","properties":{"accountId":{"type":"string"},"accountName":{"type":"string"},"accountPicture":{"type":"string"},"syncState":{"type":"object","nullable":true,"properties":{"lastSyncAt":{"type":"string","format":"date-time","nullable":true},"syncInProgress":{"type":"boolean"},"totalConversations":{"type":"number"},"totalMessages":{"type":"number"},"consecutiveFailures":{"type":"number"},"lastError":{"type":"string","nullable":true},"initialSyncCompleted":{"type":"boolean"}}}}}}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/conversations/refresh":{"post":{"operationId":"conversations-refresh","tags":["Conversations"],"summary":"Refresh conversation messages","description":"Trigger background refresh of messages for a conversation. Results pushed via WebSocket.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["conversationUrn"],"properties":{"accountId":{"type":"string","description":"LinkedIn account ID"},"conversationUrn":{"type":"string","description":"Conversation URN to refresh"}}}}}},"responses":{"200":{"description":"Refresh triggered","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"conversationUrn":{"type":"string"},"accountId":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Account not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/sales-nav/threads":{"get":{"operationId":"sales-nav-list-threads","tags":["Sales Navigator"],"summary":"List Sales Navigator threads","description":"List messaging threads from Sales Navigator. Requires active Sales Navigator subscription. Supports INBOX/UNREAD/ARCHIVED filtering. Note: GET /conversations automatically merges Sales Nav threads — use this endpoint only if you need Sales Nav-specific filtering (UNREAD/ARCHIVED).","parameters":[{"name":"accountId","in":"query","schema":{"type":"string"},"description":"LinkedIn account ID"},{"name":"count","in":"query","schema":{"type":"number","default":20,"minimum":1,"maximum":50},"description":"Number of threads"},{"name":"filter","in":"query","schema":{"type":"string","enum":["INBOX","UNREAD","ARCHIVED"],"default":"INBOX"},"description":"Thread filter"},{"name":"pageStartsAt","in":"query","schema":{"type":"number"},"description":"Pagination offset"}],"responses":{"200":{"description":"Threads list","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"threads":{"type":"array","items":{"type":"object"}},"paging":{"type":"object"},"nextPageStartsAt":{"type":"number","nullable":true},"accountId":{"type":"string"}}}}}},"400":{"description":"Not a Sales Navigator account","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"success":{"type":"boolean"},"premiumType":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Account not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/sales-nav/threads/{threadId}":{"get":{"operationId":"sales-nav-get-thread","tags":["Sales Navigator"],"summary":"Get Sales Navigator thread with messages","description":"Get a single Sales Navigator thread with message history. Note: GET /conversations/{conversationUrn}/messages auto-detects Sales Nav URNs and serves the same data.","parameters":[{"name":"threadId","in":"path","required":true,"schema":{"type":"string"},"description":"Sales Navigator thread ID"},{"name":"accountId","in":"query","schema":{"type":"string"},"description":"LinkedIn account ID"},{"name":"messageCount","in":"query","schema":{"type":"number","default":20,"minimum":1,"maximum":100},"description":"Number of messages"}],"responses":{"200":{"description":"Thread with messages","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"thread":{"type":"object"},"accountId":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Thread not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/sales-nav/send":{"post":{"operationId":"sales-nav-send-message","tags":["Sales Navigator"],"summary":"Send Sales Navigator message","description":"Send a message via Sales Navigator API. Can reply to existing thread or start new conversation with recipients. Optionally copy to CRM. Note: POST /conversations/send auto-detects Sales Nav accounts and routes accordingly.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["body"],"properties":{"accountId":{"type":"string","description":"LinkedIn account ID"},"threadId":{"type":"string","description":"Existing thread ID to reply to"},"recipients":{"type":"array","items":{"type":"string"},"description":"Profile URNs for new conversation (required if no threadId)"},"body":{"type":"string","description":"Message text"},"copyToCrm":{"type":"boolean","default":false,"description":"Copy message to CRM"}}}}}},"responses":{"200":{"description":"Message sent","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"threadId":{"type":"string"},"messageId":{"type":"string"},"accountId":{"type":"string"}}}}}},"400":{"description":"Send failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Account not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/sales-nav/mark-read":{"post":{"operationId":"sales-nav-mark-read","tags":["Sales Navigator"],"summary":"Mark Sales Navigator thread as read","description":"Mark a Sales Navigator messaging thread as read.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["threadId"],"properties":{"accountId":{"type":"string","description":"LinkedIn account ID"},"threadId":{"type":"string","description":"Thread ID to mark as read"}}}}}},"responses":{"200":{"description":"Marked as read","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"threadId":{"type":"string"},"accountId":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Account not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}}}