Glossary

The Instagram Resumable Upload Session and the rupload Host

Taras Shynkarenko
Taras Shynkarenko
Updated: 9 min read
The Instagram resumable upload session and the rupload hostThe Instagram resumable upload session and the rupload host

TL;DR, Quick Answer

9 min read

Adding upload_type=resumable to POST /<IG_ID>/media returns an id and a uri on rupload.facebook.com instead of fetching your video by URL. The upload itself is a POST to that host with Authorization OAuth, offset and file_size headers, or a file_url header for a hosted file. Meta restricts the flow to apps using Facebook Login for Business, publishes no size threshold that makes it mandatory, and documents no way to resume a broken transfer.

What is an Instagram resumable upload?

Meta calls it an Instagram resumable upload, and it is a two host flow: POST /<IG_ID>/media with upload_type=resumable creates the container on graph.facebook.com and hands back an upload target, then the bytes go to rupload.facebook.com in a second request that you control.

The default is the opposite arrangement. In a standard upload you pass video_url and Meta fetches the file from you: "We will cURL your image using the passed in URL so it must be on a public server." Your server answers a request from Meta's crawler, and the transfer succeeds or fails somewhere you cannot see. Resumable inverts that. You open the connection, you send the bytes, and you get a response about them.

Meta's stated reason for the flow appears on the Content Publishing guide's endpoint list, typo and all: "upload_type=resumable Create a resumbable upload session to upload large videos from an area with frequent network interruptions or other transmission failures."

The parameter itself is documented as optional and case sensitive on the media reference: "An optional parameter for users want to upload video through the rupload protocol, values can be set to lowercase string value: resumable." Lowercase matters. RESUMABLE is not a documented value.

Standard upload vs resumable upload
Standard upload
  • You pass a video_url and wait for Meta to fetch it
  • Your server answers a request from Meta's crawler
  • The transfer succeeds or fails somewhere you cannot see
Resumable upload
  • You open the connection to rupload.facebook.com yourself
  • You send the bytes and set the offset and file_size headers
  • You get the response about the upload directly
Resumable upload puts you on the sending end of the connection instead of waiting on Meta's fetch.

What does each step of the flow send?

Four steps, two hosts, and a different authorization scheme on each one.

StepHostRequest
1. Open the sessiongraph.facebook.comPOST /<IG_USER_ID>/media with media_type, upload_type=resumable, access_token
2. Send the bytesrupload.facebook.comPOST /ig-api-upload/<API_VERSION>/<IG_CONTAINER_ID> with Authorization, offset, file_size
3. Check the containergraph.facebook.comGET /<IG_CONTAINER_ID>?fields=status_code
4. Publishgraph.facebook.comPOST /<IG_ID>/media_publish with creation_id

Step one differs from a standard container creation in what it leaves out. There is no video_url, because there is nothing for Meta to fetch yet. A reel session is the full shape:

POST https://graph.facebook.com/v25.0/<YOUR_APP_USERS_INSTAGRAM_USER_ID>/media
  ?media_type=REELS
  &upload_type=resumable
  &caption=<IMAGE_CAPTION>
  &collaborators=<COLLABORATOR_USERNAMES>
  &cover_url=<COVER_URL>
  &audio_name=<AUDIO_NAME>
  &location_id=<LOCATION_PAGE_ID>
  &thumb_offset=<THUMB_OFFSET>
  &access_token=<USER_ACCESS_TOKEN>

The response carries a second field that a standard container does not return:

{
  "id": "<IG_CONTAINER_ID>",
  "uri": "https://rupload.facebook.com/ig-api-upload/v25.0/<IG_CONTAINER_ID>"
}

Use the uri Meta hands you rather than assembling the path yourself. Step two then posts the file to it:

curl -X POST "https://rupload.facebook.com/ig-api-upload/v25.0/<IG_CONTAINER_ID>" \
  -H "Authorization: OAuth <USER_ACCESS_TOKEN>" \
  -H "offset: 0" \
  -H "file_size: Your_file_size_in_bytes" \
  --data-binary "@Your_local_file_path.extension"

Meta documents the two numeric headers in one line each. "offset is set to the first byte being upload, generally 0." "file_size is set to the size of your file in bytes." A hosted file skips the body entirely and moves the source into a third header instead:

curl -X POST "https://rupload.facebook.com/ig-api-upload/v25.0/<IG_CONTAINER_ID>" \
  -H "Authorization: OAuth <USER_ACCESS_TOKEN>" \
  -H "file_url: <VIDEO_URL>"

