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
# 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:
export MATON_API_KEY="YOUR_API_KEY"
Getting Your API Key
- Sign in or create an account at maton.ai
- Go to maton.ai/settings
- 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
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
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
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
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:
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
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
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
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
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
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
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
GET /linkedin/rest/adAccounts/{adAccountId}
Create Ad Account
POST /linkedin/rest/adAccounts
Content-Type: application/json
{
"name": "New Ad Account",
"currency": "USD",
"reference": "urn:li:organization:{orgId}",
"type": "BUSINESS"
}
Update Ad Account
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:
GET /linkedin/rest/adAccounts/{adAccountId}/adCampaignGroups
Create Campaign Group
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
GET /linkedin/rest/adAccounts/{adAccountId}/adCampaignGroups/{campaignGroupId}
Update Campaign Group
POST /linkedin/rest/adAccounts/{adAccountId}/adCampaignGroups/{campaignGroupId}
Content-Type: application/json
X-RestLi-Method: PARTIAL_UPDATE
{
"patch": {
"$set": {
"status": "ACTIVE"
}
}
}
Delete Campaign Group
DELETE /linkedin/rest/adAccounts/{adAccountId}/adCampaignGroups/{campaignGroupId}
List Campaigns
Campaigns are also nested under ad accounts:
GET /linkedin/rest/adAccounts/{adAccountId}/adCampaigns
Create Campaign
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
GET /linkedin/rest/adAccounts/{adAccountId}/adCampaigns/{campaignId}
Update Campaign
POST /linkedin/rest/adAccounts/{adAccountId}/adCampaigns/{campaignId}
Content-Type: application/json
X-RestLi-Method: PARTIAL_UPDATE
{
"patch": {
"$set": {
"status": "ACTIVE"
}
}
}
Delete Campaign
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:
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
GET /linkedin/rest/organizations/{organizationId}
LinkedIn-Version: 202506
Lookup Organization by Vanity Name
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
GET /linkedin/rest/organizationalEntityShareStatistics?q=organizationalEntity&organizationalEntity={orgUrn}
Example:
bash
GET /linkedin/rest/organizationalEntityShareStatistics?q=organizationalEntity&organizationalEntity=urn:li:organization:12345
Get Organization Posts
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
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
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
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
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:
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
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
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
authorfield 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: 202506header for all REST API calls - Profile picture URLs may expire; re-fetch if needed
- IMPORTANT: When using curl commands, use
curl -gwhen URLs contain brackets to disable glob parsing - IMPORTANT: When piping curl output to
jqor other commands, environment variables like$MATON_API_KEYmay 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
{
"status": 403,
"serviceErrorCode": 100,
"code": "ACCESS_DENIED",
"message": "Not enough permissions to access resource"
}
Troubleshooting: API Key Issues
- Check that the
MATON_API_KEYenvironment variable is set:
echo $MATON_API_KEY
- Verify the API key is valid by listing connections:
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
- 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 |