Request a tool
All toolsAutomationsGuidesMCP serverRequest a toolPlatformsCategories
YouTube Uploader Pro icon

YouTube Uploader Pro

Publish a long-form video to your own YouTube channel from a URL. Set the title, description, tags, category, playlist, thumbnail, visibility and scheduling.

59 runs on Apify $0.55 per video uploaded ($550 / 1,000)
Run this in the cloudRun on Apify →

Developer & Research Tools

How it works

  1. 1
    Open it on Apify

    Hit Run on Apify — it opens the tool in the cloud, no install.

  2. 2
    Set the inputs

    Adjust videoUrl, title, description (sensible defaults are pre-filled).

  3. 3
    Click Run

    The tool runs on Apify’s cloud and collects the data for you.

  4. 4
    Export the results

    Download as JSON, CSV or Excel, or pipe straight into your app, Google Sheets, or an AI agent.

Pricing

$0.55 per video uploaded = $550 per 1,000

You are charged forWhenPrice
Video uploadedCharged once per video that YouTube confirms was published. Runs that publish nothing — missing or expired cookies, a rejected file, or any failure — are never charged this fee, but they are still charged for the megabytes they transferred, under "Video megabyte".$0.55
Video megabyteCharged per megabyte, rounded up, for the video bytes a run actually pulls in from your URL, and charged on EVERY outcome — not only on a confirmed upload — because that transfer is paid for the moment it happens. Your file crosses the network twice on a full upload, in from your URL and out to YouTube, and is counted once. A run that transfers less than 64 KB is not charged, a run whose result row never reaches the dataset is not charged, and the total never goes past the "maxTotalChargeUsd" you set.$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-08-25, and they are what you are actually charged.

Inputs

