ClawSkills logoClawSkills

LinkedIn

LinkedIn API integration with managed OAuth. Share posts, manage profile, run ads, and access LinkedIn features. Use this skill when users want to share cont...

Introduction

# LinkedIn

Access the LinkedIn API with managed OAuth authentication. Share posts, manage advertising campaigns, retrieve profile and organization information, upload media, and access the Ad Library.

## Quick Start

```bash # Get current user profile python <<'EOF' import urllib.request, os, json req = urllib.request.Request('https://gateway.maton.ai/linkedin/rest/me') req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}') req.add_header('LinkedIn-Version', '202506') print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2)) EOF ```

## Base URL

``` https://gateway.maton.ai/linkedin/rest/{resource} ```

The gateway proxies requests to `api.linkedin.com` and automatically injects your OAuth token.

## Authentication

All requests require the Maton API key in the Authorization header:

``` Authorization: Bearer $MATON_API_KEY ```

**Environment Variable:** Set your API key as `MATON_API_KEY`:

```bash export MATON_API_KEY="YOUR_API_KEY" ```

### Getting Your API Key

1. Sign in or create an account at [maton.ai](https://maton.ai) 2. Go to [maton.ai/settings](https://maton.ai/settings) 3. Copy your API key

### Required Headers

LinkedIn REST API requires the version header:

``` LinkedIn-Version: 202506 ```

## Connection Management

Manage your LinkedIn OAuth connections at `https://ctrl.maton.ai`.

### List Connections

```bash python <<'EOF' import urllib.request, os, json req = urllib.request.Request('https://ctrl.maton.ai/connections?app=linkedin&status=ACTIVE') req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}') print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2)) EOF ```

### Create Connection

```bash python <<'EOF' import urllib.request, os, json data = json.dumps({'app': 'linkedin'}).encode() req = urllib.request.Request('https://ctrl.maton.ai/connections', data=data, method='POST') req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}') req.add_header('Content-Type', 'application/json') print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2)) EOF ```

### Get Connection

```bash python <<'EOF' import urllib.request, os, json req = urllib.request.Request('https://ctrl.maton.ai/connections/{connection_id}') req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}') print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2)) EOF ```

**Response:** ```json { "connection": { "connection_id": "ba10eb9e-b590-4e95-8c2e-3901ff94642a", "status": "ACTIVE", "creation_time": "2026-02-07T08:00:24.372659Z", "last_updated_time": "2026-02-07T08:05:16.609085Z", "url": "https://connect.maton.ai/?session_token=...", "app": "linkedin", "metadata": {} } } ```

Open the returned `url` in a browser to complete OAuth authorization.

### Delete Connection

```bash python <<'EOF' import urllib.request, os, json req = urllib.request.Request('https://ctrl.maton.ai/connections/{connection_id}', method='DELETE') req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}') print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2)) EOF ```

### Specifying Connection

If you have multiple LinkedIn connections, specify which one to use with the `Maton-Connection` header:

```bash python <<'EOF' import urllib.request, os, json req = urllib.request.Request('https://gateway.maton.ai/linkedin/rest/me') req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}') req.add_header('LinkedIn-Version', '202506') req.add_header('Maton-Connection', 'ba10eb9e-b590-4e95-8c2e-3901ff94642a') print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2)) EOF ```

If omitted, the gateway uses the default (oldest) active connection.

## API Reference

### Profile

#### Get Current User Profile

```bash GET /linkedin/rest/me LinkedIn-Version: 202506 ```

**Example:** ```bash python <<'EOF' import urllib.request, os, json req = urllib.request.Request('https://gateway.maton.ai/linkedin/rest/me') req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}') req.add_header('LinkedIn-Version', '202506') print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2)) EOF ```

**Response:** ```json { "firstName": { "localized": {"en_US": "John"}, "preferredLocale": {"country": "US", "language": "en"} }, "localizedFirstName": "John", "lastName": { "localized": {"en_US": "Doe"}, "preferredLocale": {"country": "US", "language": "en"} }, "localizedLastName": "Doe", "id": "yrZCpj2Z12", "vanityName": "johndoe", "localizedHeadline": "Software Engineer at Example Corp", "profilePicture": { "displayImage": "urn:li:digitalmediaAsset:C4D00AAAAbBCDEFGhiJ" } } ```

### Sharing Posts

#### Create a Text Post

```bash POST /linkedin/rest/posts Content-Type: application/json LinkedIn-Version: 202506

{ "author": "urn:li:person:{personId}", "lifecycleState": "PUBLISHED", "visibility": "PUBLIC", "commentary": "Hello LinkedIn! This is my first API post.", "distribution": { "feedDistribution": "MAIN_FEED" } } ```

**Response:** `201 Created` with `x-restli-id` header containing the post URN.

#### Create an Article/URL Share

```bash POST /linkedin/rest/posts Content-Type: application/json LinkedIn-Version: 202506

{ "author": "urn:li:person:{personId}", "lifecycleState": "PUBLISHED", "visibility": "PUBLIC", "commentary": "Check out this great article!", "distribution": { "feedDistribution": "MAIN_FEED" }, "content": { "article": { "source": "https://example.com/article", "title": "Article Title", "description": "Article description here" } } } ```

#### Create an Image Post

First, initialize the image upload, then upload the image, then create the post.

**Step 1: Initialize Image Upload** ```bash POST /linkedin/rest/images?action=initializeUpload Content-Type: application/json LinkedIn-Version: 202506

{ "initializeUploadRequest": { "owner": "urn:li:person:{personId}" } } ```

**Response:** ```json { "value": { "uploadUrlExpiresAt": 1770541529250, "uploadUrl": "https://www.linkedin.com/dms-uploads/...", "image": "urn:li:image:D4D10AQH4GJAjaFCkHQ" } } ```

**Step 2: Upload Image Binary** ```bash PUT {uploadUrl from step 1} Content-Type: image/png

{binary image data} ```

**Step 3: Create Image Post** ```bash POST /linkedin/rest/posts Content-Type: application/json LinkedIn-Version: 202506

{ "author": "urn:li:person:{personId}", "lifecycleState": "PUBLISHED", "visibility": "PUBLIC", "commentary": "Check out this image!", "distribution": { "feedDistribution": "MAIN_FEED" }, "content": { "media": { "id": "urn:li:image:D4D10AQH4GJAjaFCkHQ", "title": "Image Title" } } } ```

### Visibility Options

| Value | Description | |-------|-------------| | `PUBLIC` | Viewable by anyone on LinkedIn | | `CONNECTIONS` | Viewable by 1st-degree connections only |

### Share Media Categories

| Value | Description | |-------|-------------| | `NONE` | Text-only post | | `ARTICLE` | URL/article share | | `IMAGE` | Image post | | `VIDEO` | Video post |

### Ad Library (Public Data)

The Ad Library API provides access to public advertising data on LinkedIn. These endpoints use the REST API with version headers.

#### Required Headers for Ad Library

``` LinkedIn-Version: 202506 ```

#### Search Ads

```bash GET /linkedin/rest/adLibrary?q=criteria&keyword={keyword} ```

Query parameters: - `keyword` (string): Search ad content (multiple keywords use AND logic) - `advertiser` (string): Search by advertiser name - `countries` (array): Filter by ISO 3166-1 alpha-2 country codes - `dateRange` (object): Filter by served dates - `start` (integer): Pagination offset - `count` (integer): Results per page (max 25)

**Example - Search ads by keyword:** ```bash GET /linkedin/rest/adLibrary?q=criteria&keyword=linkedin ```

**Example - Search ads by advertiser:** ```bash GET /linkedin/rest/adLibrary?q=criteria&advertiser=microsoft ```

**Response:** ```json { "paging": { "start": 0, "count": 10, "total": 11619543, "links": [...] }, "elements": [ { "adUrl": "https://www.linkedin.com/ad-library/detail/...", "details": { "advertiser": {...}, "adType": "TEXT_AD", "targeting": {...}, "statistics": { "firstImpressionDate": 1704067200000, "latestImpressionDate": 1706745600000, "impressionsFrom": 1000, "impressionsTo": 5000 } }, "isRestricted": false } ] } ```

#### Search Job Postings

```bash GET /linkedin/rest/jobLibrary?q=criteria&keyword={keyword} ```

**Note:** Job Library requires version `202506`.

Query parameters: - `keyword` (string): Search job content - `organization` (string): Filter by company name - `countries` (array): Filter by country codes - `dateRange` (object): Filter by posting dates - `start` (integer): Pagination offset - `count` (integer): Results per page (max 24)

**Example:** ```bash GET /linkedin/rest/jobLibrary?q=criteria&keyword=software&organization=google ```

**Response includes:** - `jobPostingUrl`: Link to job listing - `jobDetails`: Title, location, description, salary, benefits - `statistics`: Impression data

### Marketing API (Advertising)

The Marketing API provides access to LinkedIn's advertising platform. These endpoints use the versioned REST API.

#### Required Headers for Marketing API

``` LinkedIn-Version: 202506 ```

#### List Ad Accounts

```bash GET /linkedin/rest/adAccounts?q=search ```

Returns all ad accounts accessible by the authenticated user.

**Response:** ```json { "paging": { "start": 0, "count": 10, "links": [] }, "elements": [ { "id": 123456789, "name": "My Ad Account", "status": "ACTIVE", "type": "BUSINESS", "currency": "USD", "reference": "urn:li:organization:12345" } ] } ```

#### Get Ad Account

```bash GET /linkedin/rest/adAccounts/{adAccountId} ```

#### Create Ad Account

```bash POST /linkedin/rest/adAccounts Content-Type: application/json

{ "name": "New Ad Account", "currency": "USD", "reference": "urn:li:organization:{orgId}", "type": "BUSINESS" } ```

#### Update Ad Account

```bash POST /linkedin/rest/adAccounts/{adAccountId} Content-Type: application/json X-RestLi-Method: PARTIAL_UPDATE

{ "patch": { "$set": { "name": "Updated Account Name" } } } ```

#### List Campaign Groups

Campaign groups are nested under ad accounts:

```bash GET /linkedin/rest/adAccounts/{adAccountId}/adCampaignGroups ```

#### Create Campaign Group

```bash POST /linkedin/rest/adAccounts/{adAccountId}/adCampaignGroups Content-Type: application/json

{ "name": "Q1 2026 Campaigns", "status": "DRAFT", "runSchedule": { "start": 1704067200000, "end": 1711929600000 }, "totalBudget": { "amount": "10000", "currencyCode": "USD" } } ```

#### Get Campaign Group

```bash GET /linkedin/rest/adAccounts/{adAccountId}/adCampaignGroups/{campaignGroupId} ```

#### Update Campaign Group

```bash POST /linkedin/rest/adAccounts/{adAccountId}/adCampaignGroups/{campaignGroupId} Content-Type: application/json X-RestLi-Method: PARTIAL_UPDATE

{ "patch": { "$set": { "status": "ACTIVE" } } } ```

#### Delete Campaign Group

```bash DELETE /linkedin/rest/adAccounts/{adAccountId}/adCampaignGroups/{campaignGroupId} ```

#### List Campaigns

Campaigns are also nested under ad accounts:

```bash GET /linkedin/rest/adAccounts/{adAccountId}/adCampaigns ```

#### Create Campaign

```bash POST /linkedin/rest/adAccounts/{adAccountId}/adCampaigns Content-Type: application/json

{ "campaignGroup": "urn:li:sponsoredCampaignGroup:123456", "name": "Brand Awareness Campaign", "status": "DRAFT", "type": "SPONSORED_UPDATES", "objectiveType": "BRAND_AWARENESS", "dailyBudget": { "amount": "100", "currencyCode": "USD" }, "costType": "CPM", "unitCost": { "amount": "5", "currencyCode": "USD" }, "locale": { "country": "US", "language": "en" } } ```

#### Get Campaign

```bash GET /linkedin/rest/adAccounts/{adAccountId}/adCampaigns/{campaignId} ```

#### Update Campaign

```bash POST /linkedin/rest/adAccounts/{adAccountId}/adCampaigns/{campaignId} Content-Type: application/json X-RestLi-Method: PARTIAL_UPDATE

{ "patch": { "$set": { "status": "ACTIVE" } } } ```

#### Delete Campaign

```bash DELETE /linkedin/rest/adAccounts/{adAccountId}/adCampaigns/{campaignId} ```

### Campaign Status Values

| Status | Description | |--------|-------------| | `DRAFT` | Campaign is in draft mode | | `ACTIVE` | Campaign is running | | `PAUSED` | Campaign is paused | | `ARCHIVED` | Campaign is archived | | `COMPLETED` | Campaign has ended | | `CANCELED` | Campaign was canceled |

### Campaign Objective Types

| Objective | Description | |-----------|-------------| | `BRAND_AWARENESS` | Increase brand visibility | | `WEBSITE_VISITS` | Drive traffic to website | | `ENGAGEMENT` | Increase post engagement | | `VIDEO_VIEWS` | Maximize video views | | `LEAD_GENERATION` | Collect leads via Lead Gen Forms | | `WEBSITE_CONVERSIONS` | Drive website conversions | | `JOB_APPLICANTS` | Attract job applications |

### Organizations

#### List Organization ACLs

Get organizations the authenticated user has access to:

```bash GET /linkedin/rest/organizationAcls?q=roleAssignee LinkedIn-Version: 202506 ```

**Response:** ```json { "paging": { "start": 0, "count": 10, "total": 2 }, "elements": [ { "role": "ADMINISTRATOR", "organization": "urn:li:organization:12345", "state": "APPROVED" } ] } ```

#### Get Organization

```bash GET /linkedin/rest/organizations/{organizationId} LinkedIn-Version: 202506 ```

#### Lookup Organization by Vanity Name

```bash GET /linkedin/rest/organizations?q=vanityName&vanityName={vanityName} ```

**Example:** ```bash GET /linkedin/rest/organizations?q=vanityName&vanityName=microsoft ```

**Response:** ```json { "elements": [ { "vanityName": "microsoft", "localizedName": "Microsoft", "website": { "localized": {"en_US": "https://news.microsoft.com/"} } } ] } ```

#### Get Organization Share Statistics

```bash GET /linkedin/rest/organizationalEntityShareStatistics?q=organizationalEntity&organizationalEntity={orgUrn} ```

**Example:** ```bash GET /linkedin/rest/organizationalEntityShareStatistics?q=organizationalEntity&organizationalEntity=urn:li:organization:12345 ```

#### Get Organization Posts

```bash GET /linkedin/rest/posts?q=author&author={orgUrn} ```

**Example:** ```bash GET /linkedin/rest/posts?q=author&author=urn:li:organization:12345 ```

### Media Upload (REST API)

The REST API provides modern media upload endpoints. All require version header `LinkedIn-Version: 202506`.

#### Initialize Image Upload

```bash POST /linkedin/rest/images?action=initializeUpload Content-Type: application/json LinkedIn-Version: 202506

{ "initializeUploadRequest": { "owner": "urn:li:person:{personId}" } } ```

**Response:** ```json { "value": { "uploadUrlExpiresAt": 1770541529250, "uploadUrl": "https://www.linkedin.com/dms-uploads/...", "image": "urn:li:image:D4D10AQH4GJAjaFCkHQ" } } ```

Use the `uploadUrl` to PUT your image binary, then use the `image` URN in your post.

#### Initialize Video Upload

```bash POST /linkedin/rest/videos?action=initializeUpload Content-Type: application/json LinkedIn-Version: 202506

{ "initializeUploadRequest": { "owner": "urn:li:person:{personId}", "fileSizeBytes": 10000000, "uploadCaptions": false, "uploadThumbnail": false } } ```

**Response:** ```json { "value": { "uploadUrlsExpireAt": 1770541530110, "video": "urn:li:video:D4D10AQE_p-P_odQhXQ", "uploadInstructions": [ {"uploadUrl": "https://www.linkedin.com/dms-uploads/..."} ] } } ```

#### Initialize Document Upload

```bash POST /linkedin/rest/documents?action=initializeUpload Content-Type: application/json LinkedIn-Version: 202506

{ "initializeUploadRequest": { "owner": "urn:li:person:{personId}" } } ```

**Response:** ```json { "value": { "uploadUrlExpiresAt": 1770541530896, "uploadUrl": "https://www.linkedin.com/dms-uploads/...", "document": "urn:li:document:D4D10AQHr-e30QZCAjQ" } } ```

### Ad Targeting

#### Get Available Targeting Facets

```bash GET /linkedin/rest/adTargetingFacets ```

Returns all available targeting facets for ad campaigns (31 facets including employers, degrees, skills, locations, industries, etc.).

**Response:** ```json { "elements": [ { "facetName": "skills", "adTargetingFacetUrn": "urn:li:adTargetingFacet:skills", "entityTypes": ["SKILL"], "availableEntityFinders": ["AD_TARGETING_FACET", "TYPEAHEAD"] }, { "facetName": "industries", "adTargetingFacetUrn": "urn:li:adTargetingFacet:industries" } ] } ```

Available targeting facets include: - `skills` - Member skills - `industries` - Industry categories - `titles` - Job titles - `seniorities` - Seniority levels - `degrees` - Educational degrees - `schools` - Educational institutions - `employers` / `employersPast` - Current/past employers - `locations` / `geoLocations` - Geographic targeting - `companySize` - Company size ranges - `genders` - Gender targeting - `ageRanges` - Age range targeting

## Getting Your Person ID

To create posts, you need your LinkedIn person ID. Get it from the `/rest/me` endpoint:

```bash python <<'EOF' import urllib.request, os, json req = urllib.request.Request('https://gateway.maton.ai/linkedin/rest/me') req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}') req.add_header('LinkedIn-Version', '202506') result = json.load(urllib.request.urlopen(req)) print(f"Your person URN: urn:li:person:{result['id']}") EOF ```

## Code Examples

### JavaScript - Create Text Post

```javascript const personId = 'YOUR_PERSON_ID';

const response = await fetch( 'https://gateway.maton.ai/linkedin/rest/posts', { method: 'POST', headers: { 'Authorization': `Bearer ${process.env.MATON_API_KEY}`, 'Content-Type': 'application/json', 'LinkedIn-Version': '202506' }, body: JSON.stringify({ author: `urn:li:person:${personId}`, lifecycleState: 'PUBLISHED', visibility: 'PUBLIC', commentary: 'Hello from the API!', distribution: { feedDistribution: 'MAIN_FEED' } }) } ); ```

### Python - Create Text Post

```python import os import requests

person_id = 'YOUR_PERSON_ID'

response = requests.post( 'https://gateway.maton.ai/linkedin/rest/posts', headers={ 'Authorization': f'Bearer {os.environ["MATON_API_KEY"]}', 'Content-Type': 'application/json', 'LinkedIn-Version': '202506' }, json={ 'author': f'urn:li:person:{person_id}', 'lifecycleState': 'PUBLISHED', 'visibility': 'PUBLIC', 'commentary': 'Hello from the API!', 'distribution': { 'feedDistribution': 'MAIN_FEED' } } ) ```

## Rate Limits

| Throttle Type | Daily Limit (UTC) | |---------------|-------------------| | Member | 150 requests/day | | Application | 100,000 requests/day |

## Notes

- Person IDs are unique per application and not transferable across apps - The `author` field must use URN format: `urn:li:person:{personId}` - All posts require `lifecycleState: "PUBLISHED"` - Image/video uploads are a 3-step process: initialize upload, upload binary, create post - Include `LinkedIn-Version: 202506` header for all REST API calls - Profile picture URLs may expire; re-fetch if needed - IMPORTANT: When using curl commands, use `curl -g` when URLs contain brackets to disable glob parsing - IMPORTANT: When piping curl output to `jq` or other commands, environment variables like `$MATON_API_KEY` may not expand correctly in some shell environments

## Error Handling

| Status | Meaning | |--------|---------| | 400 | Missing LinkedIn connection or invalid request | | 401 | Invalid or missing Maton API key | | 403 | Insufficient permissions (check OAuth scopes) | | 404 | Resource not found | | 422 | Invalid request body or URN format | | 429 | Rate limited | | 4xx/5xx | Passthrough error from LinkedIn API |

### Error Response Format

```json { "status": 403, "serviceErrorCode": 100, "code": "ACCESS_DENIED", "message": "Not enough permissions to access resource" } ```

### Troubleshooting: API Key Issues

1. Check that the `MATON_API_KEY` environment variable is set:

```bash echo $MATON_API_KEY ```

2. Verify the API key is valid by listing connections:

```bash python <<'EOF' import urllib.request, os, json req = urllib.request.Request('https://ctrl.maton.ai/connections') req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}') print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2)) EOF ```

### Troubleshooting: Invalid App Name

1. Ensure your URL path starts with `linkedin`. For example:

- Correct: `https://gateway.maton.ai/linkedin/rest/me` - Incorrect: `https://gateway.maton.ai/rest/me`

## OAuth Scopes

| Scope | Description | |-------|-------------| | `openid` | OpenID Connect authentication | | `profile` | Read basic profile | | `email` | Read email address | | `w_member_social` | Create, modify, and delete posts |

## Resources

- [LinkedIn API Overview](https://learn.microsoft.com/en-us/linkedin/) - [Share on LinkedIn Guide](https://learn.microsoft.com/en-us/linkedin/consumer/integrations/self-serve/share-on-linkedin) - [Profile API](https://learn.microsoft.com/en-us/linkedin/shared/integrations/people/profile-api) - [Sign In with LinkedIn](https://learn.microsoft.com/en-us/linkedin/consumer/integrations/self-serve/sign-in-with-linkedin-v2) - [Authentication Guide](https://learn.microsoft.com/en-us/linkedin/shared/authentication/authentication) - [Marketing API](https://learn.microsoft.com/en-us/linkedin/marketing/) - [Ad Accounts](https://learn.microsoft.com/en-us/linkedin/marketing/integrations/ads/account-structure/create-and-manage-accounts) - [Campaign Management](https://learn.microsoft.com/en-us/linkedin/marketing/integrations/ads/account-structure/create-and-manage-campaigns) - [Ad Library API](https://www.linkedin.com/ad-library/api/) - [Maton Community](https://discord.com/invite/dBfFAcefs2) - [Maton Support](mailto:[email protected])

More Products