Skip to main content

Upload Photos

Upload photos (and mixed media for supported platforms) to various social media platforms using this endpoint.

Endpoint

POST /api/upload_photos

Headers

NameValueDescription
AuthorizationApikey your-api-key-hereYour API key for authentication

Common Parameters

NameTypeRequiredDescription
userStringYesUser identifier
platform[]ArrayYesPlatform(s) to upload to. Supported values: tiktok, instagram, linkedin, facebook, x, threads, pinterest, bluesky
photos[]ArrayYesArray of files to upload. Accepts photos (jpg, png, etc.).
Note: You can also include videos (mp4, mov, etc.) ONLY for Instagram and Threads mixed carousels.
titleStringYesDefault title/caption of the post
descriptionStringNoOptional extended text used on TikTok photo descriptions, LinkedIn commentary, Facebook descriptions, Pinterest notes, and Reddit bodies. Ignored elsewhere.
scheduled_dateString (ISO-8601)NoOptional date/time (ISO-8601) to schedule publishing, e.g., "2024-12-31T23:45:00Z". Must be in the future (≤ 365 days). Omit for immediate upload.
async_uploadBooleanNoIf true, the request returns immediately with a request_id and processes in the background. See Upload Status.
first_commentStringNoAutomatically post a first comment after publishing. Supported on Instagram, LinkedIn, Facebook, and Bluesky. On X (Twitter) and Threads, this creates a reply to the main post. For X threads, the comment is posted as a reply to the last tweet in the thread.

Important: If you set async_upload to false but the upload takes longer than 59 seconds, it will automatically switch to asynchronous processing to avoid timeouts. In that case, use the request_id with the Upload Status endpoint to check the upload status and result.

Scheduling behavior: When you provide scheduled_date, the API responds with 202 Accepted and includes a job_id. That same job_id will later appear in Upload History to correlate the scheduled job with the publish record.

Video Support (Mixed Carousels):

  • Instagram & Threads: You can upload videos in the photos[] array to create mixed carousels (photos + videos).
  • All other platforms (Facebook, TikTok, LinkedIn, X, Pinterest): Do NOT upload videos to this endpoint. Use the Upload Video endpoint instead. Uploading videos here for these platforms will result in an error.

Platform-Specific Titles

The title parameter serves as a fallback. To set a custom title for a particular platform, use the optional [platform]_title parameter. If provided, it will override the main title for that platform.

Example Optional Parameters:

  • instagram_title: "Check out my latest reel on Instagram! #reels"
  • facebook_title: "Excited to share this new video with my Facebook friends and family."
  • tiktok_title: "New TikTok video just dropped! 🔥"
  • linkedin_title: "A professional insight on the latest industry trends, discussed in this video."
  • x_title: "New video out now! 📢"

Platform-Specific Parameters

LinkedIn

NameTypeRequiredDescriptionDefault
linkedin_titleStringNoSpecific title for the LinkedIn post. Fallbacks to title.title
linkedin_description or descriptionStringNoSent as the post commentary. If omitted, we reuse title.title
visibilityStringNoVisibility setting for the post (accepted value: "PUBLIC")PUBLIC
target_linkedin_page_idStringNoLinkedIn page ID to upload photos to an organization"107579166"

Facebook

NameTypeRequiredDescriptionDefault
facebook_titleStringNoSpecific title for the Facebook post. Fallbacks to title.title
facebook_page_idStringYesFacebook Page ID where the photos will be posted-
facebook_media_typeStringNoType of media ("POSTS" or "STORIES")"POSTS"

Note: The caption is applied only to the first photo uploaded. For correct posting on Facebook, ensure the Page is directly associated with your personal profile and not managed through a Business Portfolio.

Note: If facebook_page_id is not provided, we will automatically use the user's only connected Page (if exactly one exists). If multiple Pages are connected, the API returns a helpful error with an available_pages list so you can choose one. Posting to personal Facebook profiles via API is not supported by Meta; only Pages can be posted to.

X (Twitter)

NameTypeRequiredDescriptionDefault
x_titleStringNoSpecific title for the tweet. Fallbacks to title.title
x_long_text_as_postBooleanNoWhen true, publishes long text as a single post. Otherwise, creates a thread.false
reply_settingsStringNoControls who can reply to the tweet ("following", "mentionedUsers", "subscribers", "verified")-
geo_place_idStringNoPlace ID for adding geographic location to the tweet-
nullcastBooleanNoWhether to publish without broadcasting (promotional/promoted-only posts)false
for_super_followers_onlyBooleanNoTweet exclusive for super followersfalse
community_idStringNoCommunity ID for posting to specific communities-
share_with_followersBooleanNoShare community post with followersfalse
direct_message_deep_linkStringNoLink to take the conversation from public timeline to private Direct Message-
tagged_user_idsArrayNoArray of user IDs to tag in the photos (max 10 users)[]
reply_to_idStringNoID of the tweet to reply to. Creates a reply to the specified tweet.-
exclude_reply_user_idsArrayNoArray of user IDs to exclude from replying to this tweet. Requires reply_to_id.[]

Note: For Twitter uploads, specify the platform as "x" in the platform[] array.

The global description field is ignored for X photo uploads.

How X (Twitter) Thread Creation Works (Advanced Logic)

Note: The following describes the default thread creation logic. To override this and post long text as a single post, set the x_long_text_as_post parameter to true.

The system is engineered to create well-formatted, natural-looking threads on X (formerly Twitter). Instead of simply splitting text at every line break, it intelligently groups paragraphs to create more readable tweets.

Here's the step-by-step logic:

