Send one photo or a carousel as multipart/form-data, in order, with the profile in user and each network in a repeated platform[] field. Each network gets the photos in the form it takes, and the answer has one result per network.
A first carousel
curl -X POST https://api.reelspan.com/v1/posts/photos \
-H "Authorization: Bearer $REELSPAN_API_KEY" \
-H "Idempotency-Key: spring-range-01" \
-F "user=my-brand" \
-F "platform[]=instagram" \
-F "platform[]=x" \
-F "platform[]=linkedin" \
-F "title=The spring range is here" \
-F "photos[][email protected]" \
-F "photos[][email protected]" \
-F "alt_text[]=A red mug on a wooden table" \
-F "alt_text[]=The same mug in blue"import { Reelspan } from 'reelspan';
import { readFile } from 'node:fs/promises';
const reelspan = new Reelspan();
const post = await reelspan.posts.photos({
user: 'my-brand',
platforms: ['instagram', 'x', 'linkedin'],
title: 'The spring range is here',
photos: [
{ data: await readFile('photo1.jpg'), name: 'photo1.jpg', type: 'image/jpeg' },
{ data: await readFile('photo2.jpg'), name: 'photo2.jpg', type: 'image/jpeg' },
],
altTexts: ['A red mug on a wooden table', 'The same mug in blue'],
});from reelspan import Reelspan
reelspan = Reelspan()
post = reelspan.posts.photos(
"my-brand",
["instagram", "x", "linkedin"],
["photo1.jpg", "photo2.jpg"],
"The spring range is here",
alt_texts=["A red mug on a wooden table", "The same mug in blue"],
)The answer is the same as for a video: one result per network under results, or a request_id when it runs on in the background. See Post status.
Where the photos come from
photos[] takes files, public https URLs, or both. You can also pass photo_media_ids[], files you uploaded earlier, to post the same photos again without sending them twice.
- Up to 35 items in all. Extra files are dropped with a warning.
- Each image can be up to 20 MB. Supported images: JPEG, PNG, WebP, GIF and HEIC.
- When you mix sources, the carousel order is: uploaded files, then
photo_media_ids[], then URLs, each in the order you sent them. - At least one photo is required; without one the answer is 400
MEDIA_REQUIRED. - One request can fetch at most 100 URLs, counting every network's own photo URLs too.
curl -X POST https://api.reelspan.com/v1/posts/photos \
-H "Authorization: Bearer $REELSPAN_API_KEY" \
-F "user=my-brand" \
-F "platform[]=threads" \
-F "title=From the shoot" \
-F "photos[]=https://example.com/photo1.jpg" \
-F "photos[]=https://example.com/photo2.jpg"await reelspan.posts.photos({
user: 'my-brand',
platforms: ['threads'],
title: 'From the shoot',
photos: ['https://example.com/photo1.jpg', 'https://example.com/photo2.jpg'],
});reelspan.posts.photos("my-brand", ["threads"], ["https://example.com/photo1.jpg", "https://example.com/photo2.jpg"], "From the shoot")Which networks take photos
Instagram, Facebook, TikTok, X, Threads, LinkedIn, Pinterest, Bluesky, Reddit, Google Business, Telegram, Discord, Slack, Mastodon, Nostr, Lemmy, WordPress, Whop and Listmonk. YouTube, Dev.to and Hashnode don't: naming one gets 400 UNSUPPORTED_POST_TYPE.
How many photos each network shows, and the sizes and shapes it accepts, are on Photo requirements.
Videos in a carousel
Instagram and Threads take videos and photos mixed in one carousel: put the video in photos[] (or its id in photo_media_ids[]) where it should appear. Every other network gets the photos only, and its result carries a warning that the videos were left out. A network left with no photos at all fails with media_required.
A different set per network
A network can get its own photos instead of photos[], for example a square crop for one network:
{network}_photos[]: public URLs, such aspinterest_photos[].{network}_photo_media_ids[]: uploaded files, such asinstagram_photo_media_ids[].
curl -X POST https://api.reelspan.com/v1/posts/photos \
-H "Authorization: Bearer $REELSPAN_API_KEY" \
-F "user=my-brand" \
-F "platform[]=instagram" -F "platform[]=pinterest" \
-F "title=New in" \
-F "pinterest_board_id=1234567890" \
-F "photos[][email protected]" \
-F "pinterest_photos[]=https://example.com/photo1-2x3.jpg"Alt text
alt_text[] gives one text per photo, in order. A single alt_text applies to every photo, and a JSON array in alt_text works too. {network}_alt_text overrides it for one network (for example x_alt_text). Alt text is sent to X, Instagram, Facebook, Threads, LinkedIn, Pinterest and Bluesky.
Captions
title is the caption on every network. {network}_title replaces it on one network (instagram_title, x_title, linkedin_title…), and {network}_description sets the longer text where a network has one. A long caption on X or Bluesky is posted as a thread.
Network settings
The fields below are the ones made for photo posts. Every network's full list is on its page under Networks.
| Network | Field | What it does |
|---|---|---|
media_type=STORIES |
Shares each photo as a Story instead of a feed post. | |
facebook_media_type=STORIES |
Shares each photo as its own Page Story. | |
| TikTok | privacy_level |
Who can see it, as for videos. |
| TikTok | photo_cover_index |
Which photo (from 0) is the cover. |
| TikTok | auto_add_music |
true lets TikTok add music. |
| TikTok | tiktok_description |
The post's description, up to 4,000 characters. Its title is the first 90 characters of the caption. |
pinterest_board_id |
The board to pin to. Required. | |
reddit_subreddit |
The community to post in. Required. |
Instagram takes photos from 4:5 to 1.91:1 and converts non-JPEG photos for you. A Pinterest carousel needs every photo in the same shape; use
pinterest_photos[]to send it its own crops.
Later, queued or repeated
The same as for a video: scheduled_date with timezone, add_to_queue=true, or repeat_every with repeat_times. A scheduled answer is 202 with a job_id. See Scheduled posts, Queue and Scheduling and repeats.