Content
# LinkedIn MCP server
> Forked from https://github.com/EseToni/open-linkedin-api 🍴
>
> The original contributor: https://github.com/EseToni 🪪
## 🆕 Latest Updates (v2.3.4)
- **Schedule/Unschedule/List scheduled** posts
- **Sending/Withdrawal/List** connection requests
- **Fixed HTTP 410 errors** in profile fetching due to LinkedIn API changes
- **Human behavior** mimicking via smart rate limiting to avoid getting blocked
- **HTTP proxy** support
- **Docker** images available at Docker Hub
- See [CHANGELOG.md](CHANGELOG.md) for full details.
> [!IMPORTANT]
> If something isn't working please raise an issue or contact me directly.
>
> Failures are expected because LinkedIn frequently changes their GraphQL endpoints.
>
> While it's technically possible to extract query ID (which is a part of endpoint url), it would require tools like Playwright.
>
> I'm currently considering to write such a script, but it would be rather big and take a plenty of time. However I'm not giving up.
>
> I've decided to turn this library into the MCP server, so that my Claude could use it 🤖
>
> Fixed bugs and added new features like human behavior mimicking 💻
>
> Added HTTP proxy support to minimize the risk of getting blocked ⚠️
>
> Added Docker support ⛴️
## Features
- ✅ **MCP Server** - Expose LinkedIn functionality to AI assistants via Model Context Protocol
- ✅ No official API access required. Just use a valid LinkedIn user account.
- ✅ Direct HTTP API interface. No Selenium, Puppeteer, or other browser-based scraping methods.
- ✅ Get and search people, companies, jobs, posts
- ✅ Send and retrieve messages
- ✅ Send and accept connection requests
- ✅ Get and react to posts
> [!IMPORTANT]
> This server is not officially supported by LinkedIn. Using it might violate LinkedIn's Terms of Service. Use it at your own risk.
## MCP Server
This project is an [MCP (Model Context Protocol)](https://modelcontextprotocol.io/) server that allows AI assistants to interact with LinkedIn programmatically.
The server uses **stdio transport**, which enables communication via standard input/output streams, making it compatible with MCP clients like Claude Desktop, GitHub Copilot, and other AI assistants.
### Running the MCP Server
Set your LinkedIn credentials as environment variables:
```bash
export LINKEDIN_USERNAME="your-email@example.com"
export LINKEDIN_PASSWORD="your-password"
```
Then run the MCP server:
```bash
poetry run python -m open_linkedin_api.mcp_server
```
The server will listen for MCP requests on stdin and send responses on stdout, following the MCP stdio transport protocol.
### Configuring with Claude Desktop
To use this MCP server with Claude Desktop, add the following to your Claude Desktop configuration file:
- **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
- **Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
```json
{
"mcpServers": {
"linkedin": {
"command": "poetry",
"args": ["run", "python", "-m", "open_linkedin_api.mcp_server"],
"cwd": "/path/to/open-linkedin-api",
"env": {
"LINKEDIN_USERNAME": "your-email@example.com",
"LINKEDIN_PASSWORD": "your-password"
}
}
}
}
```
Replace `/path/to/open-linkedin-api` with the actual path to your installation.
#### Using Docker with Claude Desktop
Alternatively, you can use Docker to run the MCP server:
```json
{
"mcpServers": {
"linkedin": {
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"-e", "LINKEDIN_USERNAME=${input:LINKEDIN_USERNAME}",
"-e", "LINKEDIN_PASSWORD=${input:LINKEDIN_PASSWORD}",
"-e", "HTTP_PROXY_HOST=${input:HTTP_PROXY_HOST}",
"-e", "HTTP_PROXY_PORT=${input:HTTP_PROXY_PORT}",
"-e", "HTTP_PROXY_USERNAME=${input:HTTP_PROXY_USERNAME}",
"-e", "HTTP_PROXY_PASSWORD=${input:HTTP_PROXY_PASSWORD}",
"shevchenkoandriy/open-linkedin-api:2.3.1"
],
"env": {}
}
}
}
```
> [!NOTE]
> - The `${input:...}` syntax is a template that prompts for user input when the MCP client starts. Claude Desktop will automatically ask for these values.
> - HTTP proxy settings are optional and only needed if you're behind a corporate proxy or need to route LinkedIn traffic through a proxy server.
### Configuring with VS Code
To use this MCP server with VS Code and GitHub Copilot, create or update the MCP configuration file:
- **macOS/Linux**: `~/.config/Code/User/globalStorage/github.vscode-copilot-mcp-config/mcp.json`
- **Windows**: `%APPDATA%\Code\User\globalStorage\github.vscode-copilot-mcp-config\mcp.json`
```json
{
"mcpServers": {
"linkedin": {
"command": "poetry",
"args": ["run", "python", "-m", "open_linkedin_api.mcp_server"],
"cwd": "/path/to/open-linkedin-api",
"env": {
"LINKEDIN_USERNAME": "your-email@example.com",
"LINKEDIN_PASSWORD": "your-password"
}
}
}
}
```
Replace `/path/to/open-linkedin-api` with the actual path to your installation.
**Note**: After updating the configuration, restart VS Code to apply the changes.
#### Using Docker with VS Code
Alternatively, you can use Docker to run the MCP server:
```json
{
"mcpServers": {
"linkedin": {
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"-e", "LINKEDIN_USERNAME=${input:LINKEDIN_USERNAME}",
"-e", "LINKEDIN_PASSWORD=${input:LINKEDIN_PASSWORD}",
"-e", "HTTP_PROXY_HOST=${input:HTTP_PROXY_HOST}",
"-e", "HTTP_PROXY_PORT=${input:HTTP_PROXY_PORT}",
"-e", "HTTP_PROXY_USERNAME=${input:HTTP_PROXY_USERNAME}",
"-e", "HTTP_PROXY_PASSWORD=${input:HTTP_PROXY_PASSWORD}",
"shevchenkoandriy/open-linkedin-api:2.3.1"
],
"env": {}
}
}
}
```
> [!NOTE]
> - The `${input:...}` syntax is a template that prompts for user input when the MCP client starts. VS Code will automatically ask for these values.
> - HTTP proxy settings are optional and only needed if you're behind a corporate proxy or need to route LinkedIn traffic through a proxy server.
Make sure to build the Docker image first (see the [Docker](#docker) section below).
**Note**: After updating the configuration, restart VS Code to apply the changes.
### Available MCP Tools
The MCP server exposes the following tools:
- `linkedin_get_profile` - Get a LinkedIn profile by public_id or urn_id
- `linkedin_search_people` - Search for people on LinkedIn
- `linkedin_search_companies` - Search for companies on LinkedIn
- `linkedin_search_jobs` - Search for jobs on LinkedIn
- `linkedin_get_profile_contact_info` - Get contact information for a profile
- `linkedin_get_profile_connections` - Get connections for a profile
- `linkedin_get_company` - Get company details by public_id
- `linkedin_send_message` - Send a message to a LinkedIn profile
### Docker
The Docker images are automatically built and published to Docker Hub with version tags.
#### Using Pre-built Images
Pull and run the latest version:
```bash
docker pull shevchenkoandriy/open-linkedin-api:latest
docker run -i --rm \
-e LINKEDIN_USERNAME="your-email@example.com" \
-e LINKEDIN_PASSWORD="your-password" \
shevchenkoandriy/open-linkedin-api:latest
```
Or use a specific version:
```bash
docker pull shevchenkoandriy/open-linkedin-api:2.3.1
docker run -i --rm \
-e LINKEDIN_USERNAME="your-email@example.com" \
-e LINKEDIN_PASSWORD="your-password" \
shevchenkoandriy/open-linkedin-api:2.3.1
```
> [!NOTE]
> When running Docker directly (not through an MCP client), you need to replace the placeholder values with your actual credentials. For MCP client configurations (Claude Desktop, VS Code), use the `${input:...}` template syntax shown in the sections above, which will prompt for credentials automatically.
#### Building Locally
Build the Docker image:
```bash
docker build -t open-linkedin-api .
```
Run the container:
```bash
docker run -e LINKEDIN_USERNAME="your-email@example.com" -e LINKEDIN_PASSWORD="your-password" open-linkedin-api
```
## Python Library
While primarily designed as an MCP server, this package can also be used as a Python library for direct API access.
> [!NOTE]
> Installation is the same as for the MCP server (see [Installation](#installation) above).
### Usage Example
```python
from open_linkedin_api import Linkedin
# Authenticate using any Linkedin user account credentials
api = Linkedin('reedhoffman@linkedin.com', '*******')
# GET a profile
profile = api.get_profile('billy-g')
# GET a profiles contact info
contact_info = api.get_profile_contact_info('billy-g')
# GET 1st degree connections of a given profile
connections = api.get_profile_connections('1234asc12304')
```
## Development
### Dependencies
- [`poetry`](https://python-poetry.org/docs/)
- A valid Linkedin user account (don't use your personal account, if possible)
### Development installation
1. Create a `.env` config file (use `.env.example` as a reference)
2. Install dependencies using `poetry`:
```bash
poetry install
poetry self add poetry-plugin-dotenv
```
### Run tests
Run all tests:
```bash
poetry run pytest
```
Run unit tests:
```bash
poetry run pytest tests/unit
```
Run E2E tests:
```bash
poetry run pytest tests/e2e
```
### Lint
```bash
poetry run black --check .
```
Or to fix:
```bash
poetry run black .
```
### Troubleshooting
#### I keep getting a `CHALLENGE`
Linkedin will throw you a curve ball in the form of a Challenge URL. We currently don't handle this, and so you're kinda screwed. We think it could be only IP-based (i.e. logging in from different location). Your best chance at resolution is to log out and log back in on your browser.
**Known reasons for Challenge** include:
- 2FA
- Rate-limit - "It looks like you’re visiting a very high number of pages on LinkedIn.". Note - n=1 experiment where this page was hit after ~900 contiguous requests in a single session (within the hour) (these included random delays between each request), as well as a bunch of testing, so who knows the actual limit.
Please add more as you come across them.
#### Search problems
- Mileage may vary when searching general keywords like "software" using the standard `search` method. They've recently added some smarts around search whereby they group results by people, company, jobs etc. if the query is general enough. Try to use an entity-specific search method (i.e. search_people) where possible.
## How it works
This project attempts to provide a simple Python interface for the LinkedIn API.
> Do you mean the [legit LinkedIn API](https://developer.linkedin.com/)?
NO! To retrieve structured data, the [LinkedIn Website](https://linkedin.com) uses a service they call **Voyager**. Voyager endpoints give us access to pretty much everything we could want from LinkedIn: profiles, companies, connections, messages, etc. - anything that you can see on linkedin.com, we can get from Voyager.
This project aims to provide complete coverage for Voyager.
[How does it work?](#deep-dive)
### Deep dive
Voyager endpoints look like this:
```text
https://www.linkedin.com/voyager/api/identity/profileView/tom-quirk
```
Or, more clearly
```text
___________________________________ _______________________________
| base path | resource |
https://www.linkedin.com/voyager/api /identity/profileView/tom-quirk
```
They are authenticated with a simple cookie, which we send with every request, along with a bunch of headers.
To get a cookie, we POST a given username and password (of a valid LinkedIn user account) to `https://www.linkedin.com/uas/authenticate`.
### Find new endpoints
We're looking at the LinkedIn website and we spot some data we want. What now?
The following describes the most reliable method to find relevant endpoints:
1. `view source`
1. `command-f`/search the page for some keyword in the data. This will exist inside of a `<code>` tag.
1. Scroll down to the **next adjacent element** which will be another `<code>` tag, probably with an `id` that looks something like
```html
<code style="display: none" id="datalet-bpr-guid-3900675">
{"request":"/voyager/api/identity/profiles/tom-quirk/profileView","status":200,"body":"bpr-guid-3900675"}
</code>
```
The value of `request` is the url! 🤘
You can also use the `network` tab in you browsers developer tools, but you will encounter mixed results.
### How Clients query Voyager
linkedin.com uses the [Rest-li Protocol](https://linkedin.github.io/rest.li/spec/protocol) for querying data. Rest-li is an internal query language/syntax where clients (like linkedin.com) specify what data they want. It's conceptually similar to the GraphQL.
Here's an example of making a request for an organisation's `name` and `groups` (the Linkedin groups it manages):
```text
/voyager/api/organization/companies?decoration=(name,groups*~(entityUrn,largeLogo,groupName,memberCount,websiteUrl,url))&q=universalName&universalName=linkedin
```
The "querying" happens in the `decoration` parameter, which looks like the following:
```text
(
name,
groups*~(entityUrn,largeLogo,groupName,memberCount,websiteUrl,url)
)
```
Here, we request an organisation name and a list of groups, where for each group we want `largeLogo`, `groupName`, and so on.
Different endpoints use different parameters (and perhaps even different syntaxes) to specify these queries. Notice that the above query had a parameter `q` whose value was `universalName`; the query was then specified with the `decoration` parameter.
In contrast, the `/search/cluster` endpoint uses `q=guided`, and specifies its query with the `guided` parameter, whose value is something like
```text
List(v->PEOPLE)
```
It could be possible to document (and implement a nice interface for) this query language - as we add more endpoints to this project, I'm sure it will become more clear if such a thing would be possible (and if it's worth it).
### Release a new version
1. Bump `version` in `pyproject.toml`
1. `poetry build`
1. `poetry publish`
1. Draft release notes in GitHub.
## Disclaimer
This library is not endorsed or supported by LinkedIn. It is an unofficial library intended for educational purposes and personal use only. By using this library, you agree to not hold the author or contributors responsible for any consequences resulting from its usage.
MCP Config
Below is the configuration for this MCP Server. You can copy it directly to Cursor or other MCP clients.
mcp.json
Connection Info
You Might Also Like
cc-switch
All-in-One Assistant for Claude Code, Codex & Gemini CLI across platforms.
awesome-mcp-servers
A collection of MCP servers.
git
A Model Context Protocol server for Git automation and interaction.
oh-my-opencode
Background agents · Curated agents like oracle, librarians, frontend...
TrendRadar
TrendRadar: Your hotspot assistant for real news in just 30 seconds.
Appwrite
Build like a team of hundreds