Intelligent Paragraph Grouping (Primary Method):

The function first identifies distinct paragraphs (any text separated by a blank line). It then combines as many of these paragraphs as possible into a single tweet, filling it up to the 280-character limit without exceeding it. The double newline (\n\n) between combined paragraphs is preserved for formatting. This results in fewer, more substantial tweets that flow naturally, just as if a person had written them.

Handling Exceptionally Long Paragraphs:

If a single paragraph is, by itself, longer than the 280-character limit, a more granular splitting logic is automatically triggered for that paragraph only:

  • Split by Line Break: The system first attempts to break the paragraph down by its individual line breaks (\n).
  • Split by Word: If any of those single lines are still too long, it will split them by words as a final resort.

Media Attachment:

For posts that include photos or videos, all media is attached only to the first tweet of the thread. The subsequent tweets in the thread will be text-only replies.

TikTok

NameTypeRequiredDescriptionDefault
tiktok_titleStringNoSpecific title for the TikTok post. Fallbacks to title.title
post_modeStringNoControls how the upload is handled. DIRECT_POST publishes immediately; MEDIA_UPLOAD sends the media to the TikTok inbox so users can finish editing in-app.DIRECT_POST
privacy_levelStringNoAccepted values: PUBLIC_TO_EVERYONE, MUTUAL_FOLLOW_FRIENDS, FOLLOWER_OF_CREATOR, SELF_ONLY.PUBLIC_TO_EVERYONE
auto_add_musicBooleanNoAutomatically add background music to photosfalse
disable_commentBooleanNoDisable comments on the postfalse
brand_content_toggleBooleanNoSet to true for paid partnerships that promote third-party brands.false
brand_organic_toggleBooleanNoSet to true when promoting the creator's own business.false
photo_cover_indexIntegerNoIndex (starting at 0) of the photo to use as the cover/thumbnail for the TikTok photo post0
tiktok_description or descriptionStringNoFor photo posts, used as description inside post_info.title

Instagram

NameTypeRequiredDescriptionDefault
instagram_titleStringNoSpecific title for the Instagram post. Fallbacks to title.title
media_typeStringNoType of media ("IMAGE" or "STORIES"). Automatically handles CAROUSEL/REELS logic if mixed media is detected."IMAGE"

The global description field is ignored for Instagram uploads (title serves as caption).

Threads

NameTypeRequiredDescriptionDefault
threads_titleStringNoSpecific title for the Threads post. Fallbacks to title.title

The global description field is ignored for Threads photo uploads.

Pinterest

NameTypeRequiredDescriptionDefault
pinterest_titleStringNoSpecific title for the Pinterest Pin. Fallbacks to title.title
pinterest_description or descriptionStringNoPopulates the Pin description. If omitted, we reuse title.title
pinterest_board_idStringYesPinterest board ID to publish the photo to.-
pinterest_alt_textStringNoAlt text for the image.-
pinterest_linkStringNoDestination link for the photo Pin.-

Bluesky

NameTypeRequiredDescriptionDefault
bluesky_titleStringNoSpecific text for the Bluesky post. Fallbacks to title.title

Note: Bluesky supports up to 4 images per post.

Example Requests

curl \
-H 'Authorization: Apikey your-api-key-here' \
-F 'photos[]=@/path/to/image.jpg' \
-F 'photos[]=@/path/to/video.mp4' \
-F 'user="test"' \
-F 'platform[]=instagram' \
-F 'title="My Mixed Carousel"' \
-X POST https://api.upload-post.com/api/upload_photos

Upload Photos to Facebook

curl \
-H 'Authorization: Apikey your-api-key-here' \
-F 'photos[]=@/path/to/image1.jpg' \
-F 'photos[]=@/path/to/image2.jpg' \
-F 'user="test"' \
-F 'platform[]=facebook' \
-F 'facebook_page_id="123456789"' \
-F 'title="My Photo Album"' \
-X POST https://api.upload-post.com/api/upload_photos

Responses

  • 200 OK (synchronous, finished fast)
{
"success": true,
"results": {
"instagram": { "success": true, "url": "https://instagram.com/p/...", "photos_were_processed": true, "changes_per_image": [ {} ] },
"reddit": { "success": false, "error": "Subreddit is required for photo posts to Reddit." }
},
"usage": { "count": 13, "limit": 100, "last_reset": "..." }
}
  • 200 OK (asynchronous/background started or sync→background fallback)
{
"success": true,
"message": "Upload initiated successfully in background.",
"request_id": "1a2b3c4d5e...",
"total_platforms": 2
}
  • 202 Accepted (scheduled)
{
"success": true,
"job_id": "scheduler_job_456",
"scheduled_date": "2025-09-22T10:00:00Z"
}
  • 400 Bad Request

    • Missing user, platform[], Pinterest without pinterest_board_id, Reddit without subreddit, invalid platforms, invalid scheduled_date.
  • 401 Unauthorized: { "success": false, "message": "Invalid or expired token" }

  • 403 Forbidden (plan restrictions)

  • 404 Not Found (e.g., user not found)

  • 429 Too Many Requests (monthly limit exceeded; includes current usage)

{
"success": false,
"message": "This upload would exceed your monthly limit.",
"usage": { "count": 10, "limit": 10, "last_reset": "..." }
}
  • 500 Internal Server Error: { "success": false, "error": "Detailed error message" }

Notes

  • When async or when sync falls back to background, use GET /api/uploadposts/status?request_id={request_id} to poll progress.
  • Per-platform results may include fields like url, post_id(s), and platform-specific metadata or error.