LinkedIn Analytics
Get engagement metrics (impressions, reactions, comments, shares) for your LinkedIn posts using the PostPost API.
Overview
PostPost provides analytics for LinkedIn posts through a single endpoint. You can retrieve individual metrics or all metrics at once.
API Reference
Endpoint:
POST https://api.postpost.dev/api/v1/linkedin-post-statisticsHeaders:
Content-Type: application/json
x-api-key: YOUR_API_KEYRequest Body:
{
"postedId": "urn:li:share:7434685316856377344",
"platformId": "linkedin-ABC123",
"queryTypes": ["ALL"]
}Available Metrics
| Metric | Description |
|---|---|
IMPRESSION | Number of times the post was displayed |
MEMBERS_REACHED | Unique LinkedIn members who saw the post |
REACTION | Total reactions (likes, celebrates, etc.) |
COMMENT | Number of comments on the post |
RESHARE | Number of times the post was shared |
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
postedId | string | Yes | LinkedIn post URN (from get-post endpoint) |
platformId | string | Yes | Your LinkedIn platform ID (e.g., linkedin-ABC123) |
queryType | string | No | Single metric: IMPRESSION, REACTION, etc. |
queryTypes | array | No | Multiple metrics: ["IMPRESSION", "REACTION"] or ["ALL"] |
Use either queryType (single) or queryTypes (multiple), not both.
Response Format
Single metric response:
{
"success": true,
"count": 4521,
"cached": false
}Multiple metrics response:
{
"success": true,
"metrics": {
"IMPRESSION": 4521,
"MEMBERS_REACHED": 3200,
"REACTION": 89,
"COMMENT": 15,
"RESHARE": 12
},
"cached": false,
"cachedMetrics": []
}Complete JavaScript Implementation
const PUBLORA_API_KEY = process.env.PUBLORA_API_KEY;
const BASE_URL = 'https://api.postpost.dev/api/v1';
/**
* Retrieve LinkedIn post analytics
* @param {string} postedId - LinkedIn post URN (urn:li:share:xxx or urn:li:ugcPost:xxx)
* @param {string} platformId - LinkedIn platform ID (linkedin-xxx)
* @param {string|string[]} metrics - Single metric, array of metrics, or 'ALL'
* @returns {Promise<Object>} Analytics data
*/
async function getLinkedInAnalytics(postedId, platformId, metrics = 'ALL') {
// Validate inputs
if (!postedId) {
throw new Error('postedId is required');
}
if (!postedId.startsWith('urn:li:')) {
throw new Error('postedId must be a LinkedIn URN (urn:li:share:xxx or urn:li:ugcPost:xxx)');
}
if (!platformId) {
throw new Error('platformId is required');
}
// Build request body
const body = {
postedId,
platformId
};
// Handle metrics parameter
if (typeof metrics === 'string') {
if (metrics === 'ALL') {
body.queryTypes = ['ALL'];
} else {
body.queryType = metrics;
}
} else if (Array.isArray(metrics)) {
body.queryTypes = metrics;
}
// Make API request
const response = await fetch(`${BASE_URL}/linkedin-post-statistics`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': PUBLORA_API_KEY
},
body: JSON.stringify(body)
});
const data = await response.json();
// Handle errors
if (!response.ok) {
const error = new Error(data.error || `HTTP ${response.status}`);
error.status = response.status;
error.response = data;
switch (response.status) {
case 400:
error.message = `Invalid request: ${data.error}`;
break;
case 401:
error.message = 'Invalid API key. Check your x-api-key header.';
break;
case 404:
error.message = 'LinkedIn connection not found. Verify platformId.';
break;
case 500:
error.message = 'Failed to fetch analytics. The post may not exist or analytics are not yet available.';
break;
}
throw error;
}
return data;
}
// ============ USAGE EXAMPLES ============
// Example 1: Get all metrics
async function getAllMetrics(postedId, platformId) {
try {
const analytics = await getLinkedInAnalytics(postedId, platformId, 'ALL');
console.log('Impressions:', analytics.metrics.IMPRESSION);
console.log('Reactions:', analytics.metrics.REACTION);
console.log('Comments:', analytics.metrics.COMMENT);
console.log('Shares:', analytics.metrics.RESHARE);
console.log('Reach:', analytics.metrics.MEMBERS_REACHED);
return analytics;
} catch (error) {
console.error('Failed to get analytics:', error.message);
throw error;
}
}
// Example 2: Get specific metrics only
async function getEngagementMetrics(postedId, platformId) {
try {
const analytics = await getLinkedInAnalytics(
postedId,
platformId,
['REACTION', 'COMMENT', 'RESHARE']
);
return analytics.metrics;
} catch (error) {
console.error('Failed to get engagement:', error.message);
throw error;
}
}
// Example 3: Get single metric
async function getImpressions(postedId, platformId) {
try {
const analytics = await getLinkedInAnalytics(postedId, platformId, 'IMPRESSION');
return analytics.count;
} catch (error) {
console.error('Failed to get impressions:', error.message);
throw error;
}
}
// Example 4: Get analytics for a post created via PostPost
async function getAnalyticsForPost(postGroupId, platformId) {
// Step 1: Get the postedId from the post
const postResponse = await fetch(`${BASE_URL}/get-post/${postGroupId}`, {
headers: { 'x-api-key': PUBLORA_API_KEY }
});
const postData = await postResponse.json();
const linkedInPost = postData.posts.find(p => p.platform === 'linkedin');
if (!linkedInPost || !linkedInPost.postedId) {
throw new Error('LinkedIn post not found or not yet published');
}
// Step 2: Get analytics
return await getLinkedInAnalytics(linkedInPost.postedId, platformId, 'ALL');
}
// Run examples
const postedId = 'urn:li:share:7434685316856377344';
const platformId = 'linkedin-ABC123';
const allMetrics = await getAllMetrics(postedId, platformId);
const engagement = await getEngagementMetrics(postedId, platformId);
const impressions = await getImpressions(postedId, platformId);Complete Python Implementation
import os
import requests
from typing import Union, List, Dict, Optional
PUBLORA_API_KEY = os.environ.get('PUBLORA_API_KEY')
BASE_URL = 'https://api.postpost.dev/api/v1'
class PostPostAnalyticsError(Exception):
"""Custom exception for analytics API errors."""
def __init__(self, message: str, status_code: int = None, response: dict = None):
super().__init__(message)
self.status_code = status_code
self.response = response
def get_linkedin_analytics(
posted_id: str,
platform_id: str,
metrics: Union[str, List[str]] = 'ALL'
) -> Dict:
"""
Retrieve LinkedIn post analytics.
Args:
posted_id: LinkedIn post URN (urn:li:share:xxx or urn:li:ugcPost:xxx)
platform_id: LinkedIn platform ID (linkedin-xxx)
metrics: Single metric string, list of metrics, or 'ALL'
Returns:
dict: Analytics data with metrics
Raises:
PostPostAnalyticsError: If the API request fails
ValueError: If inputs are invalid
"""
# Validate inputs
if not posted_id:
raise ValueError('posted_id is required')
if not posted_id.startswith('urn:li:'):
raise ValueError('posted_id must be a LinkedIn URN (urn:li:share:xxx or urn:li:ugcPost:xxx)')
if not platform_id:
raise ValueError('platform_id is required')
# Build request body
body = {
'postedId': posted_id,
'platformId': platform_id
}
# Handle metrics parameter
if isinstance(metrics, str):
if metrics == 'ALL':
body['queryTypes'] = ['ALL']
else:
body['queryType'] = metrics
elif isinstance(metrics, list):
body['queryTypes'] = metrics
# Make API request
response = requests.post(
f'{BASE_URL}/linkedin-post-statistics',
headers={
'Content-Type': 'application/json',
'x-api-key': PUBLORA_API_KEY
},
json=body
)
data = response.json()
# Handle errors
if not response.ok:
error_message = data.get('error', f'HTTP {response.status_code}')
if response.status_code == 400:
error_message = f'Invalid request: {error_message}'
elif response.status_code == 401:
error_message = 'Invalid API key. Check your x-api-key header.'
elif response.status_code == 404:
error_message = 'LinkedIn connection not found. Verify platform_id.'
elif response.status_code == 500:
error_message = 'Failed to fetch analytics. The post may not exist or analytics are not yet available.'
raise PostPostAnalyticsError(error_message, response.status_code, data)
return data
# ============ USAGE EXAMPLES ============
def get_all_metrics(posted_id: str, platform_id: str) -> Dict:
"""Get all available metrics for a post."""
try:
analytics = get_linkedin_analytics(posted_id, platform_id, 'ALL')
print(f"Impressions: {analytics['metrics']['IMPRESSION']}")
print(f"Reactions: {analytics['metrics']['REACTION']}")
print(f"Comments: {analytics['metrics']['COMMENT']}")
print(f"Shares: {analytics['metrics']['RESHARE']}")
print(f"Reach: {analytics['metrics']['MEMBERS_REACHED']}")
return analytics
except (PostPostAnalyticsError, ValueError) as e:
print(f"Failed to get analytics: {e}")
raise
def get_engagement_metrics(posted_id: str, platform_id: str) -> Dict:
"""Get engagement metrics only (reactions, comments, shares)."""
try:
analytics = get_linkedin_analytics(
posted_id,
platform_id,
['REACTION', 'COMMENT', 'RESHARE']
)
return analytics['metrics']
except (PostPostAnalyticsError, ValueError) as e:
print(f"Failed to get engagement: {e}")
raise
def get_impressions(posted_id: str, platform_id: str) -> int:
"""Get impression count for a post."""
try:
analytics = get_linkedin_analytics(posted_id, platform_id, 'IMPRESSION')
return analytics['count']
except (PostPostAnalyticsError, ValueError) as e:
print(f"Failed to get impressions: {e}")
raise
def get_analytics_for_post(post_group_id: str, platform_id: str) -> Dict:
"""Get analytics for a post created via PostPost."""
# Step 1: Get the posted_id from the post
post_response = requests.get(
f'{BASE_URL}/get-post/{post_group_id}',
headers={'x-api-key': PUBLORA_API_KEY}
)
post_data = post_response.json()
linkedin_post = next(
(p for p in post_data.get('posts', []) if p.get('platform') == 'linkedin'),
None
)
if not linkedin_post or not linkedin_post.get('postedId'):
raise ValueError('LinkedIn post not found or not yet published')
# Step 2: Get analytics
return get_linkedin_analytics(linkedin_post['postedId'], platform_id, 'ALL')
# Run examples
if __name__ == '__main__':
posted_id = 'urn:li:share:7434685316856377344'
platform_id = 'linkedin-ABC123'
all_metrics = get_all_metrics(posted_id, platform_id)
engagement = get_engagement_metrics(posted_id, platform_id)
impressions = get_impressions(posted_id, platform_id)cURL Examples
Get all metrics
curl -X POST "https://api.postpost.dev/api/v1/linkedin-post-statistics" \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"postedId": "urn:li:share:7434685316856377344",
"platformId": "linkedin-ABC123",
"queryTypes": ["ALL"]
}'Get specific metrics
curl -X POST "https://api.postpost.dev/api/v1/linkedin-post-statistics" \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"postedId": "urn:li:share:7434685316856377344",
"platformId": "linkedin-ABC123",
"queryTypes": ["IMPRESSION", "REACTION", "COMMENT"]
}'Get single metric
curl -X POST "https://api.postpost.dev/api/v1/linkedin-post-statistics" \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"postedId": "urn:li:share:7434685316856377344",
"platformId": "linkedin-ABC123",
"queryType": "IMPRESSION"
}'Important Notes
Analytics Delay
LinkedIn analytics may take up to 24 hours to fully populate. Querying immediately after posting will return partial or zero data.
Caching
PostPost caches analytics for 30 minutes to improve performance and reduce LinkedIn API calls. The cached field in the response indicates if data was served from cache.
Finding the postedId
For posts created via PostPost, get the postedId from the get-post endpoint:
const response = await fetch(`https://api.postpost.dev/api/v1/get-post/${postGroupId}`, {
headers: { 'x-api-key': 'YOUR_API_KEY' }
});
const data = await response.json();
const postedId = data.posts.find(p => p.platform === 'linkedin')?.postedId;Rate Limiting
- Analytics requests are subject to LinkedIn's rate limits
- PostPost's caching helps reduce rate limit issues
- For bulk analytics, add delays between requests
Error Reference
| Status | Error | Cause | Solution |
|---|---|---|---|
| 400 | "postedId and platformId are required" | Missing required fields | Include both fields |
| 400 | "Invalid queryType" | Unknown metric name | Use valid metric names |
| 401 | "Invalid API key" | Bad x-api-key | Check API key |
| 404 | "LinkedIn connection not found" | Wrong platformId | Verify platformId |
| 500 | "Failed to fetch LinkedIn post statistics" | LinkedIn API error | Post may not exist or no data yet |
Related Guides
- X Bookmarks - Sync, search, and export your X (Twitter) bookmarks
- LinkedIn Account Statistics - Account-level analytics
- LinkedIn Followers - Follower count and growth
- LinkedIn Profile Summary - Combined profile stats
- Get Post - Get postedId for analytics