Pulse started as a one-weekend experiment in late 2025: given a public social post URL, return its engagement numbers as clean JSON. One GET request, no auth, no platform accounts. It turned into 7 platforms, a residential-IP moat, a Playwright headless fetcher for LinkedIn, an MCP server, and a $19/mo Pro tier. This is the honest technical story.
AI agents are increasingly tasked with things like "analyze which posts from this creator are getting traction" or "track how our campaign is performing across platforms." Every platform has a different answer to that request:
The engagement numbers those APIs guard are already on each platform's public post page — visible to any browser visitor without an account. The APIs exist to control who builds products, not to protect private data. That's the gap Pulse fills.
The first version ran on Railway. It worked great in testing and died in production within hours. Every platform — without exception — has bot-detection that reacts to datacenter IP ranges. The same request from a residential address that renders a full post page returns an empty SPA shell, a CAPTCHA, or a hard block from a GCP or AWS subnet.
The fix was obvious once I saw it: run Pulse on a Mac
at home, behind a residential ISP, served to the internet via a Cloudflare quick tunnel that
publishes its rotating origin URL into a Cloudflare Worker via KV. The worker at
pulse.walls.sh routes to whichever tunnel URL is current. The LaunchAgent
keeps the Node.js process alive. The residential IP is the moat — reproducing this in a
datacenter requires residential IP proxies that cost more per-call than Pulse charges.
Each platform required a different approach. The common shape:
GET /metrics?url=<post-url> → detect platform → fetch → parse → normalize.
Normalization means: { platform, views, likes, comments, shares, publishedAt } for
every platform, with null for genuinely unavailable fields (never fabricated zeros).
YouTube was the easiest. The video page
embeds a ytInitialData JSON blob in the HTML that includes view count, like count,
and publish date. One regex pull, no headless browser needed.
X / Twitter uses an internal GraphQL API (the same one their own SPA uses). The request requires a hard-coded bearer token and a guest token obtained via a prior call — both are public, baked into the SPA's source, and have been stable for years. The response is a deeply nested JSON object; parsing it takes more lines than fetching it.
TikTok embeds a __UNIVERSAL_DATA_FOR_REHYDRATION__
JSON blob in the video page. It's big (200KB+), but digging into it with a targeted path
gives exact view count, like count, comment count, and share count. The tricky part is
TikTok's aggressive bot detection on the fetch itself — browser headers, cookie handling,
and request sequencing all matter.
Bluesky was a pleasant surprise. The
AT Protocol's app.bsky.feed.getPostThread XRPC endpoint is public and doesn't
require auth. I resolve the post URL to a DID + record key, hit the endpoint, and get
back likes, reposts, replies, and quotes. Custom handle domains and DID handles both
work — the URL resolution handles both forms.
Mastodon is federated, which means
I can't hit one endpoint. A post URL like https://fosstodon.org/@username/1234567
tells me the instance; that instance runs the Mastodon REST API, so I hit
https://fosstodon.org/api/v1/statuses/1234567. Remote profiles in
@[email protected] form work too — a WebFinger lookup resolves the home instance,
then the profile endpoint returns follower/post counts.
Instagram served its post data in a JSON blob embedded in the page for a long time. That approach still works from a residential IP with the right headers and session handling — it's currently in beta while I tune the reliability.
LinkedIn was the hardest. LinkedIn's
post pages are a React SPA that requires JavaScript execution to render the engagement counts.
The numbers are not in the initial HTML — they load via internal API calls that fire after
hydration. The solution: Playwright headless
browser, singleton Chromium instance, residential IP, and extraction from rendered
aria-label attributes ("7,598 reactions", "665 comments").
Views are owner-only on LinkedIn — the logged-out page never shows impression counts — so Pulse
estimates them from the reactions count using LinkedIn's typical engagement ratio
(viewsEstimated: true).
The whole API is designed for agents calling it, not humans reading documentation:
/metrics?url=… directly without credentials./llms.txt — machine-readable docs in the format LLMs are trained on./openapi.json — so any agent with a tool-use loop can discover the endpoints automatically.pulse-mcp on npm wraps the API in five Claude/Cursor tools; one line installs it.Every /metrics call writes a timestamped snapshot to a local SQLite
database keyed by post URL. After two or more fetches, /history?url=… returns the
full time series plus computed deltas and velocity:
{
"points": [
{ "t": "2026-06-10T14:00:00Z", "views": 1180000, "likes": 7400, "comments": 640 },
{ "t": "2026-06-11T10:00:00Z", "views": 1238474, "likes": 7598, "comments": 665 }
],
"latest": { "views": 1238474, "likes": 7598, "comments": 665 },
"delta": { "hours_elapsed": 20.0, "views": 58474, "likes": 198, "comments": 25 },
"velocity": { "views_per_hour": 2923.7, "likes_per_hour": 9.9, "comments_per_hour": 1.25 }
}
No cron job, no separate write pipeline. Just call
/metrics repeatedly and /history builds the series automatically.
An agent can poll a post during a campaign launch and get real-time velocity without any
infrastructure beyond the Pulse calls themselves.
null honestly for unavailable fields rather than fabricating zeros — agents can reason about missing data/history endpoint emerging from the caching layer — snapshots were already being written; the time series was a one-day additionLAST_HOST dedup in the tunnel script.7 platforms live: YouTube, X/Twitter, TikTok, Bluesky, Mastodon, LinkedIn, Instagram (beta).
Free at 120 calls/min. Pro is $19/mo for commercial use and 1,200 calls/min.
The MCP server (pulse-mcp) is in the official MCP registry. The API has been up continuously
since launch from this Mac in Brooklyn, NY.
Try it:
curl "https://pulse.walls.sh/metrics?url=https://www.youtube.com/watch?v=dQw4w9WgXcQ"
More: full API docs · LinkedIn metrics deep-dive · all supported platforms · Pro plan.
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