Building Social Media Automation: LinkedIn Sharing with Serverless Function
After publishing a new article or blog post, the need to promote it on social media arises. Manually sharing the post can be time-consuming and inefficient. In this article, we will explore how to build a serverless function to share an article URL on LinkedIn using its JavaScript API client and Netlify serverless functions. This is part of building an automated workflow for social media promotion.
Table of Contents
- Table of Contents
- Prerequisites
- Getting started
- Sharing a post with URL using LinkedIn API Client
- Exposing as a Netlify serverless function
- Testing the Functionality
- Deploying with Netlify
- Summary
Prerequisites
To follow along with this tutorial, you will need the following:
- A LinkedIn account
- Node.js and Netlify CLI installed.
- A Netlify account and a site created for deploying the serverless function.
- Basic knowledge of JavaScript and TypeScript.
Getting started
To start working with LinkedIn APIs, we need to perform the following steps:
- Head to LinkedIn Developer Console with your LinkedIn account.
- Create a new app by clicking on the
Create App
button. - Fill in the details such as App Name (Social Media Tester, for example) and App logo image.
- You will need to a LinkedIn Company page to associate with the app you are creating (any page you have admin access to be able to verify the connection afterwards).
Once done, the portal will redirect you to the app dashboard, where we can start configuring the permissions and API products we need for the app.
Setting up the permissions
In the app dashboard, click on Products tabs and request access to the Share on LinkedIn
and Sign In with LinkedIn using OpenID Connect
products.
Configuring OAuth 2.0 settings
With these permissions granted, we can head to the OAuth 2.0 token generator tool to generate an access token for the app. The token should include the following scopes: w_member_social
for posting on behalf of the user, and profile
and openid
for user authentication and profile information.
This access token is a 3-legged OAuth token, ensuring that the user has explicitly authorized the application to act on their behalf. After generating the token, we can use it to authenticate and securely make requests to the LinkedIn APIs. Additionally, we can review the permissions and scopes granted to the app in the Auth tab of the app dashboard.
Great! Now that we have the access token and the app set up, we can start building the automation to post on LinkedIn on behalf of the user (which, in this case, is us).
Sharing a post with URL using LinkedIn API Client
To start sharing posts programmatically, we can use the official LinkedIn API JavaScript Client for Node.js by installing it as a project dependency:
npm install linkedin-api-client
# or with yarn
yarn add linkedin-api-client
This library provides a straightforward and lightweight way to interact with LinkedIn API endpoints, leveraging Axios and TypeScript under the hood.
Next, let's create a new file, linkedin.ts
, to encapsulate the logic for sharing posts on LinkedIn. We start by initializing a client instance to interact with the API, as shown below:
// linkedin.ts
import { RestliClient } from 'linkedin-api-client';
const client = new RestliClient();
Getting the user's unique id
To post on behalf of a user, we first need to retrieve the user's unique ID (which is different from the user's LinkedIn handle). This can be done by using the /userinfo
endpoint with the access token generated earlier:
// linkedin.ts
const getUserId = async (accessToken: string) => {
const userResponse = await client.get({
resourcePath: "/userinfo",
accessToken
});
return userResponse.data?.sub;
};
The unique ID is located in the sub
field of the response's data
. This value is required for the next step: sharing a post on the user's behalf.
Sharing a post URL
Within linkedin.ts
, we define a function to share a post's URL as follows:
type SharePostArgs = {
url: string;
text: string;
};
export const sharePost = async (token: string, content: SharePostArgs) => {
//logic
};
The sharePost
function takes the access token and the content to share, which includes the URL and the text to accompany the post. We will then create
a new post entity
on the User Generated Contents resource using /ugcPosts
endpoint, as shown below:
export const sharePost = async (token: string, content: SharePostArgs) => {
const response = await client.create({
resourcePath: '/ugcPosts',
accessToken: token,
entity: {
//entity payload
}
});
};
The entity
payload is configured to include the user’s unique ID, retrieved earlier, as the author
. The author
field follows the format urn:li:person:${userId}
. Additionally, we specify:
- The
lifecycleState
as"PUBLISHED"
. - The
visibility
as"PUBLIC"
so the post is visible to the LinkedIn network.
Here’s the updated implementation:
export const sharePost = async (token: string, content: SharePostArgs) => {
//Get user's unique id
const userId = await getUserId(token);
const response = await client.create({
resourcePath: '/ugcPosts',
accessToken: token,
entity: {
author: `urn:li:person:${userId}`,
lifecycleState: "PUBLISHED",
visibility: {
"com.linkedin.ugc.MemberNetworkVisibility": "PUBLIC"
}
}
});
};
Next, we define the sharing content within the specificContent
field of the entity
object. For this scenario, the specificContent
field includes a com.linkedin.ugc.ShareContent
object, which has the following properties:
shareCommentary
: Acceptscontent.text
as the main text content to display.shareMediaCategory
: Specifies the type of media shared in the post (set as"ARTICLE"
).media
: An array of media assets for the"ARTICLE"
category, where each item includes: the URL to share and aREADY
status.
Below is the updated code:
//...
entity: {
//...
specificContent: {
"com.linkedin.ugc.ShareContent": {
shareCommentary: {
text: content.text
},
shareMediaCategory: "ARTICLE",
media: [
{
status: "READY",
originalUrl: content.url,
}
]
}
}
}
Upon successfully completing the request, the response contains a createdEntityId
, representing the unique ID of the created entity. We can return this value to the caller for further reference:
export const sharePost = async (token: string, content: SharePostArgs) => {
//...
return response.createdEntityId;
};
And that's it! We’ve created a function that leverages the LinkedIn API to share a post URL on behalf of a user. In the next step, we’ll expose this function as a serverless endpoint using Netlify, bringing us closer to fully automating the process of sharing articles on social media.
Exposing as a Netlify serverless function
We run the CLI command netlify functions:create
and follow the prompts to scaffold a new Netlify serverless function named share-on-linkedin
. The Netlify CLI will generate the function in the functions directory with the following initial code:
/* functions/share-on-linkedin.mts */
import type { Context } from '@netlify/functions'
export default async (request: Request, context: Context) => {
try {
return new Response(JSON.stringify("Hello"), {
headers: {
'content-type': 'application/json',
},
})
} catch (error) {
// Return an error response
return new Response(
JSON.stringify({ error: error.message || 'An error occurred' }),
{
status: 500,
headers: {
'content-type': 'application/json',
},
}
);
}
};
In this above code, we use TypeScript and define the function as async
to handle the asynchronous nature of LinkedIn API calls.
Next, we update the serverless function to perform the following actions:
- Parse the request body to extract the content to share,
- Retrieve the access token from environment variables (can be set in
.env
file in the project root), - Call the
sharePost
function (defined earlier inlinkedin.ts
) with the extracted parameters, and - Return the created entity ID as the response.
import type { Context } from '@netlify/functions'
import { sharePost } from '../utils/linkedin';
//Retrieve the access token from environment variables
const TOKEN = process.env.LINKEDIN_ACCESS_TOKEN;
export default async (request: Request, context: Context) => {
try {
//Parse the request body
const body = await request.json()
const { url, text } = body
//Share the post
const createdEntityId = await sharePost(TOKEN, { url, text });
//Return the created entity ID
return new Response(JSON.stringify({ createdEntityId }), {
headers: {
'content-type': 'application/json',
},
})
} catch (error) {
//...
}
}
At this point, the serverless function is ready. We can deploy it to Netlify and test its functionality by making POST requests to the endpoint.
Testing the Functionality
To test the serverless function, start a local server using the CLI command netlify dev
. Then, use a tool like Postman or Insomnia to send a POST request to the server endpoint with the following JSON payload:
{
"url": "https://mayashavin.com/articles/share-onbehalf-linkedin",
"text": "Check out this awesome article!"
}
Alternatively, we can create a simple form UI to interact with the serverless function API, and verify that the post is successfully shared on LinkedIn.
Once the function is working as expected, let's proceed to deploy it to Netlify to make it available for use.
Deploying with Netlify
To deploy our function to Netlify, run the following command:
netlify deploy --prod
The CLI deploys the function to your Netlify production environment. You can then find the function endpoint in the Netlify dashboard.
Important: Make sure to configure the LINKEDIN_ACCESS_TOKEN
environment variable in the dashboard. This step is essential for the function to authenticate and operate correctly.
Summary
We have successfully built a serverless API to share an article URL on LinkedIn on behalf of a user, leveraging the LinkedIn API JavaScript Client and Netlify serverless functions. This marks a significant step toward automating the social media sharing process for blog posts.
From here, we can extend the automation workflow to include other social media platforms and scheduled tasks. For example, we could integrate platforms like X (formerly Twitter), Facebook, or BlueSky, and customize the timing and content of posts to maximize audience engagement and reach.
With that, stay tuned for more updates on this series!
👉 Learn about Vue 3 and TypeScript with my new book Learning Vue!
👉 If you'd like to catch up with me sometimes, follow me on X | LinkedIn.
Like this post or find it helpful? Share it 👇🏼 😉
Learning Vue
Learn the core concepts of Vue.js, the modern JavaScript framework for building frontend applications and interfaces from scratch