Chrome extension

How profile, company, post, group, search, and outbound endpoints reach LinkedIn via the user's own session.

LinkFetch's extension-backed endpoints don't run on a fake LinkedIn account on our infrastructure. They run inside the end user's own signed-in LinkedIn tab through a Chrome extension called LinkFetch. The extension captures the data the user has already requested, sends it to our API, and from then on the row hits the cache like any other.

This is the only way to get LinkedIn data with a defensible compliance posture in 2026 — see Compliance for the legal reasoning.

When you need the extension

SurfaceNeeds extension?
/v1/profiles/*Yes (cache-first, miss → capture)
/v1/companies/*Yes (cache-first, miss → capture)
/v1/posts/*Yes
/v1/groups/*Yes
/v1/search/*Yes
/v1/outbound/*Yes — every action runs from the user's session
/v1/jobs/*No — our scraped dataset, no signed-in session
/v1/locations/*No

Install

Add LinkFetch to Chrome and pin the icon for visibility. The extension manifest requests:

  • linkedin.com content-script access (so it can capture and act)
  • storage (so your API key persists for the message bridge)
  • tabs (so it can route capture requests to a LinkedIn tab)

It does not request cookies or webRequest host permissions — your LinkedIn session cookie never leaves the browser.

How a cache-miss capture works

  1. Your code calls GET /v1/profiles?url=….
  2. LinkFetch returns 422 extension_required with a capture_hint.
  3. The page (or your in-app integration) postMessages the hint to the extension's content script.
  4. The extension fetches the underlying voyager endpoint inside the user's signed-in LinkedIn tab, normalises it, and POSTs it to /v1/profiles/ingest with your API key.
  5. Your code re-runs the original GET — it now hits the cache and returns the typed row with full provenance.

The /playground does this automatically when an endpoint declares a capture flow — Run cascades: GET → 422 → capture → GET → 200, all in one click.

How outbound actions work

For POST /v1/outbound/*, the API itself doesn't issue the action — it returns 422 extension_required and the actual call (connection request, DM, reaction, comment, follow, save) is dispatched by the extension on the user's session. The API still:

  • Validates the target (e.g. blocks DMs to non-1st-degree connections)
  • Debits credits when the action succeeds
  • Logs the result with provenance for audit

That separation is intentional — LinkFetch never sends actions on behalf of a user without a live extension round-trip from their own device.

Local MCP bridge

If you're driving LinkFetch from a desktop agent (Claude Desktop, Cursor) and want extension-backed endpoints to work end-to-end, the extension exposes a local MCP server on 127.0.0.1:7878. Wire it like:

{
  "mcpServers": {
    "linkfetch-local": {
      "url": "http://127.0.0.1:7878",
      "transport": "http"
    }
  }
}

This lets the agent ask the extension to capture from the user's session without any web UI in the loop. See MCP.

Diagnostics

The extension injects two attributes on <html> when it boots:

  • data-linkfetch-extension="1" — install detection
  • data-linkfetch-build="<iso>-<commit>" — build identifier

The /playground reads both to render an "Extension connected" pill with the build SHA. If you're embedding LinkFetch in your own app, the same pattern works — read the attribute, fall back to an install card.

Rate limits, the extension's own

The extension throttles outbound actions locally so a hot loop in your code can't burn through a user's daily LinkedIn cap. When LinkedIn serves a security challenge, the extension automatically pauses outbound for 24 hours and surfaces a banner — your API call gets 429 outbound_throttled with next_available_at until the pause clears.

Privacy

  • The user's LinkedIn session cookie never leaves their browser.
  • Captures are normalised to the same typed shape as our scraped data before they hit our servers.
  • Suppression and DSR removal apply to extension-captured rows the same way they apply to scraper rows. See Compliance.