Snapchat MEDIA_EXPIRED: Story media dies 24 hours after creation
Snap Public Profile Story media objects expire 24 hours after creation, which causes MEDIA_EXPIRED. Generate with Sume, then upload and post in one run.

If a Snapchat Story post returns MEDIA_EXPIRED, the media object you created is more than 24 hours old. Snap's Public Profile docs say the Story media object expires 24 hours after creation, so upload the finished clip and post the Story in the same run, and create a fresh media object when you retry a day later.
What does Snap say about the expiry?
On the Profile Asset Management page, the Stories section lists the video constraints (MP4, 5 to 60 seconds, at least 540x960) and states that the media object expires 24 hours after creation. The Story post endpoint is POST https://businessapi.snapchat.com/v1/public_profiles/{profile_id}/stories, and its result status is SUCCESS or ERROR. Two named error codes appear: MEDIA_EXPIRED and MEDIA_POSTING_ALREADY_IN_PROGRESS.
| Error code | Likely cause | Fix |
|---|---|---|
| MEDIA_EXPIRED | Media object older than 24 hours | Create a new media object and post again |
| MEDIA_POSTING_ALREADY_IN_PROGRESS | A post for that media is already running | Wait for the first result instead of re-posting |
| ERROR status | Post failed | Read the error detail and re-check the clip |
Where does a Sume avatar job fit in the 24 hours?
The clock starts when you create the Snap media object, not when Sume renders the clip. Sume jobs queue first: the jobs docs call queued a normal accepted state, with workspace concurrency applied when workers move jobs into processing. So wait until the job is completed, then start the Snap upload.
A pipeline that creates the Snap media object first and renders the avatar clip afterwards can burn the 24 hours in a queue. Order matters: generate, probe, upload, post.
A safe order of operations
- Submit the avatar video and keep the job id. Use an
Idempotency-Keyso a retry cannot create a second billed render. - Poll
GET /v1/jobs/:id/statusor use a webhook untilcompleted, then readGET /v1/jobs/:id/resultfor themedia.sume.comvideo URL. - Check duration and resolution (at least 540x960; Sume avatar output is 720p in the default 9:16).
- Encrypt, upload and finalize the file on Snap's side, then post the Story immediately.
curl https://api.sume.com/v1/jobs/job_123/status \
-H "Authorization: Bearer $SUME_API_KEY"
curl https://api.sume.com/v1/jobs/job_123/result \
-H "Authorization: Bearer $SUME_API_KEY"What if the post is late anyway?
Do not regenerate the avatar clip. Re-read the stored job result, and create a new Snap media object from the same file. Regeneration is billed again; a second upload is not a Sume cost. Also do not double-post while MEDIA_POSTING_ALREADY_IN_PROGRESS is showing, because that code means the first post has not finished.
Sume does not publish to Snapchat in the docs I read, so scheduling and retry timing live in your own code.
Sources
Related posts
More in Integrations
- Supabase Edge Function 504 at 150 s idle: Sume sync stays under it
A Supabase Edge Function that sends no response in 150 seconds returns 504. Sume sync waits at most 30 seconds: a bounded wait, then poll.
- Supabase Queues visibility timeout as a Sume job poller
Supabase Queues (pgmq) hide a read message for a visibility timeout, which fits polling a Sume job: read, check status, archive on terminal, else it reappears.
- TikTok max_video_post_duration_sec: trim before Direct Post
TikTok's creator_info endpoint returns max_video_post_duration_sec, a per-creator limit. Read it first, then cut the clip to fit with Sume video trim.
- TikTok privacy_level_options: public vs private account values
TikTok creator_info returns different privacy_level_options for public and private accounts, and Direct Post privacy_level must match one. Values for each.
Written by Sume