Overview
OpenShorts integrates with Upload-Post API to distribute your viral clips to TikTok, Instagram Reels, and YouTube Shorts with a single API call. The system supports async uploads , platform-specific metadata, and user profile selection.
Upload-Post API Integration
The /api/social/post endpoint handles multi-platform distribution:
class SocialPostRequest ( BaseModel ):
job_id: str
clip_index: int
api_key: str
user_id: str
platforms: List[ str ] # ["tiktok", "instagram", "youtube"]
# Optional overrides
title: Optional[ str ] = None
description: Optional[ str ] = None
scheduled_date: Optional[ str ] = None # ISO-8601 string
timezone: Optional[ str ] = "UTC"
@app.post ( "/api/social/post" )
async def post_to_socials ( req : SocialPostRequest):
# Resolve video file path
clip = job[ 'result' ][ 'clips' ][req.clip_index]
filename = clip[ 'video_url' ].split( '/' )[ - 1 ]
file_path = os.path.join( OUTPUT_DIR , req.job_id, filename)
# Construct Upload-Post API request
url = "https://api.upload-post.com/api/upload"
headers = {
"Authorization" : f "Apikey { req.api_key } "
}
data_payload = {
"user" : req.user_id,
"title" : final_title,
"platform[]" : req.platforms,
"async_upload" : "true" # Enable async upload
}
# Platform-specific parameters
if "tiktok" in req.platforms:
data_payload[ "tiktok_title" ] = final_description
if "instagram" in req.platforms:
data_payload[ "instagram_title" ] = final_description
data_payload[ "media_type" ] = "REELS"
if "youtube" in req.platforms:
data_payload[ "youtube_title" ] = yt_title
data_payload[ "youtube_description" ] = final_description
data_payload[ "privacyStatus" ] = "public"
# Send file
with open (file_path, "rb" ) as f:
file_content = f.read()
files = {
"video" : (filename, file_content, "video/mp4" )
}
with httpx.Client( timeout = 120.0 ) as client:
response = client.post(url, headers = headers, data = data_payload, files = files)
return response.json()
TikTok
data_payload = {
"tiktok_title" : "POV: You discovered the AI workflow that changed everything 🤯 #ai #automation"
}
TikTok titles are limited to 150 characters and support emojis and hashtags.
Instagram Reels
data_payload = {
"instagram_title" : "This AI hack will save you 10 hours/week. Comment WORKFLOW and I'll send it! 🔥" ,
"media_type" : "REELS"
}
Instagram requires media_type: "REELS" to post vertical videos as Reels (not feed posts).
YouTube Shorts
data_payload = {
"youtube_title" : "I Automated My Entire Workflow with AI (10 Hours Saved Per Week)" ,
"youtube_description" : "Full tutorial in bio! #shorts #ai #productivity" ,
"privacyStatus" : "public" # or "private", "unlisted"
}
YouTube Short titles are limited to 100 characters. The system automatically uses video_title_for_youtube_short from Gemini’s analysis.
Async Upload Feature
The async_upload: "true" parameter enables background processing:
data_payload = {
"async_upload" : "true"
}
Benefits :
API returns immediately with upload ID
Large videos don’t timeout the request
You can queue multiple uploads in parallel
Response :
{
"upload_id" : "abc123" ,
"status" : "processing" ,
"platforms" : [ "tiktok" , "instagram" , "youtube" ]
}
User Profile Selection
Before posting, fetch connected social accounts:
@app.get ( "/api/social/user" )
async def get_social_user ( api_key : str = Header( ... , alias = "X-Upload-Post-Key" )):
"""Proxy to fetch user ID from Upload-Post"""
url = "https://api.upload-post.com/api/uploadposts/users"
headers = { "Authorization" : f "Apikey { api_key } " }
async with httpx.AsyncClient( timeout = 30.0 ) as client:
resp = await client.get(url, headers = headers)
data = resp.json()
profiles_list = []
raw_profiles = data.get( 'profiles' , [])
for p in raw_profiles:
username = p.get( 'username' )
socials = p.get( 'social_accounts' , {})
connected = []
# Check which platforms are connected
for platform in [ 'tiktok' , 'instagram' , 'youtube' ]:
account_info = socials.get(platform)
if isinstance (account_info, dict ):
connected.append(platform)
profiles_list.append({
"username" : username,
"connected" : connected
})
return { "profiles" : profiles_list}
Example Response :
{
"profiles" : [
{
"username" : "my_brand" ,
"connected" : [ "tiktok" , "instagram" , "youtube" ]
},
{
"username" : "personal_account" ,
"connected" : [ "instagram" ]
}
]
}
Scheduled Posting
Schedule clips for future publication:
data_payload = {
"scheduled_date" : "2026-03-15T14:30:00" , # ISO-8601 format
"timezone" : "America/New_York"
}
Scheduled posts are useful for consistent content calendars and optimal posting times for each platform.
Full API Example
const response = await fetch ( '/api/social/post' , {
method: 'POST' ,
headers: {
'Content-Type' : 'application/json' ,
'X-Upload-Post-Key' : uploadPostApiKey
},
body: JSON . stringify ({
job_id: 'a7f3c2d1-...' ,
clip_index: 0 ,
api_key: uploadPostApiKey ,
user_id: 'my_brand' ,
platforms: [ 'tiktok' , 'instagram' , 'youtube' ],
title: 'Custom Title Override' ,
description: 'Custom description with CTA! Comment WORKFLOW 👇' ,
scheduled_date: '2026-03-15T14:30:00' ,
timezone: 'America/Los_Angeles'
})
});
const result = await response . json ();
console . log ( 'Upload ID:' , result . upload_id );
import httpx
# From OpenShorts backend
response = httpx.post(
"https://api.upload-post.com/api/upload" ,
headers = { "Authorization" : f "Apikey { api_key } " },
data = {
"user" : user_id,
"platform[]" : [ "tiktok" , "instagram" , "youtube" ],
"title" : "My Viral Clip" ,
"async_upload" : "true" ,
"tiktok_title" : "TikTok-specific caption" ,
"instagram_title" : "Instagram caption with CTA" ,
"youtube_title" : "YouTube Short Title" ,
"youtube_description" : "Description with hashtags" ,
"privacyStatus" : "public"
},
files = {
"video" : ( "clip.mp4" , open (video_path, "rb" ), "video/mp4" )
},
timeout = 120.0
)
print (response.json())
curl -X POST https://api.upload-post.com/api/upload \
-H "Authorization: Apikey YOUR_API_KEY" \
-F "user=my_brand" \
-F "platform[]=tiktok" \
-F "platform[]=instagram" \
-F "platform[]=youtube" \
-F "title=My Viral Clip" \
-F "async_upload=true" \
-F "tiktok_title=POV: You discovered... 🤯" \
-F "instagram_title=This will change your life! Comment WORKFLOW 👇" \
-F "youtube_title=How I Automated Everything with AI" \
-F "youtube_description=Full guide in bio! #shorts #ai" \
-F "privacyStatus=public" \
-F "[email protected] "
If you don’t provide custom titles/descriptions, OpenShorts uses Gemini’s AI-generated metadata:
# Fallbacks from Gemini analysis
final_title = req.title or clip.get( 'title' , 'Viral Short' )
final_description = (
req.description or
clip.get( 'video_description_for_instagram' ) or
clip.get( 'video_description_for_tiktok' ) or
"Check this out!"
)
if "youtube" in req.platforms:
yt_title = (
req.title or
clip.get( 'video_title_for_youtube_short' , final_title)
)
Error Handling
try :
response = client.post(url, headers = headers, data = data_payload, files = files)
if response.status_code not in [ 200 , 201 , 202 ]:
print ( f "❌ Upload-Post Error: { response.text } " )
raise HTTPException(
status_code = response.status_code,
detail = f "Vendor API Error: { response.text } "
)
return response.json()
except Exception as e:
print ( f "❌ Social Post Exception: { e } " )
raise HTTPException( status_code = 500 , detail = str (e))
Viral Detection AI-generated platform-specific metadata
YouTube Studio Full YouTube publishing pipeline