LinkedIn video post stuck in processing: two status ladders to poll

LinkedIn tracks a video asset (PROCESSING, AVAILABLE) and a post (PUBLISH_REQUESTED, PUBLISHED) separately. Poll both, then compare with a Sume job poll.

5 min readSume
All posts

A LinkedIn video that never appears has two places to look, because the API keeps two separate status fields: the video asset has a status, and the post has a lifecycleState. The asset must reach AVAILABLE before the post can serve it. The post is created as PUBLISHED, but a later read can return PUBLISH_REQUESTED while processing finishes, or PUBLISH_FAILED if processing did not succeed.

LinkedIn video asset status and post lifecycleState (read 2026-10-13)
FieldValueMeaning on the page
Video statusWAITING_UPLOADWaiting for the client to finish uploading
Video statusPROCESSINGGenerating missing artifacts
Video statusPROCESSING_FAILEDClient error such as file too large or unsupported format, or an internal error
Video statusAVAILABLEAll required artifacts are ready
Post lifecycleStatePUBLISH_REQUESTEDSubmitted, not ready to render; published asynchronously
Post lifecycleStatePUBLISH_FAILEDNot processed; an edit is needed to retry
Post lifecycleStateDRAFTVisible to the author only
Post lifecycleStatePUBLISHEDVisible to all; the only value accepted on create

What to read when it stalls

On the video, GET /rest/videos/{urn} returns status, and processingFailureReason appears only when the status is PROCESSING_FAILED. On the post, the Posts API returns the published version by default (viewContext is READER). Add viewContext=AUTHOR to see the latest unpublished version, which can be PUBLISHED, DRAFT or PROCESSING.

Two upload errors are worth separating from processing: EXPIRED_UPLOAD_URL (400) and a 401 on an expired upload URL. The Videos API says upload URLs typically expire 30 days after initialization, and that finalizing needs the part ETags in upload order.

The same loop on the Sume side

The render that feeds LinkedIn is also asynchronous. A Sume job moves through queued, processing and then a terminal completed, failed or canceled; the jobs guide says to use exponential backoff and never to resubmit a paid request just because your own process timed out. Only after completed should you fetch the result and start the LinkedIn upload, so a LinkedIn PROCESSING_FAILED never gets blamed on a render that was still running.

The script below polls a Sume job until it reaches a terminal state. Set SUME_API_KEY and pass the job id.

import json, os, sys, time, urllib.request

def status(job_id):
    req = urllib.request.Request(
        f"https://api.sume.com/v1/jobs/{job_id}/status",
        headers={"Authorization": f"Bearer {os.environ['SUME_API_KEY']}"})
    with urllib.request.urlopen(req, timeout=30) as r:
        return json.load(r)

def main():
    delay = 2
    while True:
        body = status(sys.argv[1])
        print(body.get("status"))
        if body.get("status") in ("completed", "failed", "canceled"):
            return
        time.sleep(delay)
        delay = min(delay * 2, 30)

main()

Sources

Related posts

More in Developers

All Developers posts

Written by Sume