FieldWhat it doesType
videoUrlPublic https direct URL to the long-form video file (.mp4/.mov) to upload. Must be a non-expired direct link (S3/CDN/key-value store) that returns the file itself, not a landing page. Required for a real upload. To see the sample row instead, run it with the cookie fields blank on an account that has no saved session yet, or tick "demoMode".string
titleThe video title, capped at YouTube's 100 characters (a longer one is cut and the run logs exactly how many characters were dropped, never silently). Required for a real upload: YouTube pre-fills the title box with the video's filename, so a blank title publishes a video called "video.mp4".string
descriptionThe video description. YouTube's limit is 5000 characters; anything longer is cut to 5000 and logged. Over 60 hashtags across the title and description makes YouTube ignore ALL of them on the video, so the run warns above that.string
tagsVideo tags. They are entered into Studio's advanced pane and the chips are read back afterwards, so the result row can say whether they actually landed. YouTube caps the combined tag string at 500 characters.array
visibilityWho can watch the video. Defaults to private on purpose: the run refuses to press Publish unless it can read the chosen option back off the radio, because a video that should have been private going public cannot be undone.string
madeForKidsSet this only if the video is directed to children. This is the COPPA audience declaration and it is legally binding on you, not on this Actor. It is also the one answer that gates Studio's Next button, so it is always set explicitly and read back.boolean
channelIdOptional. The UC… channel id to upload to, e.g. UCXXXXXXXXXXXXXXXXXXXXXX. Not the @handle and not the Brand-account id. If the signed-in account does not manage this channel, Studio silently redirects to the account's default channel, the run detects that and stops instead of uploading to the wrong place. Leave blank to use the session's default channel.string
thumbnailUrlOptional. Public https direct URL to a JPG/PNG/WEBP thumbnail, under YouTube's own 2 MB limit. Best-effort: custom thumbnails require a phone-verified channel, and on an unverified channel Studio accepts the click and silently does nothing. The result row reports whether it was applied.string
channelIsPhoneVerifiedTick this only if the channel has completed phone verification at youtube.com/verify. Without verification YouTube caps uploads at 15 minutes, and it accepts the whole file first and rejects it for length afterwards, so a 4 GB transfer can be paid for and thrown away. With this unticked, anything over 15 minutes is refused, normally on the first megabyte, read over a range request before the transfer, though a container the probe cannot read is checked after the transfer instead. One phone number can only verify 2 channels per year.boolean
publishAtOptional. RFC 3339 timestamp WITH an explicit UTC offset, e.g. 2026-09-01T14:00:00-07:00. The offset is mandatory: without one the schedule lands in whatever timezone Studio happens to be showing, and YouTube renders published dates in Pacific Time on the watch page, so an ambiguous time reads back as the wrong day. A scheduled video is private until its publish moment, so visibility is set to private for you and the log says what it will become and when.string
categoryIdThe YouTube category for the video. Set in Studio's advanced pane.string
playlistIdOptional. Add the video to this playlist. Best-effort. Studio's playlist picker is a search dropdown, so if the playlist cannot be matched the upload still completes and the result row says so.string
cookiesPASTE THIS THE FIRST TIME, THEN LEAVE IT EMPTY. Anything you paste here WINS: the run signs in with it and does not read the saved session at all. Leave the field empty and the run uses the session saved by an earlier run instead. With nothing pasted AND no saved session, the run returns the labelled sample row and uploads nothing. Paste again whenever you want to switch Google account or recover, a paste always overrides what is stored, so a saved session can never publish to an account you did not just name. HOW TO EXPORT: paste TWO Cookie-Editor JSON exports here, one after the other (order does not matter, and you do not need to merge them yourself). (1) On https://www.youtube.com/robots.txt while signed in: Cookie-Editor > Export > JSON. robots.txt because it is a plain text page with nothing loading behind it, so no background request can re-mint a cookie between the export and the paste. (2) On https://myaccount.google.com/ in the same window: Export > JSON again. BOTH are needed. Cookie-Editor can only ever export the domain of the tab you are on, and the upload flow passes through accounts.google.com, so a youtube.com-only export arrives there with nothing to show and Google draws its sign-in page. Chrome is the tested path: before exporting, set the extension to "On all sites" in chrome://extensions > Cookie-Editor > Details, or it silently omits every non-Secure cookie (SID, HSID, APISID). Export and run within a few minutes: Google re-mints the rotating cookies about every 10 minutes, and an export is good for one use only, do not test the same jar elsewhere first. Close the window afterwards, but do NOT sign out; signing out kills the session for every copy. WHAT GETS SAVED: once a run authenticates AND reads back which channel it is signed into, this Actor stores the refreshed cookie jar in a key-value store named "youtube-uploader-sessions" in YOUR OWN Apify account, never the developer's and never in a run's output store. A run that signs in and cannot read the channel back saves nothing and says so in the log. Later runs open Chrome on what was saved. It does not expire; to delete it, tick "Forget the saved session" (resetProfile) together with a fresh export, or delete that store in Apify Console > Storage, which does not sign the Google account out.string
resetProfileA RECOVERY HATCH, not the way to switch accounts. To switch accounts you only need to paste the new account's export, anything you paste already wins over the saved session, which is never consulted on a run that supplies cookies. Tick this box only when the SAVED session itself has gone bad and you want it gone: the account was signed out everywhere, or the password changed. It deletes the saved cookie jar for this channel, the one record this Actor stores, before the run reads anything. YOU MUST PASTE A FRESH COOKIE EXPORT IN THE SAME RUN. Ticking this on its own does nothing at all: that run would have no session to sign in with, so it returns the sample row and deletes nothing, a run that never opens a browser must not be able to destroy your saved session. Untick the box and empty the cookies field again afterwards.boolean
SIDIn your browser: DevTools > Application > Cookies > https://www.youtube.com, find the row named SID and copy its Value column. Paste just the value. It must come from the .youtube.com list, NOT the .google.com one. Studio never receives google.com cookies.string
HSIDFrom the same .youtube.com Cookies list. This one is HttpOnly, so a JavaScript/console cookie dump will not contain it, you need a cookie-manager extension (Cookie-Editor / Cookie Quick Manager).string
SSIDFrom the same .youtube.com Cookies list. HttpOnly, like HSID, so a console cookie dump will not contain it either.string
APISIDFrom the same .youtube.com Cookies list. Paste just the value.string
SAPISIDLoad-bearing beyond page load: Studio's own JavaScript derives the `Authorization: SAPISIDHASH` header from this value, and every Studio request 401s without it even though the page renders.string
__Secure-1PSIDThe first-party session id. HttpOnly.string
__Secure-3PSIDThe third-party-context session id. Studio specifically needs this one, an export filtered to first-party cookies only produces a session that looks complete and still bounces to the sign-in page.string
LOGIN_INFOYouTube's own logged-in-identity cookie. Studio bounces to a sign-in without it even when the whole SID family is intact. It is also the cheapest single proof that your export came from a real cookie API rather than a console dump, it is HttpOnly, so a `document.cookie` paste can never contain it.string
__Secure-1PSIDTSThe rotating freshness partner of __Secure-1PSID (TS = timestamp). Google re-mints it roughly every 10 minutes. Its absence makes Google treat the session id as unbound, i.e. stolen-looking, and redirect to sign-in. Export it fresh, minutes before the run.string
__Secure-3PSIDTSThe same, for __Secure-3PSID. Google re-mints it roughly every 10 minutes, and a session id with no matching PSIDTS is treated as unbound. Export it fresh, minutes before the run.string
__Secure-1PAPISIDBacks the SAPISID1PHASH Authorization scheme.string
__Secure-3PAPISIDBacks the SAPISID3PHASH Authorization scheme. YouTube falls back to this when SAPISID is missing.string
demoModeForce a labelled SAMPLE run that uploads nothing and moves no video bytes, even with a session available. A run with no cookies pasted AND no saved session is a sample run too. Once a session is saved, two things reach the sample row: this box, or "Forget the saved session" ticked with the cookie box left empty.boolean

