Share/Upload posts on the LinkedIn Page using the API

Share/Upload posts on the LinkedIn Page using the API

LinkedIn Page Creation

Before using the LinkedIn v2 API to post on a page, ensure the LinkedIn page exists.

  • Step 1: Sign in to your LinkedIn account.

  • Step 2: Go to the "Work" menu (top-right) from your LinkedIn profile and select Create a Company Page.

  • Step 3: Follow the instructions to create a Company or Page. Ensure you have admin access to manage and post on this page.

App Creation on the LinkedIn Developer Portal

To use the LinkedIn API, you need to create an app that will allow you to authenticate and interact with LinkedIn pages.

  • Step 1: Visit the LinkedIn Developer Portal.

  • Step 2: Log in with your LinkedIn account.

  • Step 3: In the top right, click on Create App.

  • Step 4: Fill out the required information such as: ○ App Name ○ LinkedIn Page you want to connect with
    ○ App Logo
    ○ Business Email Address
    ○ App Description
    ○ Privacy Policy URL (required)

  • Step 5: Agree to the terms and click Create App.

  • Step 6: Once the app is created, you will be redirected to the app's dashboard. Note the Client ID and Client Secret – these will be used for authentication.

App Permissions Settings

To allow your app to post on behalf of the page, you need to configure specific permissions for the app.

  • Step 1: In the app’s dashboard, go to the Permissions section.

  • Step 2: Add the following permissions:
    ○ openid – to read the basic profile of the authenticated user.
    ○ profile – to read the authenticated user's email.
    ○ w_member_social – to allow posting on LinkedIn on behalf of the user.
    ○ rw_organization – to allow posting on behalf of a LinkedIn page (organization).
    Note: Make sure you get admin access for the LinkedIn page you want to post on.

Authentication (OAuth 2.0)

Setup The next step is to authenticate the user and get the Access Token required to interact with LinkedIn APIs.

<?php
$client_id = 'YOUR_CLIENT_ID';
$client_secret = 'YOUR_CLIENT_SECRET';
$redirect_uri = 'https://your_domain.com/callback.php';
$state = uniqid();
$scope = 'openid profile w_member_social rw_organization_admin r_organization_admin'; // Permissions

$auth_url = "https://www.linkedin.com/oauth/v2/authorization?response_type=code&client_id=$client_id&redirect_uri=$redirect_uri&state=$state&scope=$scope";
header("Location: $auth_url");
exit;

if (isset($_GET['code'])) {
    $code = $_GET['code'];
    $token_url = 'https://www.linkedin.com/oauth/v2/accessToken';

    $data = [
        'grant_type' => 'authorization_code',
        'code' => $code,
        'redirect_uri' => $redirect_uri,
        'client_id' => $client_id,
        'client_secret' => $client_secret,
    ];

    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $token_url);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

    $response = curl_exec($ch);
    curl_close($ch);

    $response = json_decode($response, true);
    $access_token = $response['access_token'];

    // Save the access token
    file_put_contents('access_token.txt', $access_token);
    header("Location: post_form.php");
}
?>
  • Step 2: Direct the user to the above URL. After successful login, the user will be redirected to your redirect URI with an authorization code.

  • Step 3: Exchange the authorization code for an access token by calling the following URL: https://www.linkedin.com/oauth/v2/accessToken

<?php
session_start();

$client_id = 'YOUR_CLIENT_ID';
$client_secret = 'YOUR_CLIENT_SECRET';
$redirect_uri = 'https://your_domain.com/callback.php'; // Replace with your redirect URL

if (isset($_GET['code'])) {
    $code = $_GET['code'];

    // Exchange authorization code for access token
    $token_url = "https://www.linkedin.com/oauth/v2/accessToken";
    $data = [
        'grant_type' => 'authorization_code',
        'code' => $code,
        'redirect_uri' => $redirect_uri,
        'client_id' => $client_id,
        'client_secret' => $client_secret,
    ];

    $response = postRequest($token_url, $data);
    $response_data = json_decode($response, true);

    if (isset($response_data['access_token'])) {
        $access_token = $response_data['access_token'];

        // Save the access token
         $_SESSION['access_token'] = $response_data['access_token'];
        file_put_contents('access_token.txt', $access_token);
        header("Location: post_form.php");

        exit();
    } else {
        echo "Error retrieving access token.";
    }
} else {
    echo "Authorization failed.";
}

function postRequest($url, $data) {
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_POST, 1);
    curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

    $response = curl_exec($ch);
    curl_close($ch);

    return $response;
}
?>
  • Step 4: Once you receive the access token in the response, store it securely. This token will be used to authenticate subsequent API requests.
    Here is the code snippet,

Get the LinkedIn Page ID (Organization ID)

To post on behalf of a LinkedIn page, you need the organization ID (Page ID). Open the organization page, you can find the page id in your organization page URL. Store that ID. linkedin.com/company/page_id/admin/dashboard

We can get the page ID using the API as well,

<?php
session_start();

$client_id = 'YOUR_CLIENT_ID';
$client_secret = 'YOUR_CLIENT_SECRET';
$redirect_uri = 'https://your_domain.com/callback.php'; // Replace with your redirect URL

$api_url = 'https://api.linkedin.com/v2/organizationalEntityAcls?q=roleAssignee';

    $headers = [
        "Authorization: Bearer $access_token",
        "X-Restli-Protocol-Version: 2.0.0"
    ];

    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $api_url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);

    $response = curl_exec($ch);
    curl_close($ch);

    $data = json_decode($response, true);

    if (isset($data['elements'])) {
        foreach ($data['elements'] as $element) {
            if (isset($element['organizationalTarget'])) {
                $organization_urn = $element['organizationalTarget'];
                // Extract the organization ID from the URN
                $organization_id = str_replace('urn:li:organization:', '', $organization_urn);
                echo "Organization ID: " . $organization_id . "\n";
                header("Location: post_form.php");
            }
        }
    } else {
        echo "My access token is --> ".$access_token;

        echo "No administered organizations found or an error occurred.";
    }

?>

Post on the LinkedIn Page

Now you can use the access token to post content to the LinkedIn page.

  • Step 1: Craft the post content (example: text, image, etc.).
<?php
session_start();

if (!isset($_SESSION['access_token'])) {
    header("Location: index.php");
    exit();
}

$access_token = $_SESSION['access_token'];
$organizationID = $_SESSION['organization_id'];

// Fetch LinkedIn Profile Data
$profile_url = "https://api.linkedin.com/v2/userinfo";
$headers = ["Authorization: Bearer $access_token"];
$profile_response = getRequest($profile_url, $headers);
$profile_data = json_decode($profile_response, true);

//echo "<pre>";print_r($profile_data);
//exit();

$name = $profile_data['name'];
//$person_urn = $profile_data['id']; // LinkedIn Person URN
$person_urn = $profile_data['sub']; // LinkedIn Person URN
$picture = $profile_data['picture']; //Profile Picture
// Fetch Profile Picture
$picture_url = "https://api.linkedin.com/v2/me?projection=(profilePicture(displayImage~:playableStreams))";
$picture_response = getRequest($picture_url, $headers);
$picture_data = json_decode($picture_response, true);

$profile_picture = $picture_data['profilePicture']['displayImage~']['elements'][0]['identifiers'][0]['identifier'];

// Function to make GET requests
function getRequest($url, $headers) {
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);

    $response = curl_exec($ch);
    curl_close($ch);

    return $response;
}


?>

<!DOCTYPE html>

<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Post to LinkedIn</title>
</head>
<body>
    <form action="submit_post.php" method="POST" enctype="multipart/form-data">
        <label for="message">Post Message:</label><br>
        <textarea name="message" id="message" rows="4" cols="50"></textarea><br><br>
        <label for="image">Upload Image:</label><br>
        <input type="file" name="image" id="image"><br><br>

        <input type="hidden" name="person_urn" value="<?= htmlspecialchars($person_urn) ?>">
        <input type="hidden" name="name" value="<?= htmlspecialchars($name) ?>">
        <input type="hidden" name="profile_picture" value="<?= htmlspecialchars($picture) ?>">
        <input type="hidden" name="organizationID" value="<?= htmlspecialchars($organizationID) ?>">

        <input type="submit" value="Post to LinkedIn">
    </form>
</body>
</html>
<?php
$access_token = file_get_contents('access_token.txt');

// Check if the form is submitted
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $message = $_POST['message'];
     // Retrieve form data
    $person_urn = $_POST['person_urn'];
    $name = $_POST['name'];
    $profile_picture = $_POST['profile_picture'];
    $page_id = $_POST['organizationID'];
    $base64_image_string = "";

    //$image = $_FILES['image'];
    if (isset($_FILES['image']) && $_FILES['image']['error'] === UPLOAD_ERR_OK) {
        $image_tmp_path = $_FILES['image']['tmp_name'];

        // Convert the image to Base64
        $base64_image_string = convertImageToBase64($image_tmp_path);
    } else {
        echo "No image uploaded or an error occurred.";
    }


   // Step 1: Upload the image to LinkedIn's server
    $upload_url = "https://api.linkedin.com/v2/assets?action=registerUpload";
    $upload_headers = [
        "Authorization: Bearer $access_token",
        "Content-Type: application/json",
    ];

    $upload_data = [
        "registerUploadRequest" => [
            "owner" => "urn:li:organization:$page_id",
            "recipes" => ["urn:li:digitalmediaRecipe:feedshare-image"],
            "serviceRelationships" => [
                [
                    "identifier" => "urn:li:userGeneratedContent",
                    "relationshipType" => "OWNER",
                ],
            ],
            "supportedUploadMechanism" => ["SYNCHRONOUS_UPLOAD"],
        ],
    ];

    $upload_response = sendCurlRequest($upload_url, json_encode($upload_data), $upload_headers);
    $upload_response = json_decode($upload_response, true);

    if (empty($upload_response["value"]["uploadMechanism"]["com.linkedin.digitalmedia.uploading.MediaUploadHttpRequest"]["uploadUrl"])) {
        echo json_encode(["success" => false, "message" => "Failed to register upload."]);
        exit();
    }

    // Upload the image binary
    $image_upload_url = $upload_response["value"]["uploadMechanism"]["com.linkedin.digitalmedia.uploading.MediaUploadHttpRequest"]["uploadUrl"];
    $image_headers = [
        "Authorization: Bearer $access_token",
        "Content-Type: application/octet-stream",
    ];

    sendCurlRequest($image_upload_url, $base64_image_string, $image_headers, true);

    // Get the asset value (LinkedIn's asset URL)
    $asset = $upload_response["value"]["asset"];

    // Step 2: Create the LinkedIn post
    $post_url = "https://api.linkedin.com/v2/ugcPosts";
    $post_headers = [
        "Authorization: Bearer $access_token",
        "Content-Type: application/json",
    ];

    $post_data = [
        //"author" => "urn:li:person:$person_urn",
        "author" => "urn:li:organization:$page_id",
        "lifecycleState" => "PUBLISHED",
        "specificContent" => [
            "com.linkedin.ugc.ShareContent" => [
                "shareCommentary" => [
                    "text" => $post_text,
                ],
                "shareMediaCategory" => "IMAGE",
                "media" => [
                    [
                        "status" => "READY",
                        "description" => [
                            "text" => $post_text,
                        ],
                        "media" => $asset,
                        "title" => [
                            "text" => "Shared via My App",
                        ],
                    ],
                ],
            ],
        ],
        "visibility" => [
            "com.linkedin.ugc.MemberNetworkVisibility" => "PUBLIC",
        ],
    ];

    $post_response = sendCurlRequest($post_url, json_encode($post_data), $post_headers);
    $post_response = json_decode($post_response, true);


    if (isset($post_response["id"])) {
        echo json_encode(["success" => true, "message" => "Post shared successfully!" , "postid"=>$post_response["id"]]);
    } else {
        echo json_encode(["success" => false, "message" => "Failed to share the post."]);
    }


}

function sendCurlRequest($url, $data, $headers, $is_post = true)
{
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    if ($is_post) {
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
    }
    curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);

    $response = curl_exec($ch);
    curl_close($ch);

    return $response;
}

function convertImageToBase64($file_path) {
    // Check if the file exists
    if (file_exists($file_path)) {
        // Get the file content
        $image_data = file_get_contents($file_path);
        // Encode the file content to Base64
        $base64_image = base64_encode($image_data);

        // Get the file type (e.g., image/png)
        $file_info = finfo_open(FILEINFO_MIME_TYPE);
        $file_type = finfo_file($file_info, $file_path);
        finfo_close($file_info);

        // Combine the file type and Base64 string in the correct format
        $base64_image_string = 'data:' . $file_type . ';base64,' . $base64_image;

        return $base64_image_string;
    } else {
        return 'File not found.';
    }
}


?>
  • Step 3: If the request is successful, you will receive a response confirming that the post was shared.

Error Handling and Validation

Ensure you handle any potential errors in the API request, such as:

  • Invalid access token

  • Missing permissions

  • Invalid organization ID

  • Invalid or missing content in the post Handle the errors and log the details for debugging.

Final Checklist

  • Ensure your app has the correct permissions (w_member_social, rw_organization).

  • Make sure the access token is valid and has not expired.

  • Use the correct organization (LinkedIn page) ID for posting.

  • Double-check the content format before making the POST request.