Engineering

LinkedIn Mentions API: Tag Companies and People in a Post

A LinkedIn mention is a URN plus the entity's exact registered name, and LinkedIn gives you no general search for either one. The two lookups that do exist, their very different reach, and why a wrong company name takes the whole post down while a wrong person's name just goes quiet.

Mentioning someone on LinkedIn through the API is not a text feature. You cannot type @Acme into the post body and have LinkedIn work out who you meant. A mention is a pair — an opaque URN and the exact display name that URN resolves to — and the whole difficulty of the job is getting hold of the URN in the first place.

LinkedIn gives you no general search for this. There is no endpoint that turns "Acme Corp" into urn:li:organization:1337, and there is no endpoint at all that turns a person's name into their URN. What exists are two narrow lookups with very different reach: organizations are resolvable by anyone, and people are resolvable only if they follow your page.

This post covers both, the exact shape of linkedin.mentions, and the ways a mention fails — which are not the same for a company as for a person.

If you want the shorter version, the LinkedIn API page answers "can I @mention a company via API?" directly and is the place to start. What follows is the long form: the identifier problem, the lookups, and the failure modes you have to write code against.

The shape of a mention

Mentions ride along with the post when you create it, in a linkedin.mentions array:

json
1"linkedin": {
2  "mentions": [
3    { "urn": "urn:li:person:133", "text": "Annie Victor" },
4    { "urn": "urn:li:organization:1337", "text": "LinkedIn" }
5  ]
6}

The urn is matched against ^urn:li:(organization|person):.+ — organizations are urn:li:organization:<id>, members are urn:li:person:<id>. The text is where all the trouble lives, and three rules govern it.

The text must appear verbatim in the post content. The matching text in the body is rewritten into LinkedIn's annotation format, and it is the first occurrence that becomes the clickable mention. If your post says "great work by the Acme team" and your mention text is "Acme Corporation", there is nothing to rewrite, and the mention is skipped.

It is case-sensitive, and it must be the exact registered name. Not what you call them — what LinkedIn has on file. Outstand.so, not Outstand. This is why the lookup returns a name at all: you need LinkedIn's spelling, including its capitalisation and any suffix.

Organizations and people fail differently, and this is the thing to design around. Get an organization's name wrong and the post fails at publish time — it does not publish with a broken tag. Get a person's name wrong and it publishes fine, with the mention rendered as plain text, because member names are not checked at all. So a bad company mention is loud and costs you the post; a bad person mention is silent and costs you only the link.

If you set linkedin.content to publish different text on LinkedIn than on your other networks, note that mentions are resolved against that text, not the top-level content. Putting the registered name only in the shared body will skip every mention on the LinkedIn copy.

Finding an organization

This is the easy half, and it got easier on 31 October 2026 when we added a lookup that resolves a company, school or showcase page from whatever identifier you happen to have:

bash
1curl "https://api.outstand.so/v1/linkedin/accounts/ACCOUNT_ID/organizations/lookup?url=https://www.linkedin.com/company/linkedin/" \
2  -H "Authorization: Bearer YOUR_API_KEY"

The url parameter is more forgiving than its name suggests. It takes a linkedin.com company, school or showcase URL, a vanity name, an organization URN, or a bare numeric organization ID. All four resolve to the same thing:

json
1{
2  "success": true,
3  "data": {
4    "urn": "urn:li:organization:1337",
5    "name": "LinkedIn",
6    "vanityName": "linkedin",
7    "type": "NONE"
8  }
9}

Take urn and name straight into linkedin.mentions. type is LinkedIn's primaryOrganizationType and comes back as SCHOOL, BRAND or NONE.

The important property of this endpoint is its reach: it works for organizations you have no relationship with. You do not need an admin role on the company you are tagging, and it works from both personal profile and Company Page accounts. You can tag a customer, a partner, a conference or a competitor without either side having to do anything first.

Two caveats. It is an exact lookup, not a keyword search — you need a real identifier, not a guess at a name. And if you connected the account with a custom scope list, it needs rw_organization_admin or the lookup returns 403.

Finding a person

This is the hard half, and the limits are LinkedIn's rather than ours.

LinkedIn has no API to search all members by name. What it offers is a typeahead over the followers of a Company Page, which we exposed on 10 October 2026:

