YouTube Video Uploader
Upload videos to your YouTube channel from a URL with OAuth. Set title, tags, category, thumbnail, privacy and publish time. $0.05 per upload.
How it works
- 1Open it on Apify
Hit Run on Apify — it opens the tool in the cloud, no install.
- 2Set the inputs
Adjust
method,demoMode,videoUrl(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.05 per video uploaded = $50 per 1,000
| You are charged for | When | Price |
|---|---|---|
| Video uploaded | Charged once per video YouTube confirms was published. Runs that upload nothing — missing credentials, a rejected session, or any failure — are never charged. | $0.05 |
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-15, and they are what you are actually charged.
Inputs
| Field | What it does | Type |
|---|---|---|
method | api = official Data API with OAuth (default, reliable; needs clientId/secret/refreshToken). browser = automate YouTube Studio with exported login cookies (works locally; cloud runs face Google's session anti-fraud). | string |
demoMode | Return one clearly-labeled SAMPLE row instead of uploading, a free preview of the output shape, no credentials needed. The Actor also falls back to this automatically when no credentials are supplied. Uncheck (and add credentials) to upload for real. | boolean |
videoUrl | Public direct URL to the video file to upload (.mp4/.mov). You host it (S3/CDN/Apify KV). | string |
title | YouTube title (max 100 chars). | string |
description | Video description (hashtags appended automatically). | string |
tags | Up to 30 video tags (used by the API method). | array |
hashtags | Hashtags appended to the end of the description (e.g. shorts, reddit). | array |
privacyStatus | public, unlisted, or private. | string |
cookies | Your exported YouTube login session: a cookies array OR a Playwright storageState ({cookies,origins}) as JSON. Export with a 'Cookie-Editor'/'Get cookies.txt' extension (for .youtube.com AND .google.com), or `npx playwright codegen --save-storage`. Kept secret. | string |
channelId | Target channel ID (UC...) if your account has multiple channels. | string |
thumbnailUrl | Optional public URL to a thumbnail image. | string |
madeForKids | Declare the video as made for kids (COPPA). | boolean |
publishAt | ISO datetime to auto-publish (API method + privacy=private). | string |
categoryId | YouTube category ID. 22 = People & Blogs, 24 = Entertainment, 23 = Comedy, 27 = Education. | string |
clientId | Only for method=api. From Google Cloud Console. | string |
clientSecret | Only for method=api. | string |
refreshToken | Only for method=api. Refresh token with the youtube.upload scope. | string |
What you get
A structured dataset — each result includes fields like:
_demo_noticemethodprivacyStatusscheduledForstudioUrlthumbnailSettitleuploadSecondsvideoIdvisibilitywatchUrlokExport 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
Schedule YouTube Video Uploads to Publish Later
Queue a video now and let YouTube flip it public at your chosen time. Scheduling requires the video stay private until then, which the actor sets for you.
Upload YouTube Shorts Automatically From a URL
Point it at a vertical MP4 and it publishes as a public Short with #shorts, your title, tags and category. Uses the official YouTube Data API.
Related tools in YouTube & Creator Tools
Other ready-to-run tools in the same category — all pay-per-use on the Apify cloud.
AI Viral Clip Cutter — Long Video to Shorts
Turn a long podcast or interview into ranked vertical clips. AI scores each moment and captions follow every word. $0.20 per clip row, samples included.
AI Text-to-Speech Voiceover
Turn any script into an AI voiceover file: MP3, WAV, Opus or AAC. Pick the voice, speed and format. $40.00 per 1,000 voiceovers ($0.04 each).
Video & Audio Transcriber — Word-Level + SRT/VTT
Transcribe any video or audio URL with word-level timestamps. Download SRT, VTT and TXT. The language is found for you. $0.02 per transcribed minute.
AI Character Reference Bank
Make a consistent AI character reference set. You get portrait, 3/4, full body, side and expression sheets, plus a character-bible prompt.
YouTube Channel Scraper
Scrape videos from any YouTube channel, no login or API key. Rows carry titles, views, dates, duration, thumbnails.
YouTube Comments Scraper
Scrape YouTube comments with no login or key. Get text, likes, reply counts, author handle, verified badge and timestamp. $0.40 per 1,000.
Where this tool sits
- Categories
- YouTube & Creator Tools Social Publishing & Uploaders
- Platforms
- YouTube
YouTube Video Uploader: publish a file from a URL to your own channel through the official API
Point it at a public link to your video file, give it a title and your own Google OAuth credentials, and it uploads to your channel with the description, tags, hashtags, privacy, schedule and thumbnail set in the same run. It is built to be the publish step at the end of a pipeline: render somewhere, drop the file on a CDN, hand this actor the URL.
The awkward part is Google's, not ours, and it catches almost everyone. A new Google Cloud project starts in Testing mode, where your refresh token expires after seven days, and an unverified project has every upload forced to private whatever you asked for. Publish your consent screen and get the project verified before your first public upload. The row reports privacyOverridden: true when YouTube overruled you, so at least you find out from the output rather than from your channel.
| Input | A public direct link to the file, a title, and your own Google OAuth credentials |
| Output | One row per run, with the video id and the watch URL |
| Ceiling | One video per run, up to 30 tags |
| Account needed | Your own YouTube channel and your own Google Cloud project |
| Price | $0.05 per uploaded video, flat on every plan |
📺 What YouTube Video Uploader does
It downloads your file, signs in with your refresh token and calls videos.insert, then thumbnails.set if you gave it a thumbnail. Nothing about your channel is kept after the run.
Privacy fails closed. It defaults to private, so nothing goes public unless you ask for it. If you set a schedule on a public or unlisted video the actor moves it to private for you and says so in the log, because YouTube will not schedule anything else. Afterwards it reads back the privacy YouTube actually stored, which is how privacyOverridden gets filled in.
There is a second browser method that drives YouTube Studio with exported cookies instead. It was built for running on your own machine, and it ignores tags, publishAt, categoryId and madeForKids. On Apify, use the API method.
📥 What you give it
{
"videoUrl": "https://cdn.example.com/clips/episode-12.mp4",
"title": "How the Roman aqueducts actually worked",
"description": "A short breakdown of gravity-fed water systems.",
"tags": ["history", "engineering", "rome"],
"privacyStatus": "private",
"publishAt": "2026-06-15T14:00:00Z",
"categoryId": "27"
}
| Field | Default | What it is |
|---|---|---|
videoUrl | none | A public direct link to the .mp4 or .mov. You host it, and it has to be reachable without a login. |
title | none | Trimmed to YouTube's 100 characters. |
description | none | Your hashtags are appended to the end of it. |
tags | none | Up to 30. Anything past 30 is dropped. API method only. |
hashtags | none | Appended to the description as #tag. Non-alphanumeric characters are stripped. |
privacyStatus | private | public, unlisted or private. |
publishAt | none | ISO datetime to publish itself. YouTube only schedules private videos, so the actor sets privacy to private for you and logs it. |
categoryId | 22 | People & Blogs by default. 24 Entertainment, 23 Comedy, 27 Education. |
thumbnailUrl | none | Optional. Custom thumbnails need a verified channel; if YouTube refuses it the upload still succeeds and thumbnailSet says false. |
madeForKids | false | The COPPA audience declaration. It binds you, not this actor. API method only. |
channelId | none | The UC... id, for an account that runs more than one channel. |
clientId, clientSecret, refreshToken | none | Your own Google OAuth credentials, with the youtube.upload scope on the refresh token. Marked secret. |
method | api | api or browser. Leave it on api. |
cookies | none | Only for the browser method: a cookies array or a Playwright storageState, covering both .youtube.com and .google.com. Marked secret. |
proxyConfiguration | Apify network on | Only the browser method uses it. The API method connects directly. |
demoMode | false | Returns the labelled preview row instead of uploading. |
📤 What you get back
One row per run. There is no real sample row published here yet, so rather than invent one, these are the fields and what each of them means:
| Field | What it is |
|---|---|
ok | true once the insert returned a video id. |
videoId, watchUrl | The id, and the youtu.be link to it. |
privacyStatus | The privacy YouTube actually stored, read back after the upload. |
requestedPrivacy, privacyOverridden | What you asked for, and whether YouTube overruled it. This is the field that catches an unverified project. |
scheduledFor | Your publishAt as stored, or null. |
thumbnailSet | Whether the custom thumbnail took. API method only. |
uploadSeconds | Wall clock from the start of the download. |
method | Which of the two lanes ran. |
studioUrl | The Studio link. Browser method only. |
🧾 Reading the output
| Row | Where it lands | Billed |
|---|---|---|
| A real upload | The dataset, ok: true with a videoId | yes |
| The preview row | The run's key-value store under SAMPLE_AND_NOTICES, never the dataset | no upload fee |
That split is deliberate: a preview cannot end up looking like a delivery, and it cannot bill like one either.
A failure does not write a row. The run itself ends as failed, with a message naming the fix rather than a stack trace:
| What went wrong | What the message says |
|---|---|
| The refresh token expired or was revoked | Usually Testing mode and the seven-day expiry. Publish your consent screen. |
| No channel on the account | The signed-in account has no channel to upload to. |
| Quota exhausted | Your project's daily units are spent. |
| Forbidden, or the scope is missing | The refresh token does not carry youtube.upload. |
On the browser method a failed step also saves a screenshot and the page HTML into the run's key-value store, and names them in the error.
▶️ How to run it
1. Create an OAuth client in Google Cloud Console with the YouTube Data API enabled, and generate a refresh token carrying the youtube.upload scope. 2. Move the consent screen to In production, and request the compliance audit if you intend to publish anything public. 3. Open YouTube Video Uploader, paste the three credential fields, and put your file's link in videoUrl. 4. Fill in title, pick a categoryId, and leave privacyStatus on private for the first run. 5. Click Start, then read videoId, watchUrl and privacyOverridden off the row.
Run it with no credentials at all and you get the preview row instead, so you can see the shape before setting any of this up.
💰 How much does it cost?
$0.05 per uploaded video. Flat on every Apify plan, no volume tiers.
A run that fails before the upload completes, and a preview run, are not charged that fee. It lands on uploads that reached your channel, not on attempts.
Your Google quota is separate and it is Google's, not ours: videos.insert costs about 100 units against a default of 10,000 a day, so roughly 100 uploads a day before you need an increase.
💡 What people use it for
- The last step of a render pipeline, so the file never waits in a bucket for somebody to upload it.
- Scheduling a batch: one run per video, each with its own
publishAt, all private until their
moment.
- Re-publishing a back catalogue with consistent titles, tags and category from your own data.
- Posting to a second channel on the same account, named by
channelId.
🚧 What it does not do
- It cannot lift Google's private-video override. An unverified project forces every upload
private and only Google's compliance audit changes that. The row tells you it happened.
- It does not manage your OAuth for you. There is no shared app here: the credentials, the
project and the quota are yours.
- The browser method ignores
tags,publishAt,categoryIdandmadeForKids, and it always
declares the video as not made for kids. If your video is child-directed, use the API method, or set the declaration yourself in Studio.
- The API method collapses your description onto one line. Line breaks do not survive the
upload.
- One video per run. There is no queue and no playlist step.
- It does not wait for HD processing. A video looks soft for a while after upload and sharpens
on its own.
- It does not check what is behind your
videoUrlbeyond the file type, so point it at links you
control.
🧭 Which YouTube actor do you need?
| If you want | Use |
|---|---|
| A plain upload through the official API | This one |
| Long-form with category, playlist, scheduling and every setting read back | YouTube Uploader Pro for Videos |
| To publish a vertical clip as a Short | YouTube Uploader Pro for Shorts |
| To download a video, its audio or its subtitles | YouTube Downloader Pro |
| The videos a channel has already published | YouTube Scraper |
❓ Questions people ask
Why did my public upload come out private? An unverified Google Cloud project. YouTube forces private and does not tell you at upload time. Request the compliance audit, and watch privacyOverridden in the meantime.
My refresh token stopped working after a week. The consent screen is still in Testing mode, where Google expires tokens after seven days. Move it to In production.
Do I need to give you my Google account? No. You create the OAuth client, you hold the refresh token, and it is marked secret in the input.
How many can I upload a day? Google's quota decides, not this actor. The default works out at roughly 100 uploads a day.
Can I see the output before setting up OAuth? Yes. Run it with no credentials, or tick demoMode, and the preview row lands in the run's key-value store.
🆘 If something breaks
Open the Issues tab on the actor page with the run id. The failure message usually names the fix already, and on the browser method the saved screenshot in the key-value store shows where it stopped.