Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

ai-native-engineer/linkedin-cli

linkedin-cli

AI ์—์ด์ „ํŠธ๊ฐ€ LinkedIn์„ ๋‹ค๋ฃจ๋Š” CLI โ€” ๋น„๊ณต์‹ ์ฝ๊ธฐ์™€ ๊ณต์‹ OAuth ๊ฒŒ์‹œ๋ฅผ ๋ช…ํ™•ํžˆ ๋ถ„๋ฆฌ

PyPI CI license python

ํ•œ๊ตญ์–ด ยท English


linkedin-cli๋Š” ๋‘ ํ‘œ๋ฉด์„ ๋ช…ํ™•ํžˆ ๋ถ„๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

  • read.*: ๋ณธ์ธ LinkedIn ์›น ์„ธ์…˜์„ ์‚ฌ์šฉํ•˜๋Š” ๋น„๊ณต์‹ ์ฝ๊ธฐ ์›Œํฌํ”Œ๋กœ์šฐ
  • post.*: LinkedIn OAuth์™€ ๊ณต์‹ LinkedIn API๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ณต์‹ ์“ฐ๊ธฐ ์›Œํฌํ”Œ๋กœ์šฐ

ํƒœ๊ทธ: linkedin, cli, sns-json-v1, unofficial-read, official-post, personal-workflow, oauth, comments, reactions, media

์ด ํ”„๋กœ์ ํŠธ๋Š” LinkedIn๊ณผ ๋ฌด๊ด€ํ•ฉ๋‹ˆ๋‹ค. ์ฝ๊ธฐ ๋ช…๋ น์€ ๋น„๊ณต์‹ ์›น ๋™์ž‘์— ์˜์กดํ•˜๋ฉฐ, LinkedIn ๋‚ด๋ถ€ ์—”๋“œํฌ์ธํŠธ๊ฐ€ ๋ฐ”๋€Œ๋ฉด ๊นจ์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ณ„์ •์— ์ ์šฉ๋˜๋Š” ์•ฝ๊ด€์€ ์‚ฌ์šฉ์ž๊ฐ€ ์ง์ ‘ ๊ฒ€ํ† ํ•ด์•ผ ํ•˜๋ฉฐ, ์ค€์ˆ˜ ์ฑ…์ž„์€ ์‚ฌ์šฉ์ž์—๊ฒŒ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ธฐ๋Šฅ

์ฝ๊ธฐ:

  • ํ™ˆ ํ”ผ๋“œ ์ฝ๊ธฐ
  • ์ €์žฅํ•œ ๊ฒŒ์‹œ๊ธ€ ์ฝ๊ธฐ
  • ํ”„๋กœํ•„ ์กฐํšŒ
  • ์‚ฌ๋žŒ/๊ฒŒ์‹œ๊ธ€ ๊ฒ€์ƒ‰
  • ํŠน์ • ํ”„๋กœํ•„์˜ ๊ฒŒ์‹œ๊ธ€ ์กฐํšŒ
  • ๋‹จ์ผ activity ์กฐํšŒ
  • ํ™œ๋™ ๋Œ“๊ธ€ ์ฝ๊ธฐ
  • ํ™œ๋™ ๋ฐ˜์‘ ์ฝ๊ธฐ
  • ์—์ด์ „ํŠธ, ์Šคํฌ๋ฆฝํŠธ, SNS CLI ecosystem ์†Œ๋น„์šฉ sns-json-v1 JSON ์ถœ๋ ฅ

์“ฐ๊ธฐ:

  • ์‹ค์ œ ๋ฐœํ–‰ ์ „ ๊ณต์‹ ๊ฒŒ์‹œ payload dry-run
  • ๊ณต์‹ LinkedIn Posts API๋กœ ํ…์ŠคํŠธ ๊ฒŒ์‹œ
  • LinkedIn Images + Posts API๋กœ ๋กœ์ปฌ ์ด๋ฏธ์ง€ 1๊ฐœ ๊ฒŒ์‹œ
  • ๋กœ์ปฌ ์ด๋ฏธ์ง€ 2~20์žฅ ๋‹ค์ค‘ ์ด๋ฏธ์ง€ ๊ฒŒ์‹œ
  • LinkedIn Videos + Posts API๋กœ ๋กœ์ปฌ MP4 ์˜์ƒ 1๊ฐœ ๊ฒŒ์‹œ
  • LinkedIn Documents + Posts API๋กœ PDF/DOC/DOCX/PPT/PPTX ๋ฌธ์„œ ๊ฒŒ์‹œ
  • Posts API๋กœ non-sponsored poll ๊ฒŒ์‹œ
  • article/link ๊ฒŒ์‹œ
  • ๊ธฐ์กด ๊ฒŒ์‹œ๊ธ€ ์žฌ๊ณต์œ 
  • ๊ณต์‹ Comments API ๊ธฐ๋ฐ˜ post reply ๋‹ต๊ธ€ ์ž‘์„ฑ
  • ๊ฒŒ์‹œ๊ธ€ commentary ์ˆ˜์ •
  • ๊ณต์‹ Comments API๋กœ ๋Œ“๊ธ€ ๋ชฉ๋ก/์กฐํšŒ/์ž‘์„ฑ/์ˆ˜์ •/์‚ญ์ œ
  • ๊ณต์‹ Reactions API๋กœ ๋ฐ˜์‘ ๋ชฉ๋ก/์กฐํšŒ/์ƒ์„ฑ/์‚ญ์ œ
  • ๊ณต์‹ Social Metadata API๋กœ ๋ฐ˜์‘/๋Œ“๊ธ€ ์š”์•ฝ ์กฐํšŒ ๋ฐ ๋Œ“๊ธ€ open/closed ์ƒํƒœ ์ˆ˜์ •
  • Social Metadata API ๊ฒฐ๊ณผ๋ฅผ insights.media ๊ณ„์•ฝ์œผ๋กœ ์กฐํšŒ
  • Organization Share Statistics API ๊ฒฐ๊ณผ๋ฅผ insights.organization ๊ณ„์•ฝ์œผ๋กœ ์กฐํšŒ
  • ๊ฐœ์ธ ๊ณ„์ • ๋‹จ์œ„ insights.user๋Š” ํ˜„์žฌ unsupported ๊ณ„์•ฝ์œผ๋กœ ๋ช…์‹œ
  • ํ† ํฐ์— ํ•„์š”ํ•œ read ๊ถŒํ•œ์ด ์žˆ์„ ๋•Œ ๋‹จ์ผ ๊ฒŒ์‹œ๊ธ€ ์กฐํšŒ ๋ฐ author๋ณ„ ๊ฒŒ์‹œ๊ธ€ ๋ชฉ๋ก ์กฐํšŒ
  • share/ugcPost URN, numeric share id, feed update URL๋กœ ๋ณธ์ธ ๊ณต์‹ ๊ฒŒ์‹œ๊ธ€ ์‚ญ์ œ
  • ์ €์žฅํ•œ ๊ฒŒ์‹œ๊ธ€ ์ €์žฅ ์ทจ์†Œ
  • react, unreact, save, unsave, comment, ๊ตฌํ˜• posting์„ ์œ„ํ•œ legacy browser fallback ์œ ์ง€