What you get

A structured dataset — each result includes fields like:

okvideoIdwatchUrltitlevisibilityvisibilityConfirmedpublishAtdurationSecvideoBytesthumbnailAppliedwarningserrorCodeerror

Export every run as JSON, CSV or Excel, or send it to your app, a database, Google Sheets, or an AI agent.

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 iconDeveloper & Research Tools

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.

18 use cases

Stack Overflow / Stack Exchange Scraper iconDeveloper & Research Tools

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.

2 use cases

Package Registry Scraper (npm + PyPI) iconDeveloper & Research Tools

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.

2 use cases

arXiv Scraper iconDeveloper & Research Tools

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.

2 use cases

OpenAlex Scholarly Works Scraper iconDeveloper & Research Tools

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.

2 use cases

Crossref Scholarly Works Scraper iconDeveloper & Research Tools

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.

2 use cases

See all Developer & Research Tools →

YouTube Uploader Pro for Videos: publish long-form to your own channel from a URL

Give it a direct link to your video file and a title. It opens YouTube Studio in a real browser, signs in with your own session, fills in the whole wizard including category, playlist, thumbnail and scheduling, and hands back the video id and the watch URL.

Two things to know before you spend anything. It signs in as you, with cookies you export from your own browser, so the first run takes ten minutes of setup and the risk sits with your channel: driving Studio this way is against YouTube's terms. And YouTube decides some things from the file itself and will not take them back. A square-or-taller clip of three minutes or less is filed as a Short whatever you meant, and a channel that has not been phone verified is capped at 15 minutes and enforces it *after* accepting the whole upload. Both are checked before your file moves.