bash
1curl "https://api.outstand.so/v1/linkedin/accounts/ACCOUNT_ID/followers/search?keywords=Annie&count=10" \
2  -H "Authorization: Bearer YOUR_API_KEY"

Each result carries the member's urn and full name, and start and count page through them, up to 50 at a time.

Read the constraints before you design a feature on this:

  • Company Pages only. A personal profile account gets a 400. There is no personal-account path to mentioning a person.
  • Followers only. LinkedIn returns members who follow the page and nobody else. Someone who does not follow you is not findable, full stop — this is a follower directory, not a people search.
  • Name search only. You search by name or part of a name. You cannot hand it a profile URL.
  • It needs r_organization_followers. The scope is requested by default now, but pages connected before it was added return 403 until they are reconnected. If follower search works on your new test page and 403s on the page you have had connected for months, that is why. With bring-your-own-key, LinkedIn must also have approved r_organization_followers for your own app.

The practical consequence: mentioning companies is a general capability, and mentioning people is an audience-engagement capability. Tagging the customer you just signed will usually work. Tagging a journalist who has never followed you will not.

URNs are not portable

A URN is only valid with the LinkedIn application that issued it. URNs copied out of another tool, another vendor's API, or a colleague's script will not resolve against your app. If you are migrating from another provider, you cannot bring a mentions table with you — re-resolve every one.

This is also why caching is worth doing properly. Organization URNs are stable for a given app, so resolving a company once and storing the urn and name against your own customer record is the right pattern. It saves a call per post and it keeps the exact registered name, which is the part people get wrong.

How mentions fail

Cause

What happens

text missing from the post body

The mention is skipped; the post publishes without it

Organization text is not the exact, case-sensitive registered name

The post fails at publish time

Member text is wrong

Publishes, rendered as plain text — member names are not checked

URN came from a different LinkedIn app

Does not resolve against your app

linkedin.content set, name only in the shared body

Mentions resolve against linkedin.content, so they are skipped

The asymmetry in rows two and three is the practical point. Company mentions are validated, so a stale cached name does not quietly degrade — it takes the whole post down. And the check happens only at the moment you publish: there is no dry-run, no validation call, nothing you can hit in advance to find out whether a mention will land.

That makes the scheduling case the dangerous one. A post composed today against a company name, scheduled for next month, is validated next month. If the company rebrands in between, the publish fails. Resolve immediately before composing where you can, and treat a stored name as a cache that can go stale rather than a fact.

Putting it together

A complete flow for tagging a company in a scheduled post is two calls:

bash
1# 1. Resolve the organization
2curl "https://api.outstand.so/v1/linkedin/accounts/ACCOUNT_ID/organizations/lookup?url=acme-corp" \
3  -H "Authorization: Bearer YOUR_API_KEY"
4
5# 2. Publish, with the returned name appearing in the body
6curl -X POST https://api.outstand.so/v1/posts \
7  -H "Authorization: Bearer YOUR_API_KEY" \
8  -H "Content-Type: application/json" \
9  -d '{
10    "accounts": ["Kx7vQ"],
11    "content": "Delighted to be working with ACME Holdings Ltd on this.",
12    "linkedin": {
13      "mentions": [
14        { "urn": "urn:li:organization:1337", "text": "ACME Holdings Ltd" }
15      ]
16    }
17  }'

Two things to notice. The body text contains the registered name verbatim — that is the bit to get right. And accounts takes account identifiers from GET /v1/social-accounts, not network names: "linkedin" is not an identifier and will not resolve. Account ids are opaque, so pass back exactly what the API returned rather than constructing one.

One more trap in that array: the request is rejected with 400 only when none of the identifiers resolve. If one resolves and two do not, you get a 200 and a post that went to one account. Compare the returned socialAccounts against what you sent.

Where to go next

The LinkedIn configuration guide has the scopes, the connection flow and the mention rules in one place, and the two lookups have their own reference pages for organizations and followers.

If you are building the posting side as well, the LinkedIn Posts API guide covers author URNs, images, video and Company Page publishing, and LinkedIn API restrictions is the honest list of what LinkedIn will not let you do at all. Pricing is in LinkedIn API pricing, and the LinkedIn API page covers what Outstand handles for you.