Lesson 04 · outer fringe

Matched Audiences

Your customer's own data, turned into something targetable. Two entities, one pipeline, a state machine that expires behind your back — and a separate API program you have to apply for.

≈15 min · pinned to li-lms-2026-08


What it is

Every facet you met in Lesson 03 targets attributes LinkedIn knows: job title, employer, seniority. Matched Audiences is the mechanism for targeting attributes your customer knows: their CRM contacts, their target account list, the people who visited their pricing page.

The mechanism is identity resolution. You hand LinkedIn a list of email addresses or company names; LinkedIn matches them against member profiles and company pages; you get back an opaque audience you can target but never read. You cannot ask "who matched" — that is the privacy boundary, and it is absolute.

Read this before you plan the work

Matched Audiences is a separate API program from the Advertising API, with a separate application and separate vetting. Access to the Advertising API grants you nothing here. Advertising API access is a prerequisite for even applying. The scope is rw_dmp_segments, and LinkedIn describes access as restricted to approved developers "subject to applicable data restrictions in their agreements."

Practical consequence for your roadmap: this is a business-development dependency with an unknown lead time, not an engineering task you can schedule. Start the application before you need the feature.

Two entities, not one

This is the core of the vocabulary, and it is the thing you cannot guess.

EntityURNRoleYou…
DMP Segmenturn:li:dmpSegmentThe staging entity. Holds the raw input you push — users or companies.create it, write to it, delete it
Ad Segmenturn:li:adSegmentThe output. The matched, targetable audience.never create it — you only read its URN and target it

DMP is Data Management Platform. The asymmetry is the whole design: you own the input side, LinkedIn owns the output side. The Ad Segment is created for you when matching completes, and it arrives in an unexpected place — nested inside the DMP Segment's destinations array, under the key destinationSegmentId:

GET /rest/dmpSegments/11204
{
  "id": 11204,
  "name": "Test DMP Segment 2",
  "account": "urn:li:sponsoredAccount:516848833",
  "type": "USER",
  "sourcePlatform": "PARTNER_API",
  "destinations": [{
    "status": "BUILDING",
    "destinationSegmentId": "urn:li:adSegment:848336"   // ← the targetable thing
  }]
}

Note what this implies: the status you care about is not on the segment, it is on the destination. A "destination" is an ad exchange — and today the only accepted value is LINKEDIN. So the array always has one element and the nesting buys you nothing. It exists because the platform was designed to fan out to multiple exchanges. Write your accessor to handle the general case anyway; a hardcoded destinations[0] is the kind of shortcut that survives until it doesn't.

The three ways in

Same two entities, three different input mechanisms — distinguished by type and sourcePlatform on the DMP Segment.

MechanismtypesourcePlatformScope
CSV list upload
one-shot file of contacts or companies
USER_LIST_UPLOAD
COMPANY_LIST_UPLOAD
LIST_UPLOADrw_ads
Streaming
continuous add/remove via API
USER
COMPANY
DIRECT_API
AGENCY_API
PARTNER_API
rw_dmp_segments
Predictive Audiences
LinkedIn's AI extends a seed list
rw_dmp_segments

Two details in that table deserve attention.

First, CSV list upload uses rw_ads — the scope you already have. It sits inside the Matched Audiences docs but does not need the Matched Audiences scope. If you want first-party list targeting before your rw_dmp_segments application clears, this is the path.

Second, sourcePlatform is a declaration about what kind of business you are, and you must pick correctly:

ValueMeans
DIRECT_APIAn advertiser using matched audiences for its own ad accounts
AGENCY_APIAn agency managing audiences on behalf of multiple clients
PARTNER_APIA platform enabling many advertisers to sync audiences at scale — not an agency

For your integration

You described a multi-tenant product where many customers connect their own accounts. That is PARTNER_API — the "platform at scale, not an agency" case. It is also indexable alongside sourceSegmentId, an optional foreign key back to your ID for the segment. Set sourceSegmentId from day one: it is the only field that lets you find your own segments by your own identifier, and you cannot backfill what you never wrote.

Targeting it: the two-facet trap

Lesson 03 left you with a puzzle — two different facets with an identical value type:

urn:li:adTargetingFacet:audienceMatchingSegments   → urn:li:adSegment:10001
urn:li:adTargetingFacet:dynamicSegments           → urn:li:adSegment:10001

Here is the rule. The split is by provenance, not by type:

FacetHolds
audienceMatchingSegmentsContact lists, company lists — everything except retargeting
dynamicSegmentsWebsite retargeting and engagement retargeting segments only

Why this will bite you

An adSegment URN does not tell you which facet it belongs in. urn:li:adSegment:10001 is shaped identically whether it came from a CSV of email addresses or from the Insight Tag on a website. You must track provenance yourself — either from the dmpSegment.type you created it with, or by knowing it is a retargeting segment you did not create at all.