InputA public direct link to the video file, a title, and your own YouTube session
OutputOne row per run, with the video id and the watch URL
Ceiling4,000 MB per file, 12 hours, 15 minutes on an unverified channel
Account neededYour own YouTube channel, signed in with your own cookies
Price$0.55 per uploaded video, plus $0.02 for each megabyte transferred

🎬 What YouTube Uploader Pro for Videos does

It reads your file before it transfers it. Duration, frame size and container come off the first megabyte, so a clip YouTube would file as a Short, or one too long for an unverified channel, is turned away for a megabyte rather than four gigabytes.

Then it fills in Studio properly: title, description, tags, category, playlist, thumbnail, the kids declaration and the schedule. Each one is read back off the page afterwards, so the row says what landed rather than what was asked for. Visibility is the strict one. The run refuses to press Publish unless it can confirm the radio, and saves a draft instead, because a video that should have been private going public cannot be undone.

📥 What you give it

{
  "videoUrl": "https://cdn.example.com/episode-12.mp4",
  "title": "Episode 12: the sugar cubes puzzle",
  "categoryId": "27",
  "visibility": "private"
}
FieldDefaultWhat it is
videoUrlnoneA public https:// link to the file itself, not a page with a player on it. You host it.
titlenoneCut to YouTube's 100 characters, and the log says by how much. A blank one is refused: Studio pre-fills the box with your filename, so blank would publish a video called video.mp4.
descriptionnoneCut to 5,000 characters. Past 60 hashtags across title and description YouTube ignores all of them, so the run warns you.
tags[]Entered in the advanced pane, and the chips are read back, so tagsApplied tells you whether they took.
visibilityprivatepublic, unlisted or private.
publishAtnoneRFC 3339 with an explicit offset, like 2026-09-01T14:00:00-07:00. Without the offset the schedule lands in whatever timezone Studio is showing, and YouTube prints dates in Pacific Time. A scheduled video is private until its moment, so visibility is set to private for you.
categoryId22People & Blogs, Entertainment, Comedy or Education.
playlistIdnoneBest effort. Studio's playlist picker is a search box, so a playlist it cannot match is reported rather than guessed at.
channelIdnoneThe UC... id, not an @handle. Use it when the account runs several channels: Studio redirects silently, so the run stops with WRONG_CHANNEL instead of uploading to the wrong one.
thumbnailUrlnoneBest effort, under YouTube's own 2 MB limit. Custom thumbnails need a phone-verified channel; on an unverified one the row reports thumbnailApplied: false rather than pretending.
madeForKidsfalseYouTube's COPPA audience declaration. It binds you, not this actor.
channelIsPhoneVerifiedfalseTick it only if the channel finished verification at youtube.com/verify. It opens the path above 15 minutes.
cookiesnoneYour session, pasted once. See below. Marked secret.
resetProfilefalseDeletes the saved session before anything reads it. Only does anything when you paste a fresh export in the same run.
proxyConfigurationApify network onThe country is worth matching to where you exported. Your own servers in proxyUrls are used as given. Your video file is always fetched straight from your URL.
demoModefalseForces the labelled sample row. Nothing is uploaded and no video bytes move.

The twelve individual cookie fields underneath are a fallback. Anything in the cookies box wins.

📤 What you get back

One row per run, whatever happens. A published video carries these:

FieldWhat it is
videoId, watchUrl, studioUrlWhere the video now lives
visibilityConfirmed, madeForKids, thumbnailApplied, tagsAppliedWhat was confirmed on the page, rather than what you asked for
scheduled, publishAt, categoryId, playlistIdThe settings that took
durationSec, width, height, videoBytesWhat was read off your file
warningsThings worth knowing that did not stop the run

A refusal writes one row too. This is a real one from a real run, with the long messages cut short:

