Skip to content
Go back

How to publish a post with the LinkedIn API

How to publish a post with the LinkedIn API

Motivation

You might have many different reasons why you want to use the LinkedIn API, but most of them usually boil down to automation.

Personally, I would like to automate the process of marketing my articles on Medium, my newsletter Cloud Native Engineer on Substack, and my personal blog at gsantoro.dev to social media platforms like LinkedIn, X (formerly Twitter), or Instagram.

Lessons learned from my automation attempt, include not just LinkedIn API but also OAuth2 (used by all other social media APIs for authorisation) and Open Graph meta tags.

So keep reading if you want to know more.

How to generate an OAuth2.0 access token for LinkedIn

First of all, we need to create a new App from the LinkedIn developer portal.

You need to provide:

As stated in the developer portal:

The LinkedIn Company Page you select will be associated with your app. Verification can be done by a Page Admin. Please note this cannot be a member profile page.

The last step in creating the application is to agree on the terms and conditions.

Now that your application has been created you need to configure the authorisation permissions.

Under Settings -> App settings of the developer portal, you need to verify the application by generating and then opening a temporary verification URL.

Under Auth, you can find the client id and client secret that we are going to use later to generate the access token.

Under Products you need to Request Access from both Share on LinkedIn and Sign In with LinkedIn using OpenID Connect in order to add the necessary OAuth 2.0 scopes. The first set of APIs will add the scope named w_member_social that allows creating, modifying, and deleting posts; while the second set of APIs will add the scope named openid and email that allow you to hit the endpoint v2/userinfo that is necessary to get the author ID used for posting the articles.

Fortunately, you need to follow the previous steps only once for each application.

Now that we have configured the permissions, we have two different options to create an access token.

If you like me, are the only one using this LinkedIn application for personal use, you create an access token from the developer portal at LinkedIn OAuth2 tools. This is by far the easiest option.

Otherwise, if you want to create an access token programmatically, you can either look at the official documentation at Authorization Code Flow (3-legged OAuth) or use Postman to request an OAuth 2.0 token.

In either case, the access token has a validity of 60 days from the moment it was generated.

Unfortunately, LinkedIn doesn’t create long-lived access tokens as stated in their documentation.

To protect members’ data, LinkedIn does not generate long-lived access tokens. Make sure your application refreshes access tokens before they expire, to avoid unnecessarily sending your application’s users through the authorization process again.

If you choose to go with Postman, in the authorisation workflow to create a new access token you need to:

We will see in the next session how to use the generated access token to interact with the LinkedIn API.

How to publish an article on LinkedIn

In this section, we are only exploring one of the LinkedIn API endpoints, to publish an article on a user’s personal profile. For more information on this and other endpoints refer to the official documentation.

I found the Microsoft documentation a bit confusing to navigate at first. That’s why I wrote this article, to make it very simple for someone else (or a future me) to reproduce it.

Once you have the right pointers, all of a sudden the documentation starts making sense again.

A couple of caveats before I show you the code:

In order to get the user_id, you need to run the following code. This step can be done once and the results are stored for future use.

export LINKEDIN_ACCESS_TOKEN=<access_token>

curl --location 'https://api.linkedin.com/v2/userinfo' \
--header 'Authorization: Bearer ${LINKEDIN_ACCESS_TOKEN}'

Once you have the user_id, you can post your article by providing a URL and the content of your post.

export LINKEDIN_ACCESS_TOKEN=<access_token>


# valid values: person, organization
export AUTHOR_TYPE=person

# if author_type=person => <user_id>
# if author_type=organization => <organization_id>
export LINKEDIN_AUTHOR_ID=<author_id>

# Url of an article with OpenGraph meta tags
export ARTICLE_URL=<article_url>

# New line escaped text
export ARTICLE_CONTENT=<article_content>


curl --location 'https://api.linkedin.com/v2/ugcPosts' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer ${LINKEDIN_ACCESS_TOKEN}' \
--data '{
    "author": "urn:li:${AUTHOR_TYPE}:${LINKEDIN_USER_ID}",
    "lifecycleState": "PUBLISHED",
    "specificContent": {
        "com.linkedin.ugc.ShareContent": {
            "shareCommentary": {
                "text": "${ARTICLE_CONTENT}"
            },
            "shareMediaCategory": "ARTICLE",
            "media": [
                {
                    "status": "READY",
                    "originalUrl": "${ARTICLE_URL}"
                }
            ]
        }
    },
    "visibility": {
        "com.linkedin.ugc.MemberNetworkVisibility": "PUBLIC"
    }
}'

Just to give you an example of what the result looks like if you were to use the following environment variables in the code snippet above

export ARTICLE_URL="https://cloudnativeengineer.substack.com/p/ep-9-exploring-structured-logging"

export ARTICLE_CONTENT="I've just published my latest article on my newsletter https://lnkd.in/dK_j8acf on\nSubstack about structured logging and the new #golang package slog.\n\nCheck it out at https://lnkd.in/d7c-smzY\n\n#golang #observability #programming\n#opensource #DevOps"

You would generate the following results:

Sample LinkedIn post generated by the code above

As you can see above, if you want to provide a multi-line text for the content you need to escape the new line character with \n. This is because the API accepts only valid JSON content as the request body.

The official documentation to Share on LinkedIn describes that you can also provide two optional fields Title and Description fields as well in the same request body.

In the previous request, those two fields are left out since their values will be automatically taken from the article content using the Open Graph meta tags.

When you are providing an article URL and content text, LinkedIn will scrape the title and description from the Open Graph meta tags, plus it will also add an image preview.

If you were to provide an empty string for either of those fields, the open graph meta tags won’t be used instead and you won’t see an image preview either.

How to add a title, description, and image preview from Open Graph meta tags, is a bit of information not present anywhere in the official documentation. It took me a while to figure out.

Read the next section to learn more about the Open Graph meta tags.

What are Open Graph meta tags?

The Open Graph meta tags are metadata that can be added to html page to describe its content.

Substack does this automatically when you create a new article, but you can reproduce the same results on your own blog as well.

You can inspect the Open Graph meta tags by using the LinkedIn post inspector. If you provide the previous article URL you will get the following tags.

LinkedIn Post Inspector

The Open Graph meta tags specification has been created by Facebook, but those tags are now used by all major social media platforms like LinkedIn. You can read more about these meta tags in Facebook’s open graph protocol documentation.

Twitter uses a similar set of tags, but the concept is the same.

Conclusion

I hope you find this article informative and I hope you can apply the lessons learned here to your own automation scripts.

I’m planning to write many more articles in the future when I share a similar approach for other social media platforms like X (formerly Twitter) and Instagram and how to interact with ChatGPT API to summarise the article contents for social media content.



Share this post on:

Previous Post
Unravelling the Role of Content Delivery Networks in System Design
Next Post
Scribus: the best free Adobe InDesign alternative