You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

使用LinkedIn UGC API发布带图片的个人动态时持续返回500内部服务器错误的排查求助

LinkedIn UGC API发布带图片的个人动态时持续返回500内部服务器错误的排查求助

我最近在折腾用Python结合LinkedIn的UGC API自动发个人动态,OAuth2授权码模式已经走通了,拿到的access token也确认带了w_member_social权限。图片上传这步完全没问题:用assets?action=registerUpload接口注册上传,然后用同步上传机制把PNG图片传上去,LinkedIn返回了201状态码和有效的asset URN(比如urn:li:digitalmediaAsset:C4E22AQF...)。

但一到调用/v2/ugcPosts接口发布动态的时候,就一直碰500内部服务器错误——不管带不带图片都一样!

先给大家看看我关键的请求参数:

图片上传的注册请求体

{
  "registerUploadRequest": {
    "owner": "urn:li:me",
    "recipes": ["urn:li:digitalmediaRecipe:feedshare-image"],
    "serviceRelationships": [{
      "identifier": "urn:li:userGeneratedContent",
      "relationshipType": "OWNER"
    }],
    "supportedUploadMechanism": ["SYNCHRONOUS_UPLOAD"]
  }
}

这一步百分百成功,返回的asset URN格式也完全符合要求。

UGC动态发布的请求体

{
  "lifecycleState": "PUBLISHED",
  "specificContent": {
    "com.linkedin.ugc.ShareContent": {
      "shareCommentary": {
        "text": "Test post from script"
      },
      "shareMediaCategory": "IMAGE",
      "media": [{
        "status": "READY",
        "media": "urn:li:digitalmediaAsset:C4E22AQF..."
      }]
    }
  },
  "visibility": {
    "com.linkedin.ugc.MemberNetworkVisibility": "PUBLIC"
  }
}

哪怕我把shareMediaCategory改成NONE、删掉整个media块发纯文本动态,还是返回同样的500错误:{"message": "Internal Server Error", "status": 500}

我已经试过这些排查手段,但都没解决问题:

  • 用cURL发送完全相同的请求,结果一模一样
  • 提前校验了JSON格式,没有任何语法错误
  • 确认access token有效,且包含w_member_social权限
  • 图片上传用的urn:li:me作为owner,和个人token完全匹配

下面是我完整的Python代码片段,方便大家帮我找问题:

import os
import json
import time
import requests
import mysql.connector
from datetime import datetime

# Configuration
CLIENT_ID = 'xxxxxxxxxxxxxxxx'
CLIENT_SECRET = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
REDIRECT_URI = 'http://localhost:8000'
TOKEN_FILE = 'token.json'
# This was originally for organization posting (not used for personal profile posting)
LINKEDIN_ORGANIZATION_ID = 'urn:li:organization:xxxxxxxx'
DB_CONFIG = {
    'host': 'localhost',
    'user': 'root',
    'password': '*****',
    'database': 'kerket'
}

# Save the token to a file
def save_token(data):
    with open(TOKEN_FILE, 'w') as f:
        json.dump(data, f)

# Load the token from a file
def load_token():
    if not os.path.exists(TOKEN_FILE):
        return None
    with open(TOKEN_FILE, 'r') as f:
        return json.load(f)

# Validate expiration
def is_token_expired(token_data):
    return datetime.utcnow().timestamp() > token_data.get('expires_at', 0)

# Get valid token (currently expiration is not enforced)
def get_valid_token():
    token_data = load_token()
    return token_data['access_token']

# Upload an image or video file to LinkedIn and return asset URN and category
def upload_media_to_linkedin(filepath):
    filename = os.path.basename(filepath)
    ext = filename.lower().split('.')[-1]

    if ext in ['jpg', 'jpeg', 'png']:
        recipe = "urn:li:digitalmediaRecipe:feedshare-image"
        category = "IMAGE"
        content_type = "image/jpeg" if ext in ['jpg', 'jpeg'] else "image/png"
    elif ext == 'mp4':
        recipe = "urn:li:digitalmediaRecipe:feedshare-video"
        category = "VIDEO"
        content_type = "application/octet-stream"
    else:
        print(f"[!] Unsupported format: {filepath}")
        return None, None

    token = get_valid_token()
    if not token:
        return None, None

    headers = {
        "Authorization": f"Bearer {token}",
        "Content-Type": "application/json"
    }

    print(f"[INFO] Uploading {filename} to LinkedIn...")
    print(f"[INFO] Content-Type: {content_type}")
    print(f"[INFO] Recipe: {recipe}")

    # Register the upload
    register_data = {
        "registerUploadRequest": {
            "owner": "urn:li:me", # Works with personal profile token
            "recipes": [recipe],
            "serviceRelationships": [{
                "identifier": "urn:li:userGeneratedContent",
                "relationshipType": "OWNER"
            }],
            "supportedUploadMechanism": ["SYNCHRONOUS_UPLOAD"]
        }
    }

    r = requests.post("https://api.linkedin.com/v2/assets?action=registerUpload", headers=headers, json=register_data)
    if r.status_code != 201:
        print(f"[ERROR] Failed to register upload: {r.text}")
        return None, None

    upload_response = r.json()
    upload_url = upload_response['value']['uploadMechanism']['com.linkedin.digitalmedia.uploading.MediaUploadHttpRequest']['uploadUrl']
    asset_urn = upload_response['value']['asset']

    # Upload the file
    with open(filepath, 'rb') as f:
        upload_headers = {
            "Authorization": f"Bearer {token}",
            "Content-Type": content_type
        }
        upload_r = requests.put(upload_url, headers=upload_headers, data=f)
        if upload_r.status_code != 201:
            print(f"[ERROR] Failed to upload file: {upload_r.text}")
            return None, None

    return asset_urn, category

# Publish UGC Post
def publish_ugc_post(asset_urn=None):
    token = get_valid_token()
    if not token:
        return False

    headers = {
        "Authorization": f"Bearer {token}",
        "Content-Type": "application/json",
        "X-Restli-Protocol-Version": "2.0.0"
    }

    data = {
        "lifecycleState": "PUBLISHED",
        "specificContent": {
            "com.linkedin.ugc.ShareContent": {
                "shareCommentary": {
                    "text": "Test post from script"
                },
                "shareMediaCategory": "IMAGE" if asset_urn else "NONE",
                "media": [{
                    "status": "READY",
                    "media": asset_urn
                }] if asset_urn else []
            }
        },
        "visibility": {
            "com.linkedin.ugc.MemberNetworkVisibility": "PUBLIC"
        }
    }

    r = requests.post("https://api.linkedin.com/v2/ugcPosts", headers=headers, json=data)
    print(f"Post Status Code: {r.status_code}")
    print(f"Post Response: {r.text}")
    return r.status_code == 201

# Example usage
if __name__ == "__main__":
    # Replace with your image path
    asset_urn, category = upload_media_to_linkedin("test.png")
    if asset_urn:
        print(f"[INFO] Asset URN: {asset_urn}")
        publish_ugc_post(asset_urn)
    else:
        print("[ERROR] Failed to upload media")

有没有大佬遇到过类似的情况?我翻了好几遍LinkedIn的官方文档,也没发现自己漏了什么必填参数,图片上传明明成功了,怎么一发动态就500呢?会不会是UGC Post的payload里有什么文档没写的隐藏要求,或者我在图片上传的步骤里忽略了某个细节?

内容来源于stack exchange

火山引擎 最新活动