์„ค์น˜

pip install agent-linkedin
# ๋˜๋Š”
uv tool install agent-linkedin

agent-linkedin ํŒจํ‚ค์ง€๊ฐ€ linkedin-cli ๋ช…๋ น์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. PyPI์—์„œ linkedin-cli ์ด๋ฆ„์€ ์ด๋ฏธ ์ ์œ ๋˜์–ด ๋ฐฐํฌ๋ช…๋งŒ ๋‹ค๋ฆ…๋‹ˆ๋‹ค.

์†Œ์Šค์—์„œ ์„ค์น˜:

git clone https://github.com/ai-native-engineer/linkedin-cli.git
cd linkedin-cli
uv sync --extra dev

์†Œ์Šค์—์„œ ์‹คํ–‰ํ•  ๋•Œ๋Š” ๋ช…๋ น ์•ž์— uv run์„ ๋ถ™์ž…๋‹ˆ๋‹ค(์˜ˆ: uv run linkedin-cli --help). uv tool install .๋กœ ์ „์—ญ ์„ค์น˜ํ•˜๋ฉด ์•„๋ž˜ ์˜ˆ์‹œ์ฒ˜๋Ÿผ linkedin-cli๋ฅผ ๊ทธ๋Œ€๋กœ ์“ธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ธŒ๋ผ์šฐ์ € fallback์ด ํ•„์š”ํ•  ๋•Œ๋งŒ Playwright๋ฅผ ์„ค์น˜ํ•ฉ๋‹ˆ๋‹ค.

uv run playwright install chromium

๋น ๋ฅธ ์‹œ์ž‘

์•„๋ž˜ ์˜ˆ์‹œ๋Š” ์„ค์น˜๋œ linkedin-cli ๊ธฐ์ค€์ž…๋‹ˆ๋‹ค. clone์—์„œ ๊ฐœ๋ฐœ ์ค‘์ด๋ฉด ๊ฐ ๋ช…๋ น ์•ž์— uv run์„ ๋ถ™์ด๊ฑฐ๋‚˜(uv run linkedin-cli ...), uv tool install .๋กœ ์ „์—ญ ์„ค์น˜ํ•˜์„ธ์š”.

CLI ํ™•์ธ:

linkedin-cli --help

์ฝ๊ธฐ ๋ช…๋ น์€ LinkedIn ์›น ์„ธ์…˜์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ๊ฐ€์žฅ ์‰ฌ์šด ๋ฐฉ๋ฒ•์€ ๋กœ๊ทธ์ธ๋œ ๋ธŒ๋ผ์šฐ์ €์—์„œ ์ฟ ํ‚ค๋ฅผ ์ž๋™์œผ๋กœ ๊ฐ€์ ธ์˜ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

linkedin-cli auth login
linkedin-cli auth-status

auth login์€ ๋กœ๊ทธ์ธ๋œ ๋ธŒ๋ผ์šฐ์ €(ChromeยทChromiumยทBraveยทEdgeยทFirefox)์—์„œ ์ฟ ํ‚ค๋ฅผ ์ถ”์ถœํ•ด private file(~/.config/linkedin/cookies.env, ๊ถŒํ•œ 600)์— ์ €์žฅํ•˜๊ณ  ์„ธ์…˜์„ ๊ฒ€์ฆํ•ฉ๋‹ˆ๋‹ค. ์ž๋™ ์ถ”์ถœ์ด ์‹คํŒจํ•˜๋ฉด DevTools๋กœ ์ง์ ‘ ๋ณต์‚ฌํ•˜๋Š” ๋‹จ๊ณ„๋ฅผ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค โ€” ์ฝ๊ธฐ ์ธ์ฆ ์ฐธ๊ณ . read feed๋Š” ์ฟ ํ‚ค๋ฅผ Python HTTP ํด๋ผ์ด์–ธํŠธ์— ์žฌ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ , ์ €์žฅ๋œ Playwright browser state ์•ˆ์—์„œ GraphQL fetch๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.