{
  "ok": false,
  "charged": null,
  "uploadFeeCharged": false,
  "errorCode": "SESSION_INCOMPLETE",
  "error": "SESSION_INCOMPLETE: the cookies you supplied do not add up to a Studio session. ...",
  "reason": "The cookies supplied do not add up to a YouTube Studio session. Nothing was uploaded, so no upload fee was charged ...",
  "hint": "Export the WHOLE youtube.com cookie list, not a few fields. ...",
  "platform": "youtube",
  "posted": false,
  "confirmed": false,
  "videoId": null,
  "watchUrl": null,
  "finalUrl": null,
  "diagnosedAt": "2026-08-24T23:32:41.087Z",
  "kind": "video",
  "sourceUrl": "https://httpbin.org/status/403",
  "title": "x",
  "published": false,
  "egress": { "lane": "direct", "attached": false }
}

🧾 Reading the output

RowHow to spot itWhat happened
A published videook: true with a videoId and a watchUrlIt is on your channel
The sample row_sample and _demo, ids reading SAMPLE00000Nothing was uploaded
A refusalok: false with an errorCode, reason and hintNothing was published

The run still finishes as succeeded on a refusal, so read ok and errorCode, not the run status. Codes about your file:

CodeWhat happened
WOULD_BECOME_SHORTSquare or taller and three minutes or under. YouTube files that as a Short with no flag to turn off. Send it to the Shorts actor, or letterbox it.
UNVERIFIED_CHANNEL_15MINOver 15 minutes with Channel is phone-verified unticked. Verify at youtube.com/verify, then tick the box.
VIDEO_TOO_LONGOver 12 hours, which is YouTube's own ceiling.
VIDEO_TOO_SMALL, NO_VIDEO_TRACKUnder 2 KB, or audio with no picture.
UNSUPPORTED_CONTAINERThe opening bytes name a container this actor does not upload. It takes MP4, MOV, Matroska and WebM; AVI, FLV, Ogg, WMV and the MPEG streams are refused on the first megabyte.
VIDEO_UNAVAILABLEThe file could not be fetched: a bad status, a timeout, zero bytes, or an HTML page where the video should be. An expired signed link answers 200 with an error page, which is why the magic bytes are checked and not the content type.
OVERSIZED_VIDEOAbove the 4,000 MB ceiling, or this run's Maximum total charge (USD) does not cover it. The message says which.
BAD_INPUTA missing or non-https videoUrl, a missing title, a channelId that is not a UC... id, or unreadable cookies.

And these are about your session or Studio:

CodeWhat happened
SESSION_INCOMPLETEThe pasted cookies do not add up to a Studio session.
SESSION_INVALID, SESSION_EXPIREDThe cookies were not accepted, or Google served its sign-in page. Re-export both sets and run within a few minutes.
SESSION_CHECKPOINTGoogle served a "verify it's you" challenge. Clear it in your own browser, then re-export.
BROWSER_GATEGoogle's sign-in form refused the browser. The session was not accepted, so re-export that.
WRONG_CHANNELThe session landed on a channel other than the channelId you asked for.
VISIBILITY_UNCONFIRMEDThe visibility could not be read back, so a draft was saved instead of publishing.
DAILY_LIMITThe channel's own 24-hour upload cap.
UPLOAD_REJECTEDYouTube refused the file after it arrived, quoting its own sentence.
UPLOAD_UNCONFIRMEDPublish was clicked and Studio never confirmed. Look in Studio before retrying: a blind retry can double-post.
LAYOUT_CHANGEDA step in Studio answered no selector this actor knows. Tell us.
NETWORK, TIMED_OUT, INTERNALThe run could not reach YouTube, ran out of time, or hit something unexpected.

▶️ How to run it

1. Open YouTube Uploader Pro for Videos and put your file where it is served as a file: S3, a CDN, an Apify key-value store record. 2. Export your cookies, following the four rows below exactly. This is the step people lose an hour to. 3. Paste both exports into YouTube session cookies, fill in Title, pick a Category, set Visibility, and start within a few minutes. 4. Read the row: a watch URL on success, an errorCode and a hint on a refusal. 5. After that leave the cookie box empty: the session is saved and refreshed on every run. Paste again only to switch account, or when a run says the session stopped.

