使用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