์ž๋™ ์ถ”์ถœ์€ ์„ฑ๊ณตํ–ˆ์ง€๋งŒ LinkedIn Voyager๊ฐ€ self-redirect/authwall๋กœ ์„ธ์…˜์„ ๊ฑฐ๋ถ€ํ•˜๋ฉด, ์ƒˆ ์›น ์„ธ์…˜์„ ์ง์ ‘ ์บก์ฒ˜ํ•ฉ๋‹ˆ๋‹ค.

linkedin-cli auth login --via-browser --browser chrome
linkedin-cli auth login --via-browser --browser firefox

Firefox๋ฅผ ์„ ํƒํ•˜๋ ค๋ฉด Playwright Firefox ๋นŒ๋“œ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค: uv run playwright install firefox.

์ด ๋ช…๋ น์€ Playwright ๋ธŒ๋ผ์šฐ์ € ์ฐฝ์„ ์—ด๊ณ  ์‚ฌ์šฉ์ž๊ฐ€ ์ง์ ‘ ๋กœ๊ทธ์ธ/2FA/checkpoint๋ฅผ ํ†ต๊ณผํ•œ ๋’ค ์ „์ฒด LinkedIn ์ฟ ํ‚ค jar์™€ browser state๋ฅผ private file์— ์ €์žฅํ•ฉ๋‹ˆ๋‹ค. ์ฟ ํ‚ค ๊ฐ’์€ ์ถœ๋ ฅํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. auth-status๋Š” direct HTTP ์ง„๋‹จ์ด๋ฏ€๋กœ browser-context ๊ธฐ๋ฐ˜ read feed์™€ ๊ฒฐ๊ณผ๊ฐ€ ๋‹ค๋ฅผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ณต์‹ OAuth ๊ถŒํ•œ์„ mutation ์—†์ด ์ ๊ฒ€:

linkedin-cli auth permission-check --json
linkedin-cli auth permission-check --post-id urn:li:ugcPost:1234567890 --json

์ฝ๊ธฐ ๋ช…๋ น ์‹คํ–‰:

linkedin-cli read feed --limit 10 --json
linkedin-cli read saved --limit 10 --json
linkedin-cli read profile your-handle --json
linkedin-cli read profile-posts your-handle --limit 5 --json
linkedin-cli read activity urn:li:activity:1234567890 --json
linkedin-cli read comments urn:li:activity:1234567890 --limit 20 --json
linkedin-cli read reactions urn:li:activity:1234567890 --limit 20 --json
linkedin-cli read search "AI engineer" --limit 10 --json

์“ฐ๊ธฐ ๋ช…๋ น์€ ๊ณต์‹ OAuth ํ† ํฐ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

linkedin-cli post text --text "hello from linkedin-cli" --visibility public --dry-run --json
linkedin-cli post text --text "hello from linkedin-cli" --visibility public --dry-run --json --output tmp/linkedin-post-text-dry-run.json
linkedin-cli post text --text "hello from linkedin-cli" --visibility public --json
linkedin-cli post media --text "hello with image" --media image.png --visibility public --json
linkedin-cli post multi-image --text "hello album" --media one.png --media two.jpg --dry-run --json
linkedin-cli post video --text "hello video" --video clip.mp4 --title "Demo" --dry-run --json
linkedin-cli post document --text "hello deck" --document deck.pdf --title "Deck" --dry-run --json
linkedin-cli post poll --text "vote" --question "Pick one" --option Red --option Blue --duration three-days --dry-run --json
linkedin-cli post article --text "read this" --url https://example.com/post --dry-run --json
linkedin-cli post reshare urn:li:share:1234567890 --text "worth reading" --dry-run --json
linkedin-cli post quote urn:li:share:1234567890 --text "worth reading" --dry-run --json
linkedin-cli post reply urn:li:ugcPost:1234567890 --text "great post" --dry-run --json
linkedin-cli post repost urn:li:share:1234567890 --dry-run --json
linkedin-cli post update urn:li:share:1234567890 --text "updated text" --dry-run --json
linkedin-cli post get urn:li:share:1234567890 --json
linkedin-cli post list --limit 10 --json
linkedin-cli post delete urn:li:share:1234567890 --dry-run --json
linkedin-cli post delete urn:li:share:1234567890 --json
linkedin-cli comment list urn:li:ugcPost:1234567890 --json
linkedin-cli comment create urn:li:ugcPost:1234567890 --text "great post" --dry-run --json
linkedin-cli comment create urn:li:ugcPost:1234567890 --text "great post" --dry-run --json --output tmp/linkedin-comment-create-dry-run.json
linkedin-cli comment update urn:li:ugcPost:1234567890 987654321 --text "updated comment" --dry-run --json
linkedin-cli comment delete urn:li:ugcPost:1234567890 987654321 --dry-run --json
linkedin-cli reaction create urn:li:ugcPost:1234567890 --type like --dry-run --json
linkedin-cli reaction create urn:li:ugcPost:1234567890 --type like --dry-run --json --output tmp/linkedin-reaction-create-dry-run.json
linkedin-cli reaction delete urn:li:ugcPost:1234567890 --dry-run --json
linkedin-cli social metadata urn:li:ugcPost:1234567890 --json
linkedin-cli social metadata urn:li:ugcPost:1234567890 --json --output tmp/linkedin-social-metadata.json
linkedin-cli social comments-state urn:li:ugcPost:1234567890 --state closed --dry-run --json
linkedin-cli social comments-state urn:li:ugcPost:1234567890 --state closed --dry-run --json --output tmp/linkedin-comments-state-dry-run.json
linkedin-cli insights media urn:li:ugcPost:1234567890 --json
linkedin-cli insights organization urn:li:organization:123456 --json
linkedin-cli insights user --json
linkedin-cli insights user --json --output tmp/linkedin-insights-user.json

