TL;DR
A API do LinkedIn permite integração programática com a rede profissional do LinkedIn via OAuth 2.0, endpoints RESTful e GraphQL para perfis, publicações, comentários, páginas de empresas e anúncios. Limites de 100-500 requisições/dia por app. Este guia mostra como autenticar, acessar perfis, publicar, gerenciar páginas, usar API de anúncios e preparar sua integração para produção.
Introdução
O LinkedIn conta com mais de 900 milhões de profissionais globalmente. Se você desenvolve ferramentas de recrutamento, plataformas de marketing ou apps B2B, integrar a API do LinkedIn é essencial para atingir esse público.
Profissionais de marketing B2B gastam 15-20 horas semanais gerenciando LinkedIn manualmente. Automatize postagens, geração de leads, análise de engajamento e recrutamento via API.
Este guia cobre: OAuth 2.0, acesso a perfis, publicação, gerenciamento de páginas, anúncios, webhooks e estratégias de produção. Ao fim, você terá uma integração pronta para produção.
💡 Apidog simplifica o teste de integração de API. Teste seus endpoints do LinkedIn, valide fluxos OAuth, inspecione respostas da API e depure problemas de permissão em um único ambiente de trabalho. Importe especificações de API, simule respostas e compartilhe cenários de teste com sua equipe.
O Que É a API do LinkedIn?
O LinkedIn oferece APIs RESTful e GraphQL para acessar dados da rede profissional. Principais funcionalidades:
- Informações de perfil (com consentimento)
- Páginas de empresas e atualizações
- Publicações, comentários, reações
- Conexões (limitado)
- Vagas e candidaturas
- Gerenciamento de anúncios
- Formulários de leads
- Mensagens (acesso restrito)
Principais Recursos
| Recurso | Descrição |
|---|---|
| RESTful + GraphQL | Múltiplos estilos de API |
| OAuth 2.0 | Autorização do usuário necessária |
| Limite de Taxa | 100-500 requisições/dia por aplicativo |
| Páginas de Empresas | Operações CRUD completas |
| API de Anúncios | Gerenciamento de campanhas |
| Webhooks | Notificações em tempo real |
| Upload de Mídia | Imagens e vídeos |
Produtos da API
| API | Nível de Acesso | Caso de Uso |
|---|---|---|
| Entrar com LinkedIn | Aberto | Autenticação de usuário |
| API de Perfil | Parceiro | Ler perfil de usuário |
| API de Administração de Empresas | Parceiro | Gerenciar páginas de empresas |
| API de Anúncios | Parceiro | Gerenciamento de campanhas de anúncios |
| API de Publicação de Vagas | Parceiro | Publicar e gerenciar vagas |
| Plataforma de Desenvolvedores de Marketing | Parceiro | Acesso completo à API |
Versões da API
| Versão | Status | Data de Término |
|---|---|---|
| v2 | Atual | Ativa |
| v1 | Aposentada | Dezembro de 2023 |
Primeiros Passos: Configuração de Autenticação
Passo 1: Criar Conta de Desenvolvedor LinkedIn
- Acesse o Portal de Desenvolvedores do LinkedIn
- Faça login
- Clique em Criar Aplicativo
- Preencha os detalhes do app (nome, logo, descrição)
Passo 2: Configurar Definições do Aplicativo
Configure variáveis de ambiente para autenticação:
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;
// Obtenha esses dados no painel do app
// https://www.linkedin.com/developers/apps/{appId}/auth
Passo 3: Solicitar Permissões Necessárias
Solicite permissões apropriadas no painel. Veja tabela:
| Permissão | Descrição | Aprovação Necessária |
|---|---|---|
r_liteprofile |
Perfil básico (nome, foto) | Aprovado automaticamente |
r_emailaddress |
Endereço de e-mail | Aprovado automaticamente |
w_member_social |
Publicar em nome do usuário | Verificação de parceiro |
r_basicprofile |
Perfil completo | Verificação de parceiro |
r_organization_social |
Acesso à página da empresa | Verificação de parceiro |
w_organization_social |
Publicar na página da empresa | Verificação de parceiro |
rw_ads |
Gerenciamento de anúncios | Verificação de parceiro |
r_ads_reporting |
Análises de anúncios | Verificação de parceiro |
Passo 4: Construir URL de Autorização
Implemente o fluxo OAuth 2.0:
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()}`;
};
// Uso
const state = require('crypto').randomBytes(16).toString('hex');
const authUrl = getAuthUrl(state, ['r_liteprofile', 'r_emailaddress', 'w_member_social']);
console.log(`Redirecionar usuário para: ${authUrl}`);
Passo 5: Trocar Código por Token de Acesso
Lide com o callback OAuth:
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 dias
};
};
// Callback handler
app.get('/oauth/callback', async (req, res) => {
const { code, state } = req.query;
try {
const tokens = await exchangeCodeForToken(code);
// Armazene tokens com segurança
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 error:', error);
res.status(500).send('Autenticação falhou');
}
});
Passo 6: Atualizar Token de Acesso
Tokens expiram em 60 dias. Não há refresh token:
const refreshAccessToken = async (refreshToken) => {
// LinkedIn não fornece refresh token.
// Usuários devem reautenticar após 60 dias.
// Implemente notificação de expiração.
};
// Antes de chamadas, valide token:
const ensureValidToken = async (userId) => {
const user = await db.users.findById(userId);
if (user.linkedin_token_expires < Date.now() + 86400000) { // 24 horas
// Notifique usuário para reautenticar
await notifyUserToReauth(user.id);
throw new Error('Token expirado, por favor, reautentique');
}
return user.linkedin_access_token;
};
Passo 7: Fazer Chamadas de API Autenticadas
Crie um cliente reutilizável:
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(`Erro da API do LinkedIn: ${error.message}`);
}
return response.json();
};
// Uso
const profile = await linkedinRequest('/me');
console.log(`Olá, ${profile.localizedFirstName} ${profile.localizedLastName}`);
Acesso ao Perfil
Obtendo Perfil do Usuário
Busque o perfil autenticado:
const getUserProfile = async () => {
const response = await linkedinRequest('/me?projection=(id,firstName,lastName,profilePicture(displayImage~:playableStreams))');
return response;
};
// Uso
const profile = await getUserProfile();
console.log(`Nome: ${profile.localizedFirstName} ${profile.localizedLastName}`);
console.log(`ID: ${profile.id}`);
console.log(`Foto: ${profile.profilePicture?.['displayImage~']?.elements?.[0]?.identifiers?.[0]?.identifier}`);
Obtendo Endereço de E-mail
const getUserEmail = async () => {
const response = await linkedinRequest('/emailAddress?q=members&projection=(emailAddress*)');
return response;
};
// Uso
const email = await getUserEmail();
console.log(`E-mail: ${email.elements?.[0]?.emailAddress}`);
Campos de Perfil Disponíveis
| Campo | Permissão | Descrição |
|---|---|---|
id |
r_liteprofile | ID de membro do LinkedIn |
firstName |
r_liteprofile | Primeiro nome |
lastName |
r_liteprofile | Sobrenome |
profilePicture |
r_liteprofile | URL da foto de perfil |
headline |
r_basicprofile | Título profissional |
summary |
r_basicprofile | Seção "Sobre" |
positions |
r_basicprofile | Histórico de trabalho |
educations |
r_basicprofile | Histórico educacional |
emailAddress |
r_emailaddress | E-mail principal |
Publicação de Conteúdo
Criando uma Publicação
Publique texto no feed do usuário:
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;
};
// Uso
const post = await createPost('urn:li:person:ABC123', {
text: 'Animados para anunciar o lançamento do nosso novo produto! 🚀 #inovação #startup'
});
console.log(`Publicação criada: ${post.id}`);
Criando uma Publicação com Imagem
const createPostWithImage = async (authorUrn, postData) => {
// Passo 1: Registrar upload da imagem
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;
// Passo 2: Upload da imagem
await fetch(uploadUrl, {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + await getAccessToken(),
'Content-Type': 'application/octet-stream'
},
body: postData.imageBuffer
});
// Passo 3: Criar publicação com a imagem
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;
};
Criando uma Publicação com Vídeo
const createPostWithVideo = async (authorUrn, postData) => {
// Registrar upload do vídeo
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;
// Upload do vídeo (usar URLs pré-assinadas)
// ... lógica de upload ...
// Criar publicação
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;
};
Especificações de Mídia
| Tipo de Mídia | Formato | Tamanho Máximo | Duração |
|---|---|---|---|
| Imagem | JPG, PNG, GIF | 8MB | N/A |
| Vídeo | MP4, MOV | 5GB | 15 min no máximo |
| Documento | PDF, PPT, DOC | 100MB | N/A |
Gerenciamento de Páginas de Empresas
Obtendo Informações da Empresa
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;
};
// Uso
const company = await getCompanyInfo('1234567');
console.log(`Empresa: ${company.localizedName}`);
console.log(`Seguidores: ${company.followerCount}`);
console.log(`Site: ${company.website}`);
Publicando em Páginas de Empresas
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;
};
// Uso
const post = await createCompanyPost('urn:li:organization:1234567', {
text: 'Estamos contratando! Junte-se à nossa equipe em crescimento. #carreiras #contratação'
});
Obtendo Seguidores da Empresa
const getFollowerCount = async (organizationId) => {
const response = await linkedinRequest(
`/organizationalEntityFollowerStatistics?q=organizationalEntity&organizationalEntity=urn:li:organization:${organizationId}`
);
return response;
};
Limite de Taxa
Entendendo os Limites de Taxa
| API | Limite | Janela |
|---|---|---|
| API de Perfil | 100 requisições | Por dia |
| Publicações UGC | 50 publicações | Por dia |
| Administração de Empresas | 500 requisições | Por dia |
| API de Anúncios | 100 requisições | Por minuto |
Cabeçalhos de Limite de Taxa
| Cabeçalho | Descrição |
|---|---|
X-Restli-Quota-Remaining |
Requisições restantes |
X-Restli-Quota-Reset |
Segundos até a redefinição |
Resolução de Problemas Comuns
Problema: 401 Não Autorizado
Soluções:
- Verifique se o token de acesso não expirou (60 dias)
- Confira se o escopo do token é suficiente para o recurso
- Confirme o cabeçalho
Authorization: Bearer {token}
Problema: 403 Proibido
Soluções:
- Confira permissões do aplicativo
- Garanta que o usuário aprovou os escopos
- Verificação de parceiro pode ser necessária
Problema: 429 Limite de Taxa Atingido
Soluções:
- Implemente filas de requisições
- Use cache para reduzir chamadas
- Prefira webhooks em vez de polling
Checklist de Implantação em Produção
Antes de liberar sua integração:
- [ ] Finalize verificação de Parceiro do LinkedIn
- [ ] Implemente OAuth 2.0 com armazenamento seguro
- [ ] Notifique usuários sobre expiração de token (60 dias)
- [ ] Implemente controle de limite de taxa/filas
- [ ] Configure endpoints de webhook
- [ ] Trate erros de forma abrangente
- [ ] Faça log de todas as chamadas de API
- [ ] Revise conformidade com diretrizes de marca
Casos de Uso Reais
Plataforma de Recrutamento
- Desafio: Publicação manual em múltiplos canais
- Solução: Integração com API de Vagas
- Resultado: 80% menos tempo, 3x candidaturas
Automação de Marketing B2B
- Desafio: Agendamento inconsistente
- Solução: Publicação automatizada via API UGC
- Resultado: 5x mais engajamento, marca consistente
Conclusão
A API do LinkedIn oferece acesso amplo à rede profissional. Pontos-chave:
- OAuth 2.0 com validade de token de 60 dias
- APIs de perfil, publicação e empresas disponíveis
- Limites de taxa requerem controle rigoroso
- Verificação de parceiro necessária para APIs avançadas
- Apidog simplifica testes de API e colaboração
Seção de Perguntas Frequentes (FAQ)
Como faço para obter acesso à API do LinkedIn?
Crie uma conta de Desenvolvedor, um aplicativo e conclua a verificação de Parceiro para acesso avançado.
Posso publicar no LinkedIn automaticamente?
Sim, utilize a API UGC com permissão w_member_social para postagens pessoais ou w_organization_social para empresas.
Quais são os limites de taxa do LinkedIn?
De 100 a 500 requisições/dia por API. API de Anúncios: 100/min.
Quanto tempo duram os tokens do LinkedIn?
60 dias. O usuário precisa reautenticar após isso.
Posso acessar as conexões do usuário?
Não. O LinkedIn restringiu o acesso a conexões para a maioria dos apps devido a privacidade.
Top comments (0)