Your ad_segments table therefore needs a facet column that LinkedIn will never populate for you. Get that wrong and the campaign create fails, or worse, targets the wrong thing.

And the restrictions from Lesson 03 land squarely here: website retargeting segments may not be AND'ed with any clause targeting member behaviour or interests. So "people who visited our pricing page AND are interested in DevOps" is not expressible. Two facets, two different restriction profiles, one URN type.

The state machine

Segments are not static rows. They age, and they expire without you touching them.

stateDiagram-v2
  [*] --> BUILDING: create
  BUILDING --> READY: matched (up to 48h)
  BUILDING --> FAILED: list upload failed
  READY --> UPDATING: new list or streamed changes
  UPDATING --> READY: up to 24h
  READY --> ARCHIVED: 30 days unused
  BUILDING --> ARCHIVED: 30 days unused
  ARCHIVED --> BUILDING: added to an active campaign
  ARCHIVED --> EXPIRED: 90 days total unused
  EXPIRED --> BUILDING: new list upload only

Four things in there matter more than the diagram:

Limits and deletion

As of August 2026 a sponsored account is capped at 1,000 DMP segments, across Matched Audiences and Predictive Audiences combined. Exceeding it returns 429 with code SEGMENT_LIMIT_EXCEEDED.

This is recent and it is a real constraint for a platform product. If your design creates a segment per campaign, or per sync run, you will hit 1,000 on a large customer. Segments are a scarce, account-scoped resource — reuse them.

Deletion cascades, and it cascades further than you would expect:

DELETE /rest/dmpSegments/{id}
  → deletes the DMP segment
  → deletes its destinations
  → deletes the corresponding adSegment
  → deletes the uploaded list, if any

Deleting the staging entity destroys the targetable audience. But there is a guard: if the adSegment is in use by an active campaign, the delete fails with a 400. And you need write access to the owning sponsored account, or you get 403. So a "clean up old segments" job must handle both — and it is exactly the job you will need, given the 1,000 cap.

Predictive Audiences

Worth naming because it is the answer to a question Lesson 01 left open. You asked about Lookalike Audiences; LinkedIn's counterpart is Predictive Audiences — you supply a seed (contact list or company list), LinkedIn's model extends it to members likely to convert. Same rw_dmp_segments scope, same 1,000-segment budget, and it produces an ordinary adSegment you target like any other.


Practice

Exercise 1 · execution

The streaming workflow

A customer wants their CRM contacts targetable, kept in sync. Click the steps in order.

Exercise 2 · recognition

Which facet does this segment go in?

Every one of these is a urn:li:adSegment. Pick the facet.

Exercise 3 · bug hunt

Click the broken line

A · POST /rest/dmpSegments — a platform syncing CRM contacts for a customer

{  "name": "Acme CRM — marketing qualified",  "account": "urn:li:sponsoredAccount:516848833",  "type": "USER",  "sourcePlatform": "LIST_UPLOAD",  "destinations": [{ "destination": "LINKEDIN" }]}

B · reading the segment back to find the targetable audience

const seg = await getDmpSegment(id);const audience = `urn:li:adSegment:${seg.id}`;if (seg.destinations[0].status === 'READY') {  await addToCampaignTargeting(campaignId, audience);}

C · POST /rest/dmpSegments — target account list, streamed

{  "name": "Acme — tier 1 target accounts",  "account": "urn:li:sponsoredAccount:516848833",  "type": "COMPANY",  "sourcePlatform": "PARTNER_API",  "sourceSegmentId": "acme/tier1/v3"}

Exercise 4 · recall

Numbers and names

Scope needed for streaming segments
Scope needed for CSV list upload
Max DMP segments per sponsored account
Hours the initial match may take
Days unused before a segment is archived
Days unused before it expires
sourcePlatform for your product

Exercise 5 · free recall

Design decision

From memory: name three columns your ad_segments table needs that LinkedIn will never give you, and say why each is unrecoverable if you skip it.


Retaining this

  1. Start the Matched Audiences application now, ahead of the code. It is the only item in this whole path that is gated on someone else's calendar.
  2. Two entities, one pipeline: dmpSegment is what you write, adSegment is what you target, and the second is nested inside the first as destinationSegmentId. If you retain one sentence, retain that one.
  3. Tomorrow, draw the state machine from memory with just the numbers on the edges — 48h, 24h, 30d, 90d. Then ask yourself which single edge only exists for list uploads. That question is the whole lesson compressed.

Now unlocked

Between Lessons 01–04 you have the spine, the actors, the promotables, the targeting algebra, and audiences. The next natural fringe is status vs. serving — the three-layer distinction between what you asked for (intendedStatus), what the entity is (status), and why it isn't actually running (servingHoldReasons). That is the vocabulary your support team will live in.

Sources: Matched Audiences Overview, DMP Segments, Targeting Criteria Facet URNs, version li-lms-2026-08.