TL;DR
Die LinkedIn API ermöglicht Entwicklern die programmatische Integration in das professionelle Netzwerk von LinkedIn. Sie unterstützt OAuth 2.0-Authentifizierung, RESTful- und GraphQL-Endpunkte für Profile, Beiträge, Kommentare, Unternehmensseiten und Anzeigen, mit Ratenbegrenzungen von 100-500 Anfragen pro Tag pro App. In diesem Leitfaden findest du konkrete Schritte zur Authentifizierung, zum Profilzugriff, zum Posten von Inhalten, zur Verwaltung von Unternehmensseiten, zur Ads API und zu produktionsreifen Integrationsstrategien.
Einleitung
LinkedIn zählt über 900 Millionen professionelle Nutzer in mehr als 200 Ländern. Für die Entwicklung von Recruiting-Tools, Marketingplattformen oder B2B-Anwendungen ist die LinkedIn API-Integration entscheidend, um die Zielgruppe effizient zu erreichen.
Manuelles Verwalten der LinkedIn-Präsenz kostet B2B-Marketer 15-20 Stunden pro Woche. Mit einer robusten LinkedIn API-Integration automatisierst du Inhaltsverteilung, Lead-Erfassung, Engagement-Analysen und Rekrutierungs-Workflows.
Dieser Leitfaden zeigt Schritt für Schritt, wie du OAuth 2.0-Authentifizierung, Profilzugriff, Content-Posting, Unternehmensseitenmanagement, Anzeigenintegration, Webhooks und Produktionsstrategien umsetzt. Ziel: eine produktionsreife LinkedIn-Integration.
💡 Apidog vereinfacht das Testen der API-Integration: Teste LinkedIn-Endpunkte, validiere OAuth-Flows, prüfe API-Antworten und debugge Berechtigungen in einer Umgebung. Importiere API-Spezifikationen, modelliere Antworten und teile Testszenarien mit deinem Team.
Was ist die LinkedIn API?
LinkedIn stellt RESTful- und GraphQL-APIs bereit, mit denen du auf Netzwerkdaten zugreifen kannst. Die API unterstützt:
- Benutzerprofilinformationen (mit Zustimmung)
- Unternehmensseiten und Updates
- Beiträge, Kommentare und Reaktionen
- Verbindungen (eingeschränkt)
- Stellenanzeigen und Bewerbungen
- LinkedIn Ads-Verwaltung
- Lead-Generierungsformulare
- Nachrichten (nur für ausgewählte Partner)
Hauptfunktionen
| Funktion | Beschreibung |
|---|---|
| RESTful + GraphQL | Mehrere API-Stile |
| OAuth 2.0 | Benutzerautorisierung erforderlich |
| Ratenbegrenzung | 100-500 Anfragen/Tag pro App |
| Unternehmensseiten | Vollständige CRUD-Operationen |
| Ads API | Kampagnenverwaltung |
| Webhooks | Echtzeit-Benachrichtigungen |
| Medien-Upload | Bilder und Videos |
API-Produkte
| API | Zugriffsebene | Anwendungsfall |
|---|---|---|
| Anmelden mit LinkedIn | Offen | Benutzerauthentifizierung |
| Profil-API | Partner | Benutzerprofil lesen |
| Unternehmensadmin-API | Partner | Unternehmensseiten verwalten |
| Ads API | Partner | Anzeigenkampagnenverwaltung |
| Job Posting API | Partner | Stellenanzeigen veröffentlichen und verwalten |
| Marketing Developer Platform | Partner | Voller API-Zugriff |
API-Versionen
| Version | Status | Enddatum |
|---|---|---|
| v2 | Aktuell | Aktiv |
| v1 | Veraltet | Dezember 2023 |
Erste Schritte: Authentifizierungs-Setup
Schritt 1: LinkedIn-Entwicklerkonto erstellen
- Gehe zum LinkedIn Developer Portal
- Melde dich mit deinem LinkedIn-Konto an
- Klicke im My Apps-Dashboard auf App erstellen
- Fülle die App-Details (Name, Logo, Beschreibung) aus
Schritt 2: App-Einstellungen konfigurieren
Richte die App-Authentifizierung ein:
const LINKEDIN_CLIENT_ID = process.env.LINKEDIN_CLIENT_ID;
const LINKEDIN_CLIENT_SECRET = process.env.LINKEDIN_CLIENT_SECRET;
const LINKEDIN_REDIRECT_URI = process.env.LINKEDIN_REDIRECT_URI;
// Diese Werte findest du im App-Dashboard
// https://www.linkedin.com/developers/apps/{appId}/auth
Schritt 3: Erforderliche Berechtigungen anfordern
LinkedIn verlangt explizite Genehmigung für Berechtigungen:
| Berechtigung | Beschreibung | Genehmigung erforderlich |
|---|---|---|
r_liteprofile |
Basisches Profil (Name, Foto) | Automatisch genehmigt |
r_emailaddress |
E-Mail-Adresse | Automatisch genehmigt |
w_member_social |
Im Namen des Benutzers posten | Partnerverifizierung |
r_basicprofile |
Vollständiges Profil | Partnerverifizierung |
r_organization_social |
Zugriff auf Unternehmensseiten | Partnerverifizierung |
w_organization_social |
Auf Unternehmensseite posten | Partnerverifizierung |
rw_ads |
Anzeigenverwaltung | Partnerverifizierung |
r_ads_reporting |
Anzeigenanalysen | Partnerverifizierung |
Schritt 4: Autorisierungs-URL erstellen
Implementiere den OAuth 2.0-Flow:
const getAuthUrl = (state, scopes = ['r_liteprofile', 'r_emailaddress']) => {
const params = new URLSearchParams({
response_type: 'code',
client_id: LINKEDIN_CLIENT_ID,
redirect_uri: LINKEDIN_REDIRECT_URI,
scope: scopes.join(' '),
state: state
});
return `https://www.linkedin.com/oauth/v2/authorization?${params.toString()}`;
};
// Beispiel
const state = require('crypto').randomBytes(16).toString('hex');
const authUrl = getAuthUrl(state, ['r_liteprofile', 'r_emailaddress', 'w_member_social']);
console.log(`Benutzer weiterleiten zu: ${authUrl}`);
Schritt 5: Code gegen Zugriffstoken tauschen
Verarbeite den OAuth-Callback:
const exchangeCodeForToken = async (code) => {
const response = await fetch('https://www.linkedin.com/oauth/v2/accessToken', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
grant_type: 'authorization_code',
code: code,
client_id: LINKEDIN_CLIENT_ID,
client_secret: LINKEDIN_CLIENT_SECRET,
redirect_uri: LINKEDIN_REDIRECT_URI
})
});
const data = await response.json();
return {
accessToken: data.access_token,
expiresIn: data.expires_in // 60 Tage
};
};
// Callback-Handler
app.get('/oauth/callback', async (req, res) => {
const { code, state } = req.query;
try {
const tokens = await exchangeCodeForToken(code);
// Token sicher speichern
await db.users.update(req.session.userId, {
linkedin_access_token: tokens.accessToken,
linkedin_token_expires: Date.now() + (tokens.expiresIn * 1000)
});
res.redirect('/success');
} catch (error) {
console.error('OAuth-Fehler:', error);
res.status(500).send('Authentifizierung fehlgeschlagen');
}
});
Schritt 6: Zugriffstoken aktualisieren
Tokens laufen nach 60 Tagen ab – implementiere eine Ablaufbenachrichtigung:
const refreshAccessToken = async (refreshToken) => {
// LinkedIn gibt keine Refresh-Token aus
// Nach 60 Tagen erneute Authentifizierung erforderlich
// Ablaufbenachrichtigung implementieren
};
const ensureValidToken = async (userId) => {
const user = await db.users.findById(userId);
if (user.linkedin_token_expires < Date.now() + 86400000) { // 24h Vorwarnung
await notifyUserToReauth(user.id);
throw new Error('Token abgelaufen, bitte neu authentifizieren');
}
return user.linkedin_access_token;
};
Schritt 7: Authentifizierte API-Aufrufe tätigen
Erstelle einen wiederverwendbaren API-Client:
const LINKEDIN_BASE_URL = 'https://api.linkedin.com/v2';
const linkedinRequest = async (endpoint, options = {}) => {
const accessToken = await ensureValidToken(options.userId);
const response = await fetch(`${LINKEDIN_BASE_URL}${endpoint}`, {
...options,
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json',
'X-Restli-Protocol-Version': '2.0.0',
...options.headers
}
});
if (!response.ok) {
const error = await response.json();
throw new Error(`LinkedIn API-Fehler: ${error.message}`);
}
return response.json();
};
// Beispiel: Profil abfragen
const profile = await linkedinRequest('/me');
console.log(`Hallo, ${profile.localizedFirstName} ${profile.localizedLastName}`);
Profilzugriff
Benutzerprofil abrufen
Hole das Profil des authentifizierten Users:
const getUserProfile = async () => {
const response = await linkedinRequest('/me?projection=(id,firstName,lastName,profilePicture(displayImage~:playableStreams))');
return response;
};
// Nutzung
const profile = await getUserProfile();
console.log(`Name: ${profile.localizedFirstName} ${profile.localizedLastName}`);
console.log(`ID: ${profile.id}`);
console.log(`Foto: ${profile.profilePicture?.['displayImage~']?.elements?.[0]?.identifiers?.[0]?.identifier}`);
E-Mail-Adresse abrufen
const getUserEmail = async () => {
const response = await linkedinRequest('/emailAddress?q=members&projection=(emailAddress*)');
return response;
};
const email = await getUserEmail();
console.log(`E-Mail: ${email.elements?.[0]?.emailAddress}`);
Verfügbare Profilfelder
| Feld | Berechtigung | Beschreibung |
|---|---|---|
id |
r_liteprofile | LinkedIn-Mitglieds-ID |
firstName |
r_liteprofile | Vorname |
lastName |
r_liteprofile | Nachname |
profilePicture |
r_liteprofile | Profilfoto-URL |
headline |
r_basicprofile | Berufliche Überschrift |
summary |
r_basicprofile | Über-Mich-Bereich |
positions |
r_basicprofile | Berufserfahrung |
educations |
r_basicprofile | Ausbildungsgeschichte |
emailAddress |
r_emailaddress | Primäre E-Mail |
Inhalte posten
Einen Beitrag erstellen
Textbeitrag posten:
const createPost = async (authorUrn, postContent) => {
const response = await linkedinRequest('/ugcPosts', {
method: 'POST',
body: JSON.stringify({
author: authorUrn,
lifecycleState: 'PUBLISHED',
specificContent: {
'com.linkedin.ugc.ShareContent': {
shareCommentary: {
text: postContent.text
},
shareMediaCategory: 'NONE'
}
},
visibility: {
'com.linkedin.ugc.MemberNetworkVisibility': 'PUBLIC'
}
})
});
return response;
};
// Beispiel
const post = await createPost('urn:li:person:ABC123', {
text: 'Excited to announce our new product launch! 🚀 #innovation #startup'
});
console.log(`Beitrag erstellt: ${post.id}`);
Einen Beitrag mit Bild erstellen
Bild posten:
const createPostWithImage = async (authorUrn, postData) => {
// 1. Upload registrieren
const uploadRegistration = await linkedinRequest('/assets?action=registerUpload', {
method: 'POST',
body: JSON.stringify({
registerUploadRequest: {
recipes: ['urn:li:digitalmediaRecipe:feedshare-image'],
owner: authorUrn,
serviceRelationships: [
{
relationshipType: 'OWNER',
identifier: 'urn:li:userGeneratedContent'
}
]
}
})
});
const uploadUrl = uploadRegistration.value.uploadMechanism['com.linkedin.digitalmedia.uploading.MediaUploadHttpRequest'].uploadUrl;
const assetUrn = uploadRegistration.value.asset;
// 2. Bild hochladen
await fetch(uploadUrl, {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + await getAccessToken(),
'Content-Type': 'application/octet-stream'
},
body: postData.imageBuffer
});
// 3. Beitrag erstellen
const post = await linkedinRequest('/ugcPosts', {
method: 'POST',
body: JSON.stringify({
author: authorUrn,
lifecycleState: 'PUBLISHED',
specificContent: {
'com.linkedin.ugc.ShareContent': {
shareCommentary: {
text: postData.text
},
shareMediaCategory: 'IMAGE',
media: [
{
status: 'READY',
description: {
text: postData.imageDescription || ''
},
media: assetUrn,
title: {
text: postData.title || ''
}
}
]
}
},
visibility: {
'com.linkedin.ugc.MemberNetworkVisibility': 'PUBLIC'
}
})
});
return post;
};
Einen Beitrag mit Video erstellen
Video posten:
const createPostWithVideo = async (authorUrn, postData) => {
// Video-Upload registrieren
const uploadRegistration = await linkedinRequest('/assets?action=registerUpload', {
method: 'POST',
body: JSON.stringify({
registerUploadRequest: {
recipes: ['urn:li:digitalmediaRecipe:feedshare-video'],
owner: authorUrn,
serviceRelationships: [
{
relationshipType: 'OWNER',
identifier: 'urn:li:userGeneratedContent'
}
]
}
})
});
const assetUrn = uploadRegistration.value.asset;
// Video hochladen (Upload-Logik)
// ...
// Beitrag erstellen
const post = await linkedinRequest('/ugcPosts', {
method: 'POST',
body: JSON.stringify({
author: authorUrn,
lifecycleState: 'PUBLISHED',
specificContent: {
'com.linkedin.ugc.ShareContent': {
shareCommentary: { text: postData.text },
shareMediaCategory: 'VIDEO',
media: [{ status: 'READY', media: assetUrn }]
}
},
visibility: { 'com.linkedin.ugc.MemberNetworkVisibility': 'PUBLIC' }
})
});
return post;
};
Medienspezifikationen
| Medientyp | Format | Max. Größe | Dauer |
|---|---|---|---|
| Bild | JPG, PNG, GIF | 8 MB | N/A |
| Video | MP4, MOV | 5 GB | max. 15 Min. |
| Dokument | PDF, PPT, DOC | 100 MB | N/A |
Unternehmensseitenverwaltung
Unternehmensinformationen abrufen
const getCompanyInfo = async (companyId) => {
const response = await linkedinRequest(
`/organizations/${companyId}?projection=(id,localizedName,vanityName,tagline,description,universalName,logoV2(original~:playableStreams),companyType,companyPageUrl,confirmedLocations,industries,followerCount,staffCountRange,website, specialties)`
);
return response;
};
// Beispiel
const company = await getCompanyInfo('1234567');
console.log(`Unternehmen: ${company.localizedName}`);
console.log(`Follower: ${company.followerCount}`);
console.log(`Webseite: ${company.website}`);
Auf Unternehmensseite posten
const createCompanyPost = async (organizationUrn, postContent) => {
const response = await linkedinRequest('/ugcPosts', {
method: 'POST',
body: JSON.stringify({
author: organizationUrn,
lifecycleState: 'PUBLISHED',
specificContent: {
'com.linkedin.ugc.ShareContent': {
shareCommentary: {
text: postContent.text
},
shareMediaCategory: postContent.media ? 'IMAGE' : 'NONE',
media: postContent.media ? [
{
status: 'READY',
media: postContent.media.assetUrn
}
] : []
}
},
visibility: {
'com.linkedin.ugc.MemberNetworkVisibility': 'PUBLIC'
}
})
});
return response;
};
// Beispiel
const post = await createCompanyPost('urn:li:organization:1234567', {
text: 'Wir stellen ein! Werden Sie Teil unseres wachsenden Teams. #karriere #einstellung'
});
Follower des Unternehmens abrufen
const getFollowerCount = async (organizationId) => {
const response = await linkedinRequest(
`/organizationalEntityFollowerStatistics?q=organizationalEntity&organizationalEntity=urn:li:organization:${organizationId}`
);
return response;
};
Ratenbegrenzung
Ratenbegrenzungen verstehen
| API | Limit | Fenster |
|---|---|---|
| Profil-API | 100 Anfragen | Pro Tag |
| UGC-Beiträge | 50 Beiträge | Pro Tag |
| Unternehmensadmin | 500 Anfragen | Pro Tag |
| Ads API | 100 Anfragen | Pro Minute |
Ratenbegrenzungs-Header
| Header | Beschreibung |
|---|---|
X-Restli-Quota-Remaining |
Verbleibende Anfragen |
X-Restli-Quota-Reset |
Sekunden bis zum Zurücksetzen |
Fehlerbehebung bei häufigen Problemen
Problem: 401 Nicht autorisiert
Lösungen:
- Zugriffstoken prüfen (Gültigkeit: 60 Tage)
- Scopes des Tokens validieren
- Header
Authorization: Bearer {token}setzen
Problem: 403 Verboten
Lösungen:
- App-Berechtigungen prüfen
- Benutzerfreigabe der Scopes sicherstellen
- Partnerverifizierung ggf. abschließen
Problem: 429 Ratenbegrenzung erreicht
Lösungen:
- Anfragewarteschlange implementieren
- Antworten cachen und so API-Aufrufe reduzieren
- Webhooks statt Polling nutzen
Checkliste für die Produktionsbereitstellung
Vor dem Go-Live:
- [ ] LinkedIn-Partnerverifizierung abgeschlossen
- [ ] OAuth 2.0 mit sicherer Token-Speicherung eingebaut
- [ ] Token-Ablaufbenachrichtigungen (60 Tage) integriert
- [ ] Ratenbegrenzung und Warteschlange implementiert
- [ ] Webhook-Endpunkte konfiguriert
- [ ] Fehlerbehandlung abgedeckt
- [ ] Logging aller API-Aufrufe
- [ ] Überprüfung auf Markenrichtlinien-Konformität
Anwendungsfälle in der Praxis
Rekrutierungsplattform
- Herausforderung: Manuelles Posten auf mehreren Kanälen
- Lösung: LinkedIn Jobs API-Integration
- Ergebnis: 80 % Zeitersparnis, 3x mehr Bewerbungen
B2B Marketing-Automatisierung
- Herausforderung: Unregelmäßiger Veröffentlichungsplan
- Lösung: Automatisiertes Posten via UGC API
- Ergebnis: 5x mehr Engagement, konsistente Markenpräsenz
Fazit
Die LinkedIn API bietet umfassenden Zugriff auf professionelle Netzwerkfunktionen. Zu beachten:
- OAuth 2.0-Authentifizierung mit 60-Tage-Tokens
- APIs für Profil, Posting und Unternehmensseiten verfügbar
- Ratenbegrenzungen (100-500/Tag) erfordern aktive Verwaltung
- Partnerverifizierung für viele APIs nötig
- Apidog optimiert API-Tests und Teamwork
FAQ-Bereich
Wie erhalte ich Zugang zur LinkedIn API?
Erstelle ein LinkedIn-Entwicklerkonto, eine App und beantrage die Partnerverifizierung für erweiterten API-Zugriff.
Kann ich automatisch auf LinkedIn posten?
Ja, per UGC API mit Berechtigung w_member_social (persönlich) bzw. w_organization_social (Unternehmensposts).
Was sind LinkedIn-Ratenbegrenzungen?
Je nach API zwischen 100 und 500 Anfragen pro Tag; Ads API: 100 pro Minute.
Wie lange sind LinkedIn-Tokens gültig?
Tokens sind 60 Tage gültig. Danach ist erneute Authentifizierung erforderlich.
Kann ich auf Benutzerverbindungen zugreifen?
Nein, LinkedIn hat API-Zugriff auf Verbindungen für die meisten Apps aus Datenschutzgründen entfernt.
Top comments (0)