LinkedIn automation library - post, fetch, and search via Voyager API
Project description
linkitin
Python library for LinkedIn automation via the Voyager API. Fetch posts, search content, and publish — all through an async client.
macOS only —
login_from_browser()requires macOS and Chrome. Thelogin_with_cookies()path works cross-platform.
Install
pip install linkitin
Requires Python 3.10+.
Development
python -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
Authentication
Chrome proxy mode (recommended — requires macOS and Chrome logged into LinkedIn):
async with LinkitinClient() as client:
await client.login_from_browser() # routes API requests through Chrome via AppleScript
Chrome 145+ uses App-Bound Encryption for cookies, making them inaccessible to external tools. login_from_browser() works around this by executing API requests inside Chrome's LinkedIn tab, which automatically includes all session cookies. Requires "Allow JavaScript from Apple Events" enabled in Chrome (View > Developer).
Manual cookie input (if you have raw li_at and JSESSIONID values):
async with LinkitinClient() as client:
await client.login_with_cookies(li_at="AQED...", jsessionid="ajax:123...")
Cookies from manual input are saved to disk and reused on subsequent runs via login_from_saved().
Usage
import asyncio
from linkitin import LinkitinClient
async def main():
async with LinkitinClient() as client:
# Authenticate
if not await client.login_from_saved():
await client.login_from_browser()
# Fetch your posts
posts = await client.get_my_posts(limit=10)
for p in posts:
print(p.text[:80], f"({p.likes} likes)")
# Search posts
results = await client.search_posts("AI startups", limit=5)
# Get home feed
feed = await client.get_feed(limit=20)
# Get trending posts
trending = await client.get_trending_posts(topic="AI", period="past-week", limit=5)
# Create a text post
urn = await client.create_post("Hello LinkedIn!", visibility="PUBLIC")
# Create a post with image
with open("chart.png", "rb") as f:
urn = await client.create_post_with_image(
text="Check out these metrics!",
image_data=f.read(),
filename="chart.png",
)
# Schedule a post (automatically rounded to next 15-min slot)
from datetime import datetime, timezone, timedelta
urn = await client.create_scheduled_post(
text="Scheduled post — hello future!",
scheduled_at=datetime.now(timezone.utc) + timedelta(hours=2),
)
# Comment on a post
feed = await client.get_feed(limit=5)
comment_urn = await client.comment_post(feed[0].urn, "Great post!")
# Reply to a comment (threaded)
reply_urn = await client.comment_post(
feed[0].urn, "Thanks!", parent_comment_urn=comment_urn
)
# Repost a post from the feed
repost_urn = await client.repost(feed[0].share_urn)
# Repost with your thoughts
repost_urn = await client.repost(feed[0].share_urn, text="Great insights!")
asyncio.run(main())
Configuration
LinkitinClient accepts optional parameters to override defaults:
client = LinkitinClient(
cookies_path="~/.myapp/linkedin_cookies.json", # default: ~/.linkitin/cookies.json
timezone="America/New_York", # default: detected from system
timezone_offset=-5.0, # default: computed from system
display_width=1440, # default: 1920
display_height=900, # default: 1080
user_agent="Mozilla/5.0 ...", # default: modern Chrome/macOS UA
)
Rate Limiting
All requests go through a token-bucket rate limiter: 10 requests per minute with 1-5 second random jitter between requests. On 429/403 responses, exponential backoff kicks in starting at 30 seconds.
API Reference
| Method | Description |
|---|---|
login_from_browser() |
Authenticate via Chrome proxy (AppleScript, macOS only) |
login_with_cookies(li_at, jsessionid) |
Authenticate with manual cookies |
login_from_saved() |
Load previously saved cookies |
get_my_posts(limit=20) |
Fetch your posts |
search_posts(keywords, limit=20) |
Search posts by keywords |
get_feed(limit=20) |
Fetch home feed |
get_trending_posts(topic, period, limit, from_followed, scrolls) |
Fetch trending posts sorted by engagement |
create_post(text, visibility="PUBLIC") |
Create a text post |
upload_image(image_data, filename) |
Upload an image, returns media URN |
create_post_with_image(text, image_data, filename, visibility="PUBLIC") |
Create post with image |
create_scheduled_post(text, scheduled_at, visibility="PUBLIC") |
Schedule a text post; scheduled_at rounded to next 15-min slot |
create_scheduled_post_with_image(text, image_data, filename, scheduled_at, visibility="PUBLIC") |
Schedule a post with image; scheduled_at rounded to next 15-min slot |
comment_post(post_urn, text, parent_comment_urn="") |
Comment on a post (or reply to a comment) |
repost(share_urn, text="") |
Repost (reshare) an existing post |
delete_post(post_urn) |
Delete a post by URN |
close() |
Close the HTTP session |
Contributing
See CONTRIBUTING.md for guidelines. PRs welcome!
- Open issues for bugs or feature requests
- Keep PRs focused — one change per PR
- Run
python -m pytest tests/before submitting
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file linkitin-0.1.2.tar.gz.
File metadata
- Download URL: linkitin-0.1.2.tar.gz
- Upload date:
- Size: 56.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
784608db0da7a5542e551caf18237cd0de955930a3a6b928cfc6eaeabc11519d
|
|
| MD5 |
3e95a7ef156adcfbcac291229d08692f
|
|
| BLAKE2b-256 |
334093fd871caf1ed52ba6e0e47b1e5beba0978aec8ac6e358317c2ead21c7e3
|
Provenance
The following attestation bundles were made for linkitin-0.1.2.tar.gz:
Publisher:
publish.yml on ShuhaoZQGG/linkitin
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
linkitin-0.1.2.tar.gz -
Subject digest:
784608db0da7a5542e551caf18237cd0de955930a3a6b928cfc6eaeabc11519d - Sigstore transparency entry: 976553022
- Sigstore integration time:
-
Permalink:
ShuhaoZQGG/linkitin@416987d185e04576d40cf6d2d9d14800d44b189b -
Branch / Tag:
refs/heads/main - Owner: https://github.com/ShuhaoZQGG
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@416987d185e04576d40cf6d2d9d14800d44b189b -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file linkitin-0.1.2-py3-none-any.whl.
File metadata
- Download URL: linkitin-0.1.2-py3-none-any.whl
- Upload date:
- Size: 34.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
541d38e8830cb1fe1c2bc651bf106b4a82c4c703b65fcbc02ce79e1a4294fffc
|
|
| MD5 |
3d79f603870153a4ff4a1f141060aab5
|
|
| BLAKE2b-256 |
04870699231351c1a085d476cf3b2be7f2337865092acf69a87e0298bc446635
|
Provenance
The following attestation bundles were made for linkitin-0.1.2-py3-none-any.whl:
Publisher:
publish.yml on ShuhaoZQGG/linkitin
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
linkitin-0.1.2-py3-none-any.whl -
Subject digest:
541d38e8830cb1fe1c2bc651bf106b4a82c4c703b65fcbc02ce79e1a4294fffc - Sigstore transparency entry: 976553024
- Sigstore integration time:
-
Permalink:
ShuhaoZQGG/linkitin@416987d185e04576d40cf6d2d9d14800d44b189b -
Branch / Tag:
refs/heads/main - Owner: https://github.com/ShuhaoZQGG
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@416987d185e04576d40cf6d2d9d14800d44b189b -
Trigger Event:
workflow_dispatch
-
Statement type: