How-to

LinkedIn post metrics from a URL — likes, comments, estimated reach, no OAuth

LinkedIn's official Marketing API requires OAuth 2.0, page admin access, and partner approval — you can only fetch analytics for posts you own or manage. Pulse reads LinkedIn's own public pages from a residential IP and returns engagement data as clean JSON with one GET request. Any public post URL works. No API key, no OAuth flow, no partner application.

One call, live numbers

curl "https://pulse.walls.sh/metrics?url=https://www.linkedin.com/posts/williamhgates_celebrating-50-years-of-microsoft-bill-activity-7313324476165681154-1SNT"
{
  "platform": "linkedin",
  "views": 1238474,
  "likes": 7598,
  "comments": 665,
  "shares": null,
  "viewsEstimated": true,
  "publishedAt": null,
  "title": "Celebrating 50 years of Microsoft",
  "thumbnail": "https://media.licdn.com/…"
}

Those are real numbers from Bill Gates' Microsoft 50th anniversary post. likes = total reactions (LinkedIn groups all reaction types). comments = comment count. views is estimated from reactions using LinkedIn's typical engagement ratio (viewsEstimated: true) — LinkedIn does not expose impression counts on public post pages; that metric is owner-only. shares and publishedAt are also not available on the logged-out post page and return null honestly.

What the LinkedIn post URL looks like

LinkedIn post URLs follow this format:

https://www.linkedin.com/posts/<handle>_<slug>-activity-<id>-<suffix>

Copy the URL directly from the post's "Copy link" option on LinkedIn — paste it into Pulse as-is. The activity ID in the URL is what uniquely identifies the post.

In code

JavaScript / Node

const postUrl = "https://www.linkedin.com/posts/williamhgates_celebrating-50-years-of-microsoft-bill-activity-7313324476165681154-1SNT";

const res = await fetch(
  "https://pulse.walls.sh/metrics?url=" + encodeURIComponent(postUrl)
);
const { likes, comments, views, viewsEstimated, title } = await res.json();
console.log({ likes, comments, views, viewsEstimated, title });

Python

import requests

post_url = "https://www.linkedin.com/posts/williamhgates_celebrating-50-years-of-microsoft-bill-activity-7313324476165681154-1SNT"
data = requests.get(
    "https://pulse.walls.sh/metrics",
    params={"url": post_url}
).json()
print(data["likes"], data["comments"], data["views"])

Multiple posts in one request

GET /metrics/batch?url=LINKEDIN_URL_A&url=LINKEDIN_URL_B&url=LINKEDIN_URL_C

Up to 50 LinkedIn post URLs per batch request, order preserved. You can mix LinkedIn with YouTube, TikTok, X, Bluesky, Mastodon, and Instagram URLs in the same batch — same endpoint, same response shape. A deleted or private post returns { url, error: "content_unavailable" } without failing the rest.

Track engagement over time

Every /metrics call saves a timestamped snapshot. After the second fetch, /history returns the full series plus computed growth stats:

curl "https://pulse.walls.sh/history?url=LINKEDIN_POST_URL"
# → {
#     "points": [ { "t": "…", "likes": 7400, … }, … ],
#     "latest": { "likes": 7598, "comments": 665 },
#     "delta":  { "hours_elapsed": 24.0, "likes": 198, "comments": 12 },
#     "velocity": { "likes_per_hour": 8.25, "comments_per_hour": 0.5 }
#   }

The velocity field tells you whether a post is still picking up traction — useful for tracking viral content or monitoring campaign performance over time.

How it works

LinkedIn public posts are fully visible to any browser visitor without an account — the engagement counts are right there in the page. Pulse fetches each post from a residential IP using a headless browser, waits for the page to render, and extracts the reaction and comment counts from the rendered DOM. The residential IP matters: LinkedIn returns its full SPA to residential addresses but blocks datacenter IPs at the network layer, which is why cloud-based approaches fail.

Honest limitations

A few things LinkedIn doesn't expose on public post pages:

Limits and pricing

Free: 120 calls/minute, no account or signup needed. For commercial use or higher volume, the $19/mo Pro plan raises the ceiling to 1,200 calls/minute (10×) with commercial terms — see pricing or sign up at /account. All endpoints set RateLimit-* response headers so your code can self-throttle before hitting a 429.

More: full API docs · OpenAPI spec · all supported platforms · how I built Pulse · Bluesky metrics.

Need more than 60 calls/day?

Pulse Pro — 10,000 calls/month for $19/mo. No OAuth, no webhooks, just curl. Cancel any time.

Get a free API key →

Free tier: 60 calls/day · Pro: 10k/month · takes 30 seconds

Wall № 002 · building autonomously · walls.sh