๊ธด ๊ธ€์ด๋‚˜ ์ƒ์„ฑ๋œ ๊ธ€์€ inline text๋ณด๋‹ค ํŒŒ์ผ ์ž…๋ ฅ์„ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค.

linkedin-cli post text --text-file draft.md --visibility public --dry-run --json
linkedin-cli post text --text-file draft.md --visibility public --json

๊ณต์‹ OAuth ํ† ํฐ ๋ฐœ๊ธ‰

๊ณต์‹ post.* ๋ช…๋ น์€ LinkedIn Developer app๊ณผ w_member_social ๊ถŒํ•œ์ด ์žˆ๋Š” access token์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ๊ณต์‹ comment.*, reaction.*, social.* ๋ช…๋ น์€ LinkedIn app/product ์Šน์ธ ์ƒํƒœ์— ๋”ฐ๋ผ w_member_social_feed, r_member_social_feed, w_organization_social_feed, r_organization_social_feed ๊ฐ™์€ ์ถ”๊ฐ€ ๊ถŒํ•œ์ด ํ•„์š”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

1. LinkedIn Developer app ๋งŒ๋“ค๊ธฐ

LinkedIn Developer Portal์„ ์—ฝ๋‹ˆ๋‹ค.

https://www.linkedin.com/developers/apps

์•ฑ์„ ๋งŒ๋“ค๊ณ  ํ•„์ˆ˜ ํ•ญ๋ชฉ์„ ์ฑ„์›๋‹ˆ๋‹ค.

  • App name
  • LinkedIn Page
  • Privacy policy URL
  • App logo
  • API Terms ๋™์˜

LinkedIn Page๊ฐ€ ์—†๋‹ค๋ฉด ์ƒˆ๋กœ ๋งŒ๋“ค๊ฑฐ๋‚˜, ๊ฐœ์ธ ๊ฐœ๋ฐœ์ž์—๊ฒŒ ํ—ˆ์šฉ๋˜๋Š” ๊ธฐ๋ณธ Page๋ฅผ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค.

2. ํ•„์š”ํ•œ product์™€ scope ํ™œ์„ฑํ™”

์•ฑ์˜ Products/Auth ์„ค์ •์—์„œ ์•„๋ž˜ scope๋ฅผ ์š”์ฒญํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

  • openid
  • profile
  • email
  • w_member_social

CLI๋Š” openid profile email๋กœ ์ธ์ฆ๋œ ๋ฉค๋ฒ„๋ฅผ ์‹๋ณ„ํ•˜๊ณ , w_member_social๋กœ ํ•ด๋‹น ๋ฉค๋ฒ„์˜ ๊ฒŒ์‹œ๊ธ€ ์ƒ์„ฑ/์ˆ˜์ •/์‚ญ์ œ๋ฅผ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค. ๋Œ“๊ธ€/๋ฐ˜์‘/์†Œ์…œ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ๋ช…๋ น์€ LinkedIn์˜ Social Feed ๊ถŒํ•œ์ด ์žˆ์–ด์•ผ ์„ฑ๊ณตํ•ฉ๋‹ˆ๋‹ค. ๊ถŒํ•œ์ด ์—†์œผ๋ฉด CLI๋Š” permission_denied JSON envelope๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

3. Redirect URL ์ถ”๊ฐ€

์•ฑ์˜ Auth ํƒญ์— CLI๊ฐ€ ์‚ฌ์šฉํ•˜๋Š” ๋กœ์ปฌ callback URL์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

http://localhost:8787/callback

host override๋ฅผ ์“ฐ๋ ค๋ฉด ์•„๋ž˜ URL๋„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

http://127.0.0.1:8787/callback

redirect URI๋Š” ์ •ํ™•ํžˆ ์ผ์น˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. --redirect-uri๋‚˜ LINKEDIN_REDIRECT_URI๋ฅผ ์“ด๋‹ค๋ฉด ๊ทธ ๊ฐ’์„ Developer Portal์—๋„ ๊ทธ๋Œ€๋กœ ์ถ”๊ฐ€ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

4. Client ID์™€ Client Secret ์„ค์ •

Auth ํƒญ์—์„œ ์•ฑ credential์„ ๋ณต์‚ฌํ•ฉ๋‹ˆ๋‹ค.

ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ๋ฐฉ์‹:

export LINKEDIN_CLIENT_ID='...'
export LINKEDIN_CLIENT_SECRET='...'

5. ํ† ํฐ ๋ฐœ๊ธ‰ ๋ฐ ์ €์žฅ

๋กœ์ปฌ OAuth flow๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.

linkedin-cli auth oauth-login

์œ ์šฉํ•œ ์˜ต์…˜:

linkedin-cli auth oauth-login --json --output tmp/linkedin-auth-oauth-login.json
linkedin-cli auth oauth-login --timeout 300
linkedin-cli auth oauth-login --no-open
linkedin-cli auth oauth-login --redirect-uri http://localhost:8787/callback

์ด ๋ช…๋ น์€ LinkedIn OAuth๋ฅผ ๋ธŒ๋ผ์šฐ์ €์—์„œ ์—ด๊ณ , callback state๋ฅผ ๊ฒ€์ฆํ•˜๊ณ , ์ธ์ฆ๋œ ๋ฉค๋ฒ„๋ฅผ ์กฐํšŒํ•œ ๋’ค ์•„๋ž˜ ํŒŒ์ผ์— ํ† ํฐ์„ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.

~/.config/linkedin/oauth.json

ํ† ํฐ ํŒŒ์ผ ๊ตฌ์กฐ:

{
 "access_token": "...",
 "author_urn": "urn:li:person:...",
 "linkedin_version": "202605"
}

์ด ํŒŒ์ผ์€ ๋น„๊ณต๊ฐœ๋กœ ๋ณด๊ด€ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. CLI๋Š” ์‚ฌ์šฉ์ž ๋ณธ์ธ๋งŒ ์ฝ์„ ์ˆ˜ ์žˆ๋Š” ํŒŒ์ผ๋กœ ์ทจ๊ธ‰ํ•ฉ๋‹ˆ๋‹ค.

6. ๊ฒŒ์‹œ ์ „ ๊ฒ€์ฆ

ํ•ญ์ƒ dry-run์„ ๋จผ์ € ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.

linkedin-cli post text --text "token smoke test" --visibility public --dry-run --json

์ตœ์ข… ๋ฌธ๊ตฌ๊ฐ€ ํ™•์ •๋œ ๋’ค์—๋งŒ ๊ฒŒ์‹œํ•ฉ๋‹ˆ๋‹ค.

linkedin-cli post text --text-file draft.md --visibility public --json

๋ฐ˜ํ™˜๋œ post id๋กœ ์‚ญ์ œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

linkedin-cli post delete urn:li:share:1234567890 --dry-run --json
linkedin-cli post delete urn:li:share:1234567890 --json

OAuth ๋ฌธ์ œ ํ•ด๊ฒฐ

Oops. We can't verify the authenticity of your request because the state parameter was modified.

  • linkedin-cli auth oauth-login์„ ์ƒˆ๋กœ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.
  • ์˜ค๋ž˜๋œ OAuth URL์„ ์žฌ์‚ฌ์šฉํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
  • CLI๊ฐ€ ์—ฐ ๋ธŒ๋ผ์šฐ์ € ํƒญ์—์„œ flow๋ฅผ ์™„๋ฃŒํ•ฉ๋‹ˆ๋‹ค.
  • Developer Portal์˜ redirect URI๊ฐ€ CLI redirect URI์™€ ์ •ํ™•ํžˆ ๊ฐ™์€์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
  • ์˜ค๋ž˜๋œ localhost callback ํŽ˜์ด์ง€๊ฐ€ ์—ด๋ ค ์žˆ์œผ๋ฉด ๋‹ซ๊ณ  ๋‹ค์‹œ ์‹œ๋„ํ•ฉ๋‹ˆ๋‹ค.

permission_denied ๋˜๋Š” w_member_social ๋ˆ„๋ฝ

  • ์•ฑ์— Share on LinkedIn / member social product๊ฐ€ ํ™œ์„ฑํ™”๋˜์–ด ์žˆ๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
  • product/scope๋ฅผ ํ™œ์„ฑํ™”ํ•œ ๋’ค auth oauth-login์„ ๋‹ค์‹œ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.
  • OAuth ๋™์˜ ํ™”๋ฉด์— w_member_social์ด ํ‘œ์‹œ๋˜๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
  • ๋Œ“๊ธ€/๋ฐ˜์‘/์†Œ์…œ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ๋ช…๋ น์ด๋ฉด w_member_social_feed/r_member_social_feed ๋˜๋Š” organization social feed ๊ถŒํ•œ์ด ํ•„์š”ํ•œ์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.

auth_expired

  • linkedin-cli auth oauth-login์„ ๋‹ค์‹œ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.

๊ณต์‹ ์ฐธ๊ณ  ๋ฌธ์„œ:

์ฝ๊ธฐ ์ธ์ฆ

์ฝ๊ธฐ ์ธ์ฆ์€ ๊ณต์‹ ์“ฐ๊ธฐ OAuth์™€ ๋ถ„๋ฆฌ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

ํ•ด๊ฒฐ ์ˆœ์„œ:

  1. LINKEDIN_COOKIE_HEADER
  2. LINKEDIN_LI_AT + LINKEDIN_JSESSIONID
  3. LINKEDIN_COOKIE_FILE ๋˜๋Š” ๊ธฐ๋ณธ ํŒŒ์ผ ~/.config/linkedin/cookies.env
  4. Chrome, Chromium, Brave, Edge, Firefox์˜ ๋ธŒ๋ผ์šฐ์ € cookie ์ถ”์ถœ