Success is two fields: {"success":true,"message":"Upload successful."}.

Why is the authorization header different on rupload?

Because Meta writes it differently on each host, and the examples are side by side on the same page. Calls to graph.facebook.com in the Content Publishing guide use -H "Authorization: Bearer <ACCESS_TOKEN>". Every call to rupload.facebook.com on that same page uses -H "Authorization: OAuth <ACCESS_TOKEN>".

Same token, different scheme name. Meta gives no explanation, and the rupload examples never show Bearer. Copy the scheme from the example that matches the host you are calling.

Two more details are easy to lose in the code blocks. Meta's published curl for the rupload step contains a stray backtick inside the URL, sitting between the container ID placeholder and the closing quote. It is a typo in the doc, not a piece of syntax. And the Content Publishing guide's own parameter list for the upload step stops mid sentence: it announces "the following parameters", prints access_token, then ends on an empty bullet. The complete header list only exists on the media endpoint reference, not in the guide.

When is resumable required rather than optional?

Meta never publishes a file size that makes it mandatory, and the honest answer is that the only hard requirement is about your login flow, not your file.

The Content Publishing guide restricts the whole flow in one clause: upload_type=resumable is "Only for apps that have implemented Facebook Login for Business." The requirements table on the same page backs that up by listing host URLs per login type. Instagram API with Instagram Login gets graph.instagram.com. Instagram API with Facebook Login gets graph.facebook.com and rupload.facebook.com, annotated "(For resumable video uploads)".

AdaptlyPost
AdaptlyPost

Start 7-Day FREE Trial

All-platform analytics

Social Inbox

AI-powered assistant

So an app built on Business Login for Instagram cannot use resumable at all. Video has to arrive through video_url and Meta's fetch. That is a login architecture decision made long before anyone uploads a file, and it is not reversible per request.

Where resumable is available, Meta's guidance is qualitative rather than numeric. The trigger is "large videos from an area with frequent network interruptions or other transmission failures", with no byte threshold attached. The only related numbers Meta does publish are the specification ceilings: reels cap at 300 MB and 15 minutes, stories at 100 MB and 60 seconds. Neither is described as a resumable threshold.

Practical reading: use resumable whenever your source file is local rather than already sitting on a CDN, because the alternative requires you to host the file publicly for the duration of Meta's fetch. That matters most for anything long, which is where uploading long form video to Instagram gets awkward and where the reel length limits Meta has been testing push file sizes up.

A phone mid-upload with a weak signal icon, the situation resumable uploads are built to survive.

How do you actually resume an interrupted upload?

Meta does not document it. That is the largest gap in the feature, and it sits directly under the feature's name.

The offset header is the only mechanism that hints at partial transfers, and both places Meta describes it point at the same value: "offset is set to the first byte being upload, generally 0." There is no documented chunk size, no endpoint that reports how many bytes the server already holds, no second request shape for continuing a broken transfer, and no example anywhere in the Instagram Platform docs that passes a non zero offset. A "resumable" upload session, as published, is a single POST of the whole file with an offset field that is always zero in the examples.

What Meta does give you is a container that survives a failure long enough to retry from the beginning. Containers expire after 24 hours, and an account can create 400 of them in a rolling 24 hour period. A failed byte transfer costs one container out of that 400, not one publish out of your daily quota, so restarting is cheap in the budget that matters. Anything queued in advance still has to respect the same ceilings, which is why scheduled Instagram posts fail at the container stage more often than at the publish stage.

What does a failed upload look like?

A failure on the rupload host does not come back as a standard Graph API error object. It comes back as a debug_info envelope with the real error stringified inside it:

{
  "debug_info": {
    "retriable": false,
    "type": "ProcessingFailedError",
    "message": "{\"success\":false,\"error\":{\"message\":\"unauthorized user request\"}}"
  }
}

Parse retriable first. It is the field that tells you whether a retry is worth the container. false means the transfer will fail the same way again, and the example Meta chose, an unauthorized user request, is exactly that kind of failure.

Failures that surface later, on the Graph API side, use the normal code and subcode pairs.

SymptomCodeSubcodeMessage
Upload failed for no stated reason-12207053unknown upload error
Container expired before publish-22207020The media you are trying to access has expired. Please try to upload again.
Container not found at publish242207008The media builder with creation id = {creation-id} does not exist or has been expired.
Published too early90072207027The media is not ready for publishing, please wait for a moment
Video format rejected3522207026The video format is not supported. Please check spec for supported {video} format

