Facebook Reels Uploader
Publish a Reel to your Facebook Page or profile from a video URL. A Page token uses Meta's Graph API. Cookies are needed for a profile.
How it works
- 1Open it on Apify
Hit Run on Apify — it opens the tool in the cloud, no install.
- 2Set the inputs
Adjust
videoUrl,caption,reelTitle(sensible defaults are pre-filled). - 3Click Run
The tool runs on Apify’s cloud and collects the data for you.
- 4Export the results
Download as JSON, CSV or Excel, or pipe straight into your app, Google Sheets, or an AI agent.
Pricing
$0.35 per reel published (browser) = $350 per 1,000
| You are charged for | When | Price |
|---|---|---|
| Reel published (browser) | Charged once per Reel confirmed published through the Facebook composer. A run that publishes nothing is never charged this fee, though it is still charged for the megabytes it moved before it stopped, under "Video megabyte". | $0.35 |
| Reel published (Graph API) | Charged once per Reel confirmed published through Meta’s official Graph API. Cheaper than the browser route, and never charged per megabyte. A run that publishes nothing is never charged this fee. | $0.15 |
| Video megabyte | Charged per megabyte of video moved on browser runs, and charged on a confirmed publish and on a failed run alike, because those megabytes move either way. On a confirmed publish it is one event per megabyte of your video. On a run that stopped early it is the traffic actually measured, plus whatever of your file had already been pulled in from your host; that part is charged at what those bytes cost rather than at the per-megabyte rate, so on a small download it often rounds to nothing. Measured bytes only, never an estimate: a leg that cannot be measured is charged as zero. Never more than a successful publish of the same file would have cost, and never past the "maxTotalChargeUsd" you set. Runs that use your own proxy servers, that switch the Apify proxy off, or that go through the Graph API are never charged this. | $0.02 |
Pay-per-event pricing: you are billed per result, not per subscription. Billing is handled by Apify on your own account. These are the live Apify store prices, in effect since 2026-09-22, and they are what you are actually charged.
Inputs
| Field | What it does | Type |
|---|---|---|
videoUrl | Public direct https:// URL to the .mp4 (S3, a CDN, an Apify key-value store, anything that serves the file itself rather than an HTML landing page). Required for a real run; leave the credential fields blank to get a free labelled sample row instead. The clip is checked before anything is transferred: 3 seconds or longer, 540x960 or larger, 24-60 fps, and inside Meta's 16:9-to-9:16 band. Vertical 1080x1920 H.264 + AAC is the format Meta documents. | string |
caption | Reel description; put #hashtags here. Capped at 2,200 characters. Meta publishes no documented Reels description limit, so this Actor uses a conservative ceiling rather than risk a silent truncation it cannot verify. On the browser lane the caption is typed into the composer and then read back before publishing: if it does not match, the run stops instead of publishing a reel with the wrong text. | string |
reelTitle | Optional title (Business Suite "Add a title" / API `title`). Capped at 255 characters. Separate from the caption, and not shown on every surface. | string |
target | Selects composer route. A personal profile can ONLY be reached through the browser lane (cookies): Meta states "You can only publish Reels to Facebook Pages", so no access token of any tier can publish to a profile. A Page can be reached either way, and the API lane is the better one. | string |
pageId | Numeric Page id. Required when target=page (and whenever a Page access token is supplied). Find it in Meta Business Suite under the Page's settings, or from /me/accounts in the Graph API Explorer. Not the @username. | string |
shareMode | `schedule` / `draft` are Graph API-lane only in v1: they need a Page access token. On the browser lane anything other than "now" is rejected up front rather than silently published immediately. | string |
scheduledPublishTime | Required when shareMode=schedule, e.g. 2026-09-01T14:30:00Z. Meta's own window applies: more than 10 minutes in the future and within 29 days. Scheduled reels count against the same publishing budget as immediate ones. | string |
crosspostToInstagram | Browser lane only. Asserted by read-back; never left to the composer's default. If the toggle exists but its state cannot be read, the run refuses to publish rather than post to a second platform you did not ask for. The Graph API has no equivalent, so on the API lane this is ignored and the run says so in the log. | boolean |
c_user | Browser lane. DevTools -> Application -> Cookies -> https://www.facebook.com, row `c_user`, copy the Value column. It is your numeric account id. | string |
xs | Browser lane, same list. This is the session secret, it looks like 12%3Aabc...%3A2%3A1690... . Paste the value exactly, including the %3A sequences. Anyone holding this value holds your session, so treat it like a password. | string |
datr | Browser lane, same list. Optional but strongly recommended: it identifies your browser to Facebook's security system, and a session arriving without the `datr` it normally carries reads as a brand-new device and often gets a checkpoint. Never invent one, a `datr` Facebook has never seen is worse than none. | string |
cookies | Advanced alternative. Paste a Cookie-Editor JSON export of the whole facebook.com jar, or a raw `Cookie:` header. Leave blank if you filled the three fields above. Export from a facebook.com tab where you are signed in, messenger.com and instagram.com are separate cookie jars and will not work. | string |
pageAccessToken | Graph API lane. A long-lived PAGE access token (not a user token) from a user with the CREATE_CONTENT task on the Page. Supplying this switches the Actor to the official API and skips the browser entirely, cheaper and far more reliable. It needs no App Review, because you are the admin of your own Meta app; the README has the ten-minute setup. Requires `pageId`, and cannot publish to a personal profile. | string |
graphApiVersion | Pinned. Never call unversioned: an unversioned Graph call silently follows Meta's newest release and changes behaviour under you. Only change this if Meta deprecates the pinned version. | string |
continueOnNonEnglishUi | Browser lane. If your Facebook interface is not English, structural locators are used and text-anchored steps are best-effort. Off = the run stops with an actionable message instead of guessing which button it is about to click. | boolean |
proxyUrls | Optional. Full proxy URLs (http://, https://, socks5://), used exactly as given and in the order listed, instead of the default. Equivalent to "Proxy URLs" inside the Proxy setting above. This top-level spelling exists so an API caller can set it without nesting. | array |
demoMode | Force a labelled SAMPLE row that uploads nothing and charges nothing, even if credentials are provided. A run with no credentials at all is automatically demo mode. | boolean |
What you get
A structured dataset — each result includes fields like:
oklanetargetreelIdreelUrlcaptionvideoMegabytespublishedAterrorerrorCodeExport every run as JSON, CSV or Excel, or send it to your app, a database, Google Sheets, or an AI agent.
2 ready-to-run use cases
How to Post a Reel on Facebook Without a Page
The Graph API posts Reels to Pages only, so a personal profile means driving the composer instead. Give it a public .mp4 link and your own session.
Facebook Reels API Upload: Publish to a Page You Admin
Publishes a Reel to your Page through the official Graph API, with no browser and no proxy. Needs a public .mp4 URL and your own Page access token.
Related tools in Developer & Research Tools
Other ready-to-run tools in the same category — all pay-per-use on the Apify cloud.
GitHub Scraper
Search GitHub repos and users: stars, forks, language, topics, licence, plus user bio, company and followers. No token needed. $0.90 per 1,000 rows.
Stack Overflow / Stack Exchange Scraper
Search Stack Overflow and Stack Exchange by keyword or tag. Score, answer count, views, reputation and body text. $2 per 1,000 questions.
Package Registry Scraper (npm + PyPI)
Get npm and PyPI package metadata as JSON. Version, license, author, repo, keywords and npm monthly downloads. $2 per 1,000 packages.
arXiv Scraper
Search arXiv papers by title, author, abstract or category. Get full abstracts, authors, categories, DOI, dates and PDF links. $2 per 1,000 papers.
OpenAlex Scholarly Works Scraper
Search 250M+ OpenAlex papers with no API key. Get titles, authors, venue, year, citations, DOI, OA links and full abstracts. $2.00 per 1,000 papers.
Crossref Scholarly Works Scraper
Search 150M+ papers on Crossref: DOI, title, authors, journal, publisher, date, citations and abstract. No API key. $1.00 per 1,000 works.
Where this tool sits
- Categories
- Developer & Research Tools
Facebook Reels Uploader: publish a Reel to your Page or profile from a video URL
Give it a public link to your MP4 and your own Facebook credentials, and it posts the Reel, waits until Facebook actually shows it, and hands back the reel id and its link.
The credentials are the part to settle first, because there is no way around them. Publishing to a Page wants a Page access token and goes through Meta's official API, which is the route to use if you can. Publishing to a personal profile is only possible with your own Facebook cookies, because Meta's API publishes Reels to Pages and nothing else, whatever token you hold. Run it with no credentials at all and you get a labelled sample row: nothing is uploaded, and no publish fee and no megabytes are charged.
| Input | A public direct link to your MP4, plus your own token or cookies |
| Output | One row per run, with the reel id and link once Facebook confirms it |
| Ceiling | One reel per run, video up to 500 MB |
| Account needed | Yes, your own: a Page access token, or your Facebook cookies |
| Price | $0.15 per reel through the API, $0.35 through the browser |
🔍 What Facebook Reels Uploader does
Two routes to the same job. Supplying a pageAccessToken picks the first one.
| Route | What it needs | Can publish to | Can schedule or draft | Per reel |
|---|---|---|---|---|
| Graph API | pageAccessToken and pageId | A Page you admin | Yes | $0.15 |
| Browser | Your c_user, xs and datr cookies | A Page, or your own profile | No, immediate only | $0.35 |
On the API route Meta fetches your videoUrl itself, so nothing large moves through the run. When Meta cannot reach that URL the run downloads the file and pushes the bytes to Meta instead, rather than failing.
The clip is checked before anything is transferred: 3 seconds or longer (3 to 90 on the API route), 540x960 or larger, 24 to 60 fps, and inside Meta's 16:9 to 9:16 shape band. Vertical 1080x1920, H.264 with AAC sound, is what Meta documents. Anything outside that is refused with BAD_INPUT before a byte moves.
A closed composer is not a published reel, and a 200 from Meta is not one either. Both transcoding and review can reject a video minutes after the upload looked fine. So the run lists the reels on the target first, publishes, then watches up to three minutes for a reel id that was not there before. No new id means UPLOAD_UNCONFIRMED and no publish fee, and a list it cannot read at all stops it instead of publishing blind. On the browser route the caption and the Instagram toggle are read back first: a wrong caption, or a post to a platform you did not ask for, is worse than no reel.
📥 What you give it
{
"videoUrl": "https://example.com/my-reel.mp4",
"caption": "Behind the scenes from Tuesday #workshop",
"target": "page",
"pageId": "1234567890",
"pageAccessToken": "<your long-lived Page token>"
}
| Field | Default | What it is |
|---|---|---|
videoUrl | none | A public https:// link to the file itself, not to a page that plays it. An S3 link, a CDN link or an Apify key-value store record all work. |
caption | empty | The reel description, hashtags included. Up to 2,200 characters. |
reelTitle | empty | Optional title, up to 255 characters. Separate from the caption and not shown everywhere. |
target | profile | profile or page. A Page needs pageId. |
pageAccessToken | none | A long lived Page token, not a user token, from someone with the CREATE_CONTENT task on the Page. Supplying it switches the run to the API route. |
pageId | none | The Page's numeric id, not its @username. Required for target: page. |
shareMode | now | now, schedule or draft. The last two are API route only, and anything but now is refused up front on the browser route rather than published immediately. |
scheduledPublishTime | none | ISO 8601, for schedule. Meta's window applies: more than 10 minutes out and within 29 days. |
crosspostToInstagram | false | Browser route only. The API has no equivalent and the run says so in the log. |
c_user, xs, datr | none | Your Facebook cookies, for the browser route. Secret fields: never written to the dataset, the store or the log, which reports only a length. |
cookies | none | Or paste the whole facebook.com jar as a Cookie-Editor export or a raw header, instead of the three fields. |
continueOnNonEnglishUi | false | Browser route. Off means a non-English interface stops the run with a clear message rather than guessing which button it is about to click. |
graphApiVersion | v25.0 | Pinned on purpose. Leave it unless Meta retires that version. |
proxyConfiguration | the default | Browser route only. Set its country to where your cookies were exported: a country jump is the single biggest cause of a security check on your account. |
proxyUrls | none | Optional. Your own servers, used exactly as given. There is no per megabyte charge on a run that uses them, and the video ceiling becomes 304 MB instead of 500 MB. |
demoMode | false | Force the labelled sample row even when credentials are filled in. |
📤 What you get back
One row per run. This is a real row from a real run whose cookies had expired, which is the shape you get when nothing was published. The message is cut short here:
{
"ok": false,
"platform": "facebook",
"lane": "browser",
"target": "profile",
"posted": false,
"confirmed": false,
"reelId": null,
"reelUrl": null,
"publishedAt": null,
"errorCode": "SESSION_INVALID",
"error": "SESSION_INVALID: Your Facebook session is not logged in (Facebook redirected to the login page). Cookies are revoked by logging out, by \"Log out of all sessions\", and often by a password change. Re-export c_user, xs and datr from a browser where you are signed in. ...",
"caption": "billing audit run 2",
"videoUrl": "https://download.samplelib.com/mp4/sample-5s.mp4",
"warnings": [],
"chargeStatus": "pending",
"chargeSummaryKey": "CHARGE_SUMMARY.json",
"debugScreenshot": "SESSION_INVALID.png",
"debugHtml": "SESSION_INVALID.html"
}
| Field | What it is |
|---|---|
ok, posted, confirmed | Whether the run worked, whether it published, and whether Facebook then showed the reel. All three are true on a clean publish. |
reelId, reelUrl, publishedAt | The new reel's id, its link and the time, once confirmed. null otherwise. |
lane, target, pageId | Which route ran, and where it published. |
errorCode, error, warnings | What stopped it, in a code you can branch on and a sentence you can read. |
videoSizeMegabytes, videoMegabytes | The file's size, and the megabyte count the run asked to bill. On a failure row the second is the traffic already moved, not the file size. |
chargeStatus, chargeSummaryKey | The row is written before the charge settles, so treat these as a pointer, not a receipt. |
debugScreenshot, debugHtml | Keys in the run's store holding a picture and the page at the moment it stopped. |
🧾 Reading the output
Three places to look.
| Where | What is in it |
|---|---|
| The dataset | The one result row above. |
CHARGE_SUMMARY.json | The settled figures for the run, which is where to check what was actually billed. |
<CODE>.png and <CODE>.html | A screenshot and the page from the moment a browser run stopped, named after the error code. |
The codes worth knowing:
| Code | What happened |
|---|---|
BAD_INPUT | The clip failed a check, or the input cannot work: a token with target: profile, or schedule on the browser route. Nothing moved. |
SESSION_INVALID | Your cookies are no longer signed in. Re-export them. |
API_TOKEN_INVALID | The Page token was killed by a password change or a permissions change. Mint a new one. |
VIDEO_UNAVAILABLE, OVERSIZED_VIDEO | Your videoUrl would not serve the file, or it is over the ceiling. |
API_PROCESSING_FAILED | Meta took the upload and then rejected it while processing. No publish fee, nothing per megabyte. |
UPLOAD_UNCONFIRMED | No new reel appeared. Check the account before retrying so you do not double post. No publish fee. |
LAYOUT_CHANGED | The reel list could not be read, so the run refused to publish. |
COMPOSER_UNAVAILABLE | Facebook did not offer that account the reel composer. |
LOCALE_UNSUPPORTED | The interface is not in English and continueOnNonEnglishUi is off. |
API_RATE_LIMIT, ACTION_BLOCKED | Meta is holding you off. The message carries its own estimate of when to try again. |
▶️ How to run it
1. For a Page, mint the token once. Create an app at developers.facebook.com as the account that admins the Page, open the Graph API Explorer, add pages_show_list, pages_read_engagement and pages_manage_posts, generate a token, switch the dropdown from *User Token* to Page Token, then extend it in the Access Token Debugger. GET /me/accounts?fields=id,name,tasks should list your Page with CREATE_CONTENT. 2. For a profile, copy c_user, xs and datr from DevTools on a signed-in facebook.com tab, under Application, then Cookies. Values exactly as shown, %3A included. 3. Open Facebook Reels Uploader, paste your videoUrl and caption, fill in the fields for your route. 4. Click Start, then read reelUrl on the result row.
💰 How much does it cost?
$0.15 per reel through the API route, $0.35 through the browser route. Flat on every Apify plan. Either way the fee applies only when Facebook confirms the reel is up: a run that fails or cannot be confirmed pays no publish fee.
The browser route also costs $0.02 for each megabyte of video moved through this actor's own connection, and those megabytes are billed whether the reel landed or not, because the traffic already happened. Point proxyUrls at your own servers and there is no megabyte charge on that run. The API route never charges per megabyte: Meta collects the file itself.
A demo run uploads nothing, so it pays no publish fee and nothing per megabyte.
💡 What people use it for
- Posting the day's clip to a Page from a pipeline, without anybody opening Business Suite.
- Scheduling a week of reels in one sitting on the API route, each with its own publish time.
- Publishing to a personal creator profile, which no API token can do.
- Pushing an approved edit straight from storage, by its link.
🚧 What it does not do
- It does not hold your credentials. You pass them per run, and the cookie fields stay out of
the dataset, the store and the log.
- No personal profile through the API. Meta publishes Reels to Pages only, so a profile means
cookies.
- No scheduling or drafts on the browser route. Anything but publish now is refused up front.
- One video per run. For a batch, run it once per clip.
- Meta allows 30 API reels per Page per 24 hours, which is Meta's published limit, not ours.
- It cannot see a music or rights takedown. A reel can publish cleanly and be muted or blocked
minutes later over its audio, and Meta sends no signal for it. The run reports the publish it confirmed and nothing beyond that.
- Reach is not visible either. Whether anyone is shown the reel is outside anything an uploader
can tell you.
- No editing, trimming or format conversion. The file you point at is the file that gets posted.
🧭 Which uploader do you need?
| If you want | Use |
|---|---|
| A Reel on a Facebook Page or profile | This one |
| A Reel on Instagram | Instagram Reel Uploader Pro |
| A video on TikTok | TikTok Video Uploader Pro |
| A Short on YouTube | YouTube Uploader Pro |
| To post from Claude, ChatGPT or n8n across platforms | Social Media Uploader Pro |
❓ Questions people ask
Do I need Meta's App Review? No. You admin the app and the Page, so the token you mint for yourself is enough. The setup above is a one-off and takes about ten minutes.
Why is the browser route more expensive? It runs a real session and a real composer, and the video passes through the run. The API route just hands Meta a URL.
What if my run says UPLOAD_UNCONFIRMED? Look at the account first. The reel may well be there, in which case retrying would post it twice. No publish fee was charged either way.
Can I publish to a Page without cookies? Yes, and you should. The API route is the reliable one.
Is my session safe? The cookie fields are secret and never leave the run. Logging out everywhere, or changing the password, revokes them.
🆘 If something breaks
Open the Issues tab on the actor page. Include the run ID and the errorCode from the result row. Never paste a cookie value or a token into an issue.