StepWhat to doWhy
1Install Cookie-Editor in Chrome, open chrome://extensions, Details, set access to On all sitesWithout it the export silently omits SID, HSID and APISID, and still looks complete
2Sign in to the right channel, go to https://www.youtube.com/robots.txt, Export to JSONA plain text page cannot re-mint a cookie between your export and your paste
3Go to https://myaccount.google.com/ in the same window, Export to JSON againThe extension only ever exports the tab's own domain, and the upload passes through both
4Paste both blocks in the one box and start within a few minutesTwo cookies in the jar are re-minted about every ten minutes

💰 How much does it cost?

$0.55 per uploaded video, plus $0.02 for each megabyte the run transfers.

Megabytes are counted on every attempt, so a transfer that breaks and restarts can bill more megabytes than the file holds. The upload fee is only charged once YouTube confirms it.

A vertical two-minute clip, an 18-minute cut on an unverified channel, an AVI: each is turned away on that first megabyte. No video bytes move on the sample row, or on a refusal that lands there, so neither carries megabytes or the upload fee.

💡 What people use it for

  • Publishing the render at the end of an editing pipeline, instead of leaving it in a bucket.
  • Scheduling a back catalogue: one run per episode, each with its own publishAt, all private

until their moment.

  • Posting to a second channel you manage, named by channelId so it cannot land on the wrong one.
  • Keeping metadata consistent: category, tags and playlist come from your data.

🚧 What it does not do

  • It cannot answer Google's "verify it's you" screen. That run stops at SESSION_CHECKPOINT

and publishes nothing. Clear the challenge in your own browser, then re-export.

  • No Data API lane. Studio in a browser is the only path, for the reason below.
  • Matroska and WebM skip the Short and 15-minute checks, because the duration cannot be read

from them. Those two land on YouTube's judgement rather than ours. MP4 is the safe choice.

  • Playlists are matched on the title, not on the PL... id, since the picker is a search box.
  • The altered-content question is always answered No. If your video needs a yes, set it in

Studio yourself afterwards.

  • It does not wait for HD processing. A video looks soft for a while after publishing and

sharpens on its own.

  • One shape of file is judged only after the whole transfer: an MP4 with its moov atom at the

end, on a host that ignores a range request. The refusal arrives with the megabytes already billed. Serve a faststart MP4: ffmpeg -i in.mp4 -c copy -movflags +faststart out.mp4.

  • Your saved session lives in a store called youtube-uploader-sessions **in your own Apify

account**, and no cookie value ever reaches a dataset row.

🧭 Which YouTube actor do you need?

If you wantUse
To publish long-form, with category, playlist and schedulingThis one
To publish a vertical clip as a ShortYouTube Uploader Pro for Shorts
A plainer upload from a URLYouTube Video Uploader
To download a video, its audio or its subtitlesYouTube Downloader Pro
The videos a channel has already publishedYouTube Scraper

❓ Questions people ask

Can I use the official API instead? Not usefully. A video uploaded through videos.insert from an unverified API project created after 28 July 2020 is locked to private forever, with no appeal on YouTube's own help pages. That is why there is no API mode here.

Why was my vertical video refused? At three minutes or under YouTube would have filed it as a Short, server-side, with no way back except deleting it. Use the Shorts actor, or letterbox it.

Where do my cookies go? Into your own Apify account, in the store named above, and into the browser this actor drives.

Can I try it first? Tick demoMode, or run it with no cookies on an account with no saved session. You get one labelled sample row, no upload and no video bytes.

How do I switch channels? Paste the new account's export. Anything you paste wins, and the saved session is not read on that run.

🆘 If something breaks

Open the Issues tab with the run id and the row's errorCode. A LAYOUT_CHANGED row means Studio moved something and we need to hear about it.