Meta scopes 2207053 to this flow specifically: "An unknown error occured during upload. Generate a new container and use it to try again. This should only affect video uploads."

A status dashboard on a server room screen, the kind of view a polling loop checks for a result.

Which errors show up after the upload succeeds?

The ones that come from processing, and you only see them by polling. Meta is explicit that a container ID proves nothing: "Video uploads are asynchronous, so receiving a container ID does not guarantee that the upload was successful."

GET /<IG_CONTAINER_ID>?fields=status_code returns one of five values.

status_codeMeaning Meta publishes
IN_PROGRESSThe container is still in the publishing process
FINISHEDThe container and its media object are ready to be published
ERRORThe container failed to complete the publishing process
EXPIREDThe container was not published within 24 hours and has expired
PUBLISHEDThe container's media object has been published

Only FINISHED is safe to publish. Requesting the status field alongside status_code is worth the extra parameter, because Meta defines it as the detail line: "If status_code is ERROR, this value will be an error subcode."

AdaptlyPost
AdaptlyPost

Start 7-Day FREE Trial

All-platform analytics

Social Inbox

AI-powered assistant

Meta caps the polling advice at a specific cadence: "We recommend querying a container's status once per minute, for no more than 5 minutes." A long reel can still be processing after that, and Meta does not say what to do next. The workable pattern is to keep polling at a slower interval until the container either finishes or hits the 24 hour expiry, which is also how scheduling reels through the API has to be built and how story containers behave on their own 24 hour clock.

Frequently asked questions

What does upload_type=resumable do on the Instagram API?

It creates a container that expects you to push the video to rupload.facebook.com yourself, instead of having Meta fetch it from a video_url you host. The response includes both an id and a uri pointing at the upload host.

Which host handles Instagram resumable uploads?

rupload.facebook.com, at the path /ig-api-upload/<API_VERSION>/<IG_CONTAINER_ID>. Container creation, status checks and publishing all stay on graph.facebook.com.

Which headers does the rupload request need?

Authorization: OAuth <ACCESS_TOKEN> plus either offset and file_size for a local file, or file_url for a file already hosted publicly. Note that the rupload examples use OAuth where the Graph API examples use Bearer.

Can apps using Instagram Login use resumable uploads?

No. Meta restricts upload_type=resumable to apps that have implemented Facebook Login for Business, and lists rupload.facebook.com only under that login type.

At what file size does Instagram require a resumable upload?

Meta publishes no such threshold. The guide recommends resumable for "large videos from an area with frequent network interruptions" without naming a size, and documents the parameter itself as optional.

How do you resume a broken Instagram upload?

Meta does not document a resume procedure. The offset header exists but every published example sets it to 0, and no endpoint reports how many bytes the server already received. Generate a new container and start again.

How long does an Instagram upload container stay valid?

A container expires 24 hours after you create it, whether you published it or not. Miss that window and you get code -2, subcode 2207020, with the message that the media you are trying to access has expired and you need to try uploading again. The status_code field reports EXPIRED for the same reason.

How often should you poll an Instagram container's status?

Meta caps its own advice at once per minute for no more than five minutes. A long reel can still show IN_PROGRESS after that window, and Meta does not document what to do next. The practical answer is to keep polling at a slower interval until the container either reaches FINISHED or hits its 24 hour expiry.

What does the retriable field in a rupload error mean?

It is the first field to check in the debug_info envelope rupload returns on failure. A value of false means the transfer will fail the same way again if you resend it, exactly as in Meta's own example of an unauthorized user request. Checking retriable before you retry saves you from spending another container on a request that cannot succeed.

Does a container ID mean the Instagram upload succeeded?

A container ID alone proves nothing. Meta states plainly that video uploads are asynchronous, so receiving a container ID does not guarantee the upload was successful. The only way to know is polling the container with GET /<IG_CONTAINER_ID>?fields=status_code and waiting for FINISHED before you publish.

Was This Article Helpful?

Let us know what you think!

See us more often in Google

One click marks AdaptlyPost as a preferred source, so our articles sit higher in your Top Stories, AI Mode, and AI Overviews.

Before you go...

AdaptlyPost

AdaptlyPost

Schedule your content across all platforms

Manage all your social media accounts in one place with AdaptlyPost.

All-platform analytics

Social Inbox

AI-powered assistant

Related Glossary Terms

Related Articles