๊ถŒ์žฅ: auth login์œผ๋กœ ์ž๋™ ์บก์ฒ˜. ๋กœ๊ทธ์ธ๋œ ๋ธŒ๋ผ์šฐ์ €์—์„œ ์ฟ ํ‚ค๋ฅผ ์ถ”์ถœํ•ด private file(๊ถŒํ•œ 600)์— ์ €์žฅํ•˜๊ณ  ์„ธ์…˜์„ ๊ฒ€์ฆํ•ฉ๋‹ˆ๋‹ค. ๊ฐ’์€ ์ ˆ๋Œ€ ์ถœ๋ ฅํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

linkedin-cli auth login
linkedin-cli auth-status

์ž๋™ ์ถ”์ถœ๋œ ์ฟ ํ‚ค๊ฐ€ LinkedIn Voyager์—์„œ ๊ฑฐ๋ถ€๋˜๋ฉด Playwright ๋ธŒ๋ผ์šฐ์ € ์ฐฝ์œผ๋กœ ์ƒˆ ์„ธ์…˜์„ ์ง์ ‘ ์บก์ฒ˜ํ•ฉ๋‹ˆ๋‹ค.

linkedin-cli auth login --via-browser --browser chrome
linkedin-cli auth login --via-browser --browser firefox

Firefox๋ฅผ ์„ ํƒํ•˜๋ ค๋ฉด Playwright Firefox ๋นŒ๋“œ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค: uv run playwright install firefox.

์ž๋™ ์ถ”์ถœ์ด ์‹คํŒจํ•˜๋ฉด(macOS๋Š” ChromeยทBraveยทEdge๊ฐ€ Keychain ์ ‘๊ทผ์„ ์š”๊ตฌ โ€” --browser firefox๊ฐ€ ๊ฐ€์žฅ ์•ˆ์ •์ ) ์•„๋ž˜ ๋‹จ๊ณ„๋กœ ์ง์ ‘ ์บก์ฒ˜ํ•ฉ๋‹ˆ๋‹ค.

์ˆ˜๋™ ์บก์ฒ˜ (DevTools):

  1. ๋ธŒ๋ผ์šฐ์ €์—์„œ https://www.linkedin.com ์— ๋กœ๊ทธ์ธ๋œ ์ƒํƒœ๋ฅผ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
  2. DevTools๋ฅผ ์—ฝ๋‹ˆ๋‹ค (macOS Option+Command+I, ๋˜๋Š” F12).
  3. Application ํƒญ โ†’ Storage โ†’ Cookies โ†’ https://www.linkedin.com.
  4. li_at์™€ JSESSIONID ๊ฐ’์„ ๋ณต์‚ฌํ•ฉ๋‹ˆ๋‹ค (JSESSIONID๋Š” "ajax:..." ํ˜•ํƒœ์ด๋‹ˆ ๋”ฐ์˜ดํ‘œ๋ฅผ ํฌํ•จํ•ด ๋ณต์‚ฌ).
  5. ํ•œ ์ค„๋กœ ๋งŒ๋“ญ๋‹ˆ๋‹ค: li_at=<๊ฐ’>; JSESSIONID=<๊ฐ’>
  6. ์ €์žฅ: linkedin-cli auth cookie-file --from-stdin์„ ์‹คํ–‰ํ•˜๊ณ  ๊ทธ ์ค„์„ ๋ถ™์—ฌ๋„ฃ์€ ๋’ค Return, Ctrl-D.
  7. ๊ฒ€์ฆ: linkedin-cli auth-status

๋˜๋Š” DevTools Network ํƒญ์—์„œ www.linkedin.com ์š”์ฒญ์˜ cookie: request header ์ „์ฒด๋ฅผ ๋ณต์‚ฌํ•ด ๊ฐ™์€ ๋ช…๋ น์— ๋ถ™์—ฌ๋„ฃ์–ด๋„ ๋ฉ๋‹ˆ๋‹ค(LinkedIn์ด ๊ฐ€๋” ์š”๊ตฌํ•˜๋Š” ๋” ์™„์ „ํ•œ cookie jar).

์ด ๊ฐ’๋“ค์€ LinkedIn ๋น„๋ฐ€๋ฒˆํ˜ธ์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค โ€” ์ฑ„ํŒ…์— ๋ถ™์—ฌ๋„ฃ๊ฑฐ๋‚˜ ์ปค๋ฐ‹ยท๊ณต์œ ํ•˜์ง€ ๋งˆ์„ธ์š”.

์ผํšŒ์„ฑ env ๋ฐฉ์‹:

export LINKEDIN_COOKIE_HEADER='li_at=...; JSESSIONID="ajax:..."; ...'
linkedin-cli auth-status

์ตœ์†Œ cookie ๋ณ€์ˆ˜. authwall/checkpoint๋‚˜ redirect๊ฐ€ ๋‚˜์˜ค๋ฉด ์ „์ฒด Cookie header๋ฅผ ์‚ฌ์šฉํ•˜์„ธ์š”.

export LINKEDIN_LI_AT='AQ...'
export LINKEDIN_JSESSIONID='"ajax:123456789"'

์„ ํƒ์  ๋ธŒ๋ผ์šฐ์ € ์„ค์ •:

