How to Automate LinkedIn Posts with Python 2025
I have a simple workflow: I write my posts in WordPress, and once they’re ready, a workflow kicks off to get them published. A key part of this process is announcing the new post on LinkedIn to let my network know it’s live.
Sounds easy, right? Well, I quickly discovered that most Python libraries for the LinkedIn API are outdated. LinkedIn has deprecated large parts of its old API, leaving many tools broken.
After a bit of digging, I found a solid solution using just Python and the requests library. With this method, I can successfully post to my personal profile and into groups I manage. While I couldn’t get it to work for a company page, this covers most of my needs. Here’s how you can do it too.
Step 1: Getting the Bloody Access Token
Before we can do anything, we need an access token. Be warned, this is the most painful part of the process.
Create a LinkedIn App
First, you need to head over to the LinkedIn Developer Portal and create an app. Fill out all the necessary details, give it a logo, and make sure you verify it if needed.
Inside your app settings, you’ll find your Client ID and Client Secret. You’ll also need to add a Redirect URI. For this guide, we’ll use a dummy URL: https://localhost:3000/callback. Just make sure the URL you add here is the exact same one you use in the next step.
Authorize Your App
Now, you need to grant your newly created app permission to access your LinkedIn account. To do this, construct the following URL, replacing YOUR_CLIENT_ID with your app’s Client ID.
https://www.linkedin.com/oauth/v2/authorization?response_type=code&client_id=YOUR_CLIENT_ID&redirect_uri=https://localhost:3000/callback&state=foobar&scope=openid,profile,w_member_social,w_organization_social
Paste this URL into your browser. You’ll be asked to log in to LinkedIn and authorize the app. After you approve it, you’ll be redirected to the dummy URL we set up. Since localhost:3000 isn’t running anything, you’ll see an error page. This is expected!
Look at the URL in your browser’s address bar. It will look something like this:
See that long string of characters after code=? That’s your authorization code. Copy it!
Exchange the Code for an Access Token
Now we trade that code for our final access token. Open your terminal and use the following cURL command. Replace the placeholders with your Client ID, Client Secret, and the authorization code you just copied.
Bash
curl --location --request POST 'https://www.linkedin.com/oauth/v2/accessToken' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=authorization_code' \
--data-urlencode 'code=PASTE_YOUR_AUTHORIZATION_CODE_HERE' \
--data-urlencode 'client_id=PASTE_YOUR_CLIENT_ID_HERE' \
--data-urlencode 'client_secret=PASTE_YOUR_CLIENT_SECRET_HERE' \
--data-urlencode 'redirect_uri=https://localhost:3000/callback'
When you run this, the response will be a JSON object containing your access_token. Save it securely—this is the key to making API calls.
Step 2: Finding Your User and Group IDs
To post, you need to know who is posting and where they are posting. In the LinkedIn API, these are identified by URNs (Uniform Resource Names).
Get Your Personal URN
You can find your personal URN by making a simple API call. Run the following Python script, making sure to add your new access token.
Python
import requests
# Paste the access token you received
ACCESS_TOKEN = "YOUR_ACCESS_TOKEN_HERE"
headers = {
"Authorization": f"Bearer {ACCESS_TOKEN}"
}
api_url = "https://api.linkedin.com/v2/userinfo"
try:
response = requests.get(api_url, headers=headers)
response.raise_for_status()
user_info = response.json()
print(f"Success! Your URN is: {user_info['sub']}")
except requests.exceptions.RequestException as e:
print(f"An error occurred: {e}")
The output will contain your URN in the sub field. It will look like urn:li:person:xxxxxxxx.
Get a Group URN
To find a group’s ID, navigate to the group on LinkedIn and click to manage or edit it. The group ID is the number in the URL. Your Group URN will be urn:li:group:GROUP_ID.
Step 3: Let’s Post to LinkedIn!
With the access token and URNs in hand, we can finally post. The script below sends a simple text post.
- Set the
ACCESS_TOKEN. - Set the
authorto your personal URN. - To post to a group, set the
containerEntityto the group’s URN. To post to your personal profile, comment out or remove the"containerEntity"line entirely. - Change the
textinsideshareCommentaryto whatever you want to post.
Python
import requests
import json
# Your access token
ACCESS_TOKEN = "YOUR_ACCESS_TOKEN_HERE"
# Your personal URN (from the previous step)
YOUR_PERSON_URN = "urn:li:person:xxxxxxxx"
# The URN of the group you want to post to
GROUP_URN = "urn:li:group:1234567"
def post_to_linkedin(post_text):
"""Posts a message to a LinkedIn group or personal profile."""
api_url = "https://api.linkedin.com/v2/ugcPosts"
headers = {
"Authorization": f"Bearer {ACCESS_TOKEN}",
"Content-Type": "application/json",
"X-Restli-Protocol-Version": "2.0.0"
}
post_data = {
"author": YOUR_PERSON_URN,
"lifecycleState": "PUBLISHED",
# To post to a group, include the line below.
# To post to your personal feed, remove it.
"containerEntity": GROUP_URN,
"specificContent": {
"com.linkedin.ugc.ShareContent": {
"shareCommentary": {
"text": post_text
},
"shareMediaCategory": "NONE"
}
},
"visibility": {
"com.linkedin.ugc.MemberNetworkVisibility": "PUBLIC"
}
}
try:
response = requests.post(api_url, headers=headers, data=json.dumps(post_data))
response.raise_for_status() # Raise an exception for bad status codes
print("Post successful! ")
print(response.json())
except requests.exceptions.RequestException as e:
print(f"An error occurred: {e}")
if e.response:
print(f"Response status code: {e.response.status_code}")
print(f"Response content: {e.response.text}")
# Example usage:
my_new_blog_post_announcement = "I just published a new blog post about automating LinkedIn with Python! Check it out. #python #automation #linkedinapi"
post_to_linkedin(my_new_blog_post_announcement)
And that’s it! While the initial setup with OAuth 2.0 is a bit of a marathon, once you have the token, automating your posts from any Python script is straightforward. Happy automating!