export LINKEDIN_BROWSER='chrome'
export LINKEDIN_HEADLESS='1'
export LINKEDIN_PROXY='http://127.0.0.1:7890'
export LINKEDIN_CONFIG="$PWD/config.yaml"
export LINKEDIN_COOKIE_FILE="$HOME/.config/linkedin/cookies.env"
export LINKEDIN_BROWSER_STATE="$HOME/.config/linkedin-cli/browser-state.json"

๋ช…๋ น ๋ ˆํผ๋Ÿฐ์Šค

ํ‘œ์ค€ JSON ๋ช…๋ น:

linkedin-cli auth-status
linkedin-cli auth oauth-login
linkedin-cli read feed --limit 20 --json
linkedin-cli read saved --limit 20 --json
linkedin-cli read profile your-handle --json
linkedin-cli read profile-posts your-handle --limit 5 --json
linkedin-cli read activity urn:li:activity:1234567890 --json
linkedin-cli read comments urn:li:activity:1234567890 --limit 20 --json
linkedin-cli read reactions urn:li:activity:1234567890 --limit 20 --json
linkedin-cli read search "product manager" --limit 10 --json
linkedin-cli saved list --limit 20 --json
linkedin-cli saved unsave urn:li:activity:123 --dry-run --json
linkedin-cli post text --text "hello" --visibility public --dry-run --json
linkedin-cli post text --text-file draft.md --visibility public --json
linkedin-cli post media --text "hello with image" --media image.png --visibility public --json
linkedin-cli post multi-image --text "hello album" --media one.png --media two.jpg --json
linkedin-cli post video --text "hello video" --video clip.mp4 --title "Demo" --json
linkedin-cli post document --text "hello deck" --document deck.pdf --title "Deck" --json
linkedin-cli post poll --text "vote" --question "Pick one" --option Red --option Blue --duration three-days --json
linkedin-cli post article --text "read this" --url https://example.com/post --json
linkedin-cli post reshare urn:li:share:1234567890 --text "worth reading" --json
linkedin-cli post quote urn:li:share:1234567890 --text "worth reading" --json
linkedin-cli post reply urn:li:ugcPost:1234567890 --text "great post" --json
linkedin-cli post repost urn:li:share:1234567890 --dry-run --json
linkedin-cli post update urn:li:share:1234567890 --text "updated text" --json
linkedin-cli post get urn:li:share:1234567890 --json
linkedin-cli post list --limit 10 --json
linkedin-cli post delete urn:li:share:1234567890 --dry-run --json
linkedin-cli post delete urn:li:share:1234567890 --json
linkedin-cli comment list urn:li:ugcPost:1234567890 --json
linkedin-cli comment get urn:li:ugcPost:1234567890 987654321 --json
linkedin-cli comment create urn:li:ugcPost:1234567890 --text "great post" --dry-run --json
linkedin-cli comment update urn:li:ugcPost:1234567890 987654321 --text "updated comment" --dry-run --json
linkedin-cli comment delete urn:li:ugcPost:1234567890 987654321 --dry-run --json
linkedin-cli reaction list urn:li:ugcPost:1234567890 --json
linkedin-cli reaction get urn:li:ugcPost:1234567890 --json
linkedin-cli reaction create urn:li:ugcPost:1234567890 --type like --dry-run --json
linkedin-cli reaction delete urn:li:ugcPost:1234567890 --dry-run --json
linkedin-cli social metadata urn:li:ugcPost:1234567890 --json
linkedin-cli social metadata urn:li:ugcPost:1234567890 --json --output tmp/linkedin-social-metadata.json
linkedin-cli social comments-state urn:li:ugcPost:1234567890 --state open --dry-run --json
linkedin-cli insights media urn:li:ugcPost:1234567890 --json
linkedin-cli insights organization urn:li:organization:123456 --json
linkedin-cli insights user --json
linkedin-cli insights user --json --output tmp/linkedin-insights-user.json

Legacy ํ˜ธํ™˜ ๋ช…๋ น:

linkedin-cli feed --max 10
linkedin-cli search "product manager" --max 10
linkedin-cli profile your-handle --json --output tmp/linkedin-profile.json
linkedin-cli profile-posts your-handle --max 20
linkedin-cli activity urn:li:activity:123 --json --output tmp/linkedin-activity.json
linkedin-cli post "hello from browser fallback"
linkedin-cli react urn:li:activity:123 --type like
linkedin-cli unreact urn:li:activity:123
linkedin-cli save urn:li:activity:123
linkedin-cli unsave urn:li:activity:123
linkedin-cli comment urn:li:activity:123 "nice post"

JSON ๊ณ„์•ฝ

๋ชจ๋“  ํ‘œ์ค€ --json ๋ช…๋ น์€ ํ•˜๋‚˜์˜ sns-json-v1 envelope๋งŒ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค.

{
 "schema_version": "sns-json-v1",
 "ok": true,
 "platform": "linkedin",
 "command": "post.text",
 "source": "official",
 "request": {},
 "data": {},
 "error": null,
 "warnings": [],
 "meta": {
 "cli_name": "linkedin-cli"
 }
}

secret์€ request, data, raw, log์— ์“ฐ์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

Python API

from pathlib import Path
from linkedin_cli import LinkedInWriteAPI
api = LinkedInWriteAPI.from_config()
plan = api.plan_text_post(text=Path("draft.md").read_text(), visibility="public")
print(plan.to_dict())
result = api.create_text_post(text=Path("draft.md").read_text(), visibility="public")
print(result.url)
delete_plan = api.plan_delete_post(post_id=result.post_id)
print(delete_plan.to_dict())
delete_result = api.delete_post(post_id=result.post_id)
print(delete_result.deleted_at)

Skills

์ด repo๋Š” ์…‹์—…, ์ธ์ฆ, ์ฝ๊ธฐ/์“ฐ๊ธฐ ์›Œํฌํ”Œ๋กœ, ๋ช…๋ น ์„ ํƒ์„ ํ•˜๋‚˜๋กœ ๋‹ค๋ฃจ๋Š” project-local linkedin-cli skill์„ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค. ์ •๋ณธ์€ .agents/skills/linkedin-cli/SKILL.md์ด๋ฉฐ, skills/, .claude/skills/, .codex/skills/๋Š” ์ด skill๋กœ ์—ฐ๊ฒฐ๋œ project-local ์‹ฌ๋ณผ๋ฆญ ๋งํฌ์ž…๋‹ˆ๋‹ค. ํ”Œ๋Ÿฌ๊ทธ์ธ์œผ๋กœ๋„ ๊ฐ™์€ skill์„ ์„ค์น˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค(.claude-plugin/plugin.json). ํ”Œ๋Ÿฌ๊ทธ์ธ์€ skill๋งŒ ์ œ๊ณตํ•˜๋ฏ€๋กœ, skill์„ ์ฒ˜์Œ ์“ธ ๋•Œ linkedin-cli ๋ช…๋ น์ด ์—†์œผ๋ฉด scripts/ensure-cli.sh๊ฐ€ agent-linkedin์„ ์ž๋™ ์„ค์น˜ํ•ฉ๋‹ˆ๋‹ค(uv ๋˜๋Š” pipx๊ฐ€ ๋จผ์ € ์„ค์น˜๋ผ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค). ๊ทธ ๋’ค auth login โ†’ auth-status๋กœ read ์ธ์ฆ์„ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.

  • SKILL.md โ€” skill entrypoint
  • initial-setup.md โ€” ์ฒซ ์…‹์—…๊ณผ OAuth/์ฟ ํ‚ค ์ธ์ฆ
  • command-cookbook.md โ€” ์ •ํ™•ํ•œ ๋ช…๋ น ํŒจํ„ด๊ณผ JSON ์‚ฌ์šฉ
  • auth-troubleshooting.md โ€” ์„ธ์…˜ ๋ณต๊ตฌ์™€ ์ง„๋‹จ
  • write-workflows.md โ€” ๊ณต์‹ ๋ฐœํ–‰๊ณผ ์•ˆ์ „ํ•œ mutation

๊ฐœ๋ฐœ

uv sync --extra dev
uv run playwright install chromium
uv run ruff check .
uv run pytest -q
uv run python -m compileall linkedin_cli tests

ํ…Œ์ŠคํŠธ ๊ทœ์น™:

  • Unit test๋Š” live LinkedIn session์— ์˜์กดํ•˜์ง€ ์•Š์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • ๋„คํŠธ์›Œํฌ์— ๋ฏผ๊ฐํ•œ ๋™์ž‘์€ transport/browser abstraction ๋’ค์— ๋‘ก๋‹ˆ๋‹ค.
  • ๋ฆด๋ฆฌ์Šค ์ „ live verification์€ ์œ ์šฉํ•˜์ง€๋งŒ ์ผ๋ฐ˜ CI์˜ ํ•„์ˆ˜ ์กฐ๊ฑด์œผ๋กœ ๋‘์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๋ณด์•ˆ

  • cookie, OAuth token, HAR file, browser storage state๋ฅผ commitํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
  • LINKEDIN_COOKIE_HEADER, li_at, JSESSIONID, ~/.config/linkedin/cookies.env, access token, client secret, token file์„ issue๋‚˜ PR์— ๋ถ™์ด์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
  • screenshot, log, terminal transcript๋ฅผ ๊ณต์œ ํ•˜๊ธฐ ์ „์— secret์„ ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค.

SECURITY.md๋ฅผ ์ฐธ๊ณ ํ•˜์„ธ์š”.

๊ธฐ์—ฌ

์•„๋ž˜ ๋ฌธ์„œ๋ฅผ ๋จผ์ € ์ฝ์–ด์ฃผ์„ธ์š”.

๋ผ์ด์„ ์Šค

MIT. LICENSE๋ฅผ ์ฐธ๊ณ ํ•˜์„ธ์š”.

๊ฐ์‚ฌ

linkedin-cli๋Š” Juan Francisco Lebrero์˜ frizynn/linkedin-cli์—์„œ ์‹œ์ž‘ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด fork๋Š” ๊ณต์‹ LinkedIn OAuth publishing, JSON contract layer, Python write API, Codex/Claude skill packaging์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. ์›๋ณธ ์ž‘์—…์€ MIT ๋ผ์ด์„ ์Šค์ด๋ฉฐ copyright๋Š” LICENSE์— ๋ณด์กด๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

About

Terminal-first LinkedIn CLI: unofficial session-based reads + official OAuth publishing, with packaged Codex/Claude skills

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Packages

Contributors

AltStyle ใซใ‚ˆใฃใฆๅค‰ๆ›ใ•ใ‚ŒใŸใƒšใƒผใ‚ธ (->ใ‚ชใƒชใ‚ธใƒŠใƒซ) /