ThumbAPI logoThumbAPI
Automation

How to Fully Automate YouTube Thumbnail Uploads with n8n and ThumbAPI

Aldin Kozica
Aldin Kozica
11 min read
How to Fully Automate YouTube Thumbnail Uploads with n8n and ThumbAPI — ThumbAPI Blog

Every creator knows the pain of context-switching: you finish editing a video, schedule it, and then you have to manually upload the thumbnail. If you are building a programmatic content pipeline, this manual step ruins the entire automation.

This guide walks through the exact setup I used to close that loop — Google Cloud OAuth2 for n8n, a community node for ThumbAPI, and a paste-ready workflow that generates a thumbnail from the video title and pushes the raw bytes directly back to YouTube via Google's Resumable Upload protocol.

If you would rather save the rendered thumbnail to Google Drive instead of overwriting the YouTube one, the n8n thumbnail-to-Drive walkthrough covers that variant.

Step 1: Create a Google Cloud Project

Before we touch anything inside n8n, we need a Google Cloud project so we can issue an OAuth2 client for n8n.

Critical: create the project from the exact same Google account that owns your YouTube channel. If the accounts don't match, the OAuth tokens won't have permission to modify your videos.

  1. Open the Google Cloud Console welcome page.
  2. In the top navigation bar, click the project dropdown. If you already have projects, click New Project; if this is your first one, you'll see the option immediately.
  3. Enter a project name — it isn't critical, e.g. youtube-auto-thumbnails — and click Create.
  4. If you have multiple projects, select this new one from the top nav dropdown. With only one project it opens automatically.

Step 2: Open APIs & Services

From the Cloud Console dashboard, open APIs & Services (search for it in the top search bar if you don't see it in the side menu).

APIs & Services entry point in the Google Cloud Console dashboard

Then go to Credentials → Create Credentials → OAuth client ID.

The first time you try to create an OAuth client, Google blocks you with: "To create an OAuth client ID, you must first configure your consent screen." Click Configure consent screen, then Get started.

Step 3: Configure the OAuth Consent Screen

You'll be walked through a short form. Fill it in exactly like this:

  1. App information — enter an app name and the support email (your Google account email).
  2. Audience — pick External. This is for personal channel automation, not an internal Workspace app.
  3. Contact info — your email again.
  4. Check the data policy box and click Create.

After saving you'll land on a screen confirming the consent setup. Click Create OAuth client to continue.

Step 4: Create the OAuth 2.0 Client

Now we issue the actual Client ID + Client Secret that n8n will use.

  1. Application type — choose Web application.
  2. Authorized JavaScript origins — this depends on where n8n runs:
    • n8n Cloud / self-hosted on a domain: paste your instance URL, e.g. https://n8n.yourdomain.com
    • Local n8n (this tutorial): http://localhost:5678/
  3. Authorized redirect URIs — take whatever you entered above and append /rest/oauth2-credential/callback. So:
    • Local: http://localhost:5678/rest/oauth2-credential/callback
    • Cloud: https://n8n.yourdomain.com/rest/oauth2-credential/callback

OAuth client creation form — Web application type with Authorized JavaScript origins and redirect URI fields

Click Create. A modal pops up with your Client ID and Client Secret.

Save these now. Either download the JSON or copy both fields somewhere safe — you'll need them in Step 6 when wiring n8n. Downloading the JSON is the safer option since you keep access to them later.

Step 5: Add Yourself as a Test User and Enable the YouTube API

Two small but mandatory steps before the API will actually answer.

Add yourself as a test user

Back on the OAuth screen, open Audience in the left sidebar. Under Test users, click Add users and add the Google account email you use for YouTube.

Audience screen with the Test users section where you add your Google account email

The 403 Forbidden bypass. Skip this step and Google blocks your n8n login with 403 Access Denied the moment you try to authorize. It's the single most common reason this integration "doesn't work" for people.

Enable YouTube Data API v3

In the top search bar, type YouTube Data API v3 and pick it from the dropdown.

YouTube Data API v3 page in Google Cloud Console with the Enable button

Click the blue Enable button. The project is now allowed to talk to YouTube on your behalf.

That's the Google side done. Now into n8n.

Step 6: Wire Google OAuth2 into n8n

Tip for local n8n: open n8n in an incognito tab before doing this. Logged-in Google session cookies in your main browser can hijack the OAuth handoff and silently use the wrong account.

In your n8n instance:

  1. Click + in the top-right corner and choose Add credential.

  2. Pick Google OAuth2 API and click Continue.

  3. Paste the Client ID and Client Secret you saved in Step 4.

  4. In the Scope field, set:

    https://www.googleapis.com/auth/youtube
    
  5. Click Sign in with Google at the bottom.

A Google auth screen will open. Pick the account that owns your YouTube channel — this needs to be the same email you added as a test user in Step 5.

Google account picker shown during the n8n OAuth handshake

You'll see a yellow warning that "Google hasn't verified this app." That's expected — your app is in testing mode. Click Continue, or Advanced → Go to app (unsafe), and approve all requested permissions.

You should end up back in n8n with a green Connection successful banner and Account connected under the credential.

Repeat: the project must be created from the same Google account that owns your YouTube channel. Same email for OAuth and for YouTube — otherwise the upload step will silently fail later.

Step 7: Install the ThumbAPI Community Node

The workflow uses a dedicated n8n node so you don't have to hand-craft HTTP requests against the ThumbAPI endpoint.

  1. In n8n, open Settings (bottom-left) → Community Nodes.

  2. Click Install in the top-right.

  3. In the npm package field, enter:

    n8n-nodes-thumbapi
    
  4. Tick the data policy checkbox and click Install.

n8n Community Nodes install dialog with n8n-nodes-thumbapi entered as the package name

n8n will pull the package and the ThumbAPI node will appear in the node palette.

Step 8: Paste the Workflow

Everything is in place — now drop in the pipeline. Download the workflow JSON (recommended — avoids any copy-paste mangling), open the n8n canvas, click on the empty grid and press Cmd + V (or Ctrl + V) to paste the whole workflow at once. The full JSON is also inlined at the bottom of this post if you want to scan it first.

Full n8n workflow on the canvas after pasting the JSON — all nodes connected end to end

Resolving the orange credential warnings

When you paste an external workflow, n8n shows an orange icon on the HTTP-Get upload link node and the Generate a thumbnail node. That's a built-in security behavior — the JSON never carries secrets across machines. You just need to bind your local credentials.

  • HTTP-Get upload link → open the node, in the credential dropdown pick the Google account you authorized in Step 6.
  • Generate a thumbnail → open the node, in the credential dropdown choose Create New Credential.

Testing mode: in the ThumbAPI credential, paste thumbapi_test as the API key. This test key bypasses billing and always returns the same placeholder image — perfect for verifying the pipeline end-to-end without spending credits.

Production mode: when you're ready for real, branded thumbnails, grab a free production key on app.thumbapi.dev/signup (50 credits per month, no card) and paste it into the credential instead.

Step 9: Set Your YouTube Channel ID

One last thing before you can test. Open the second node — Input values — and replace the placeholder in the ChanelId field with your actual YouTube channel ID.

You can find your channel ID in two places:

  • YouTube Studio → Settings → Channel → Advanced settings
  • Inside your channel's public URL (the UC... string)

Paste it in, save, and you're ready to run.

How the Pipeline Works Under the Hood

When the workflow runs, this is what's actually happening:

  1. Fetch latest video data. The workflow hits your channel's public RSS feed (https://www.youtube.com/feeds/videos.xml?channel_id=...) to pull your most recent uploads.
  2. Filter & extract. A small JavaScript node strips out YouTube Shorts to isolate long-form uploads, and extracts the videoId and title for the most recent one.
  3. Generate the thumbnail. The ThumbAPI node takes the raw title string and renders a .png thumbnail. No design tool, no template, no Photoshop — just title in, image out.
  4. Resumable upload handshake. The workflow POSTs to Google's upload endpoint, declaring the exact byte size of the incoming image. Google replies with a temporary streaming URL in the location response header.
  5. Binary PUT. The final node PUTs the raw image bytes to that location URL, and YouTube swaps the thumbnail on the live video in place.

Production Blueprint

This workflow always operates on the most recent video in the feed, which makes it the perfect tail to chain onto a video-upload workflow. Once your upload step finishes, fire this one — and the thumbnail attaches itself within seconds of the video going public.

If you want to personalize the visual style further — embed your profile photo, drop in a brand logo, pick a custom asset set — you don't need to touch the workflow. Toggle Profile photo: on inside the ThumbAPI node, or configure assets visually in the ThumbAPI Studio. The custom asset datasets guide covers the brand-consistency knobs in more detail.

Copy-Paste n8n Workflow JSON

Recommended: download the workflow.json and paste it from there. Copying directly from this code block can occasionally introduce smart-quote or escape-character artifacts depending on your browser, which break the n8n import.

{
  "nodes": [
    {
      "parameters": {
        "content": "Here you update API key when you register on ThumbAPI \nTesting API key: thumbapi_test",
        "height": 96,
        "width": 246
      },
      "type": "n8n-nodes-base.stickyNote",
      "position": [368, -32],
      "typeVersion": 1,
      "id": "d47be3f2-33ab-4d64-a7d8-25e46091e6a4",
      "name": "Sticky Note1"
    },
    {
      "parameters": {
        "content": "Here you edit your chanell ID ",
        "height": 80,
        "width": 150
      },
      "type": "n8n-nodes-base.stickyNote",
      "position": [-336, -32],
      "typeVersion": 1,
      "id": "be346780-59f9-4f7d-8779-76d578f077fe",
      "name": "Sticky Note"
    },
    {
      "parameters": {
        "method": "PUT",
        "url": "={{ $json.headers.location }}",
        "sendBody": true,
        "contentType": "binaryData",
        "inputDataFieldName": "={{ $('Thumbnail').item.binary.thumbnail}}",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [944, 80],
      "id": "70b9d24a-80e0-4de7-91d7-4e9f3b45b341",
      "name": "HTTP upload thumbnail"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "=https://www.googleapis.com/upload/youtube/v3/thumbnails/set?videoId={{ $('VideIdSet').item.json.videoId }}&uploadType=resumable",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "googleOAuth2Api",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "X-Upload-Content-Type",
              "value": "image/png"
            },
            {
              "name": "X-Upload-Content-Length",
              "value": "={{ $('Thumbnail').item.binary.thumbnail.bytes }}"
            }
          ]
        },
        "options": {
          "response": {
            "response": {
              "fullResponse": true
            }
          }
        }
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [752, 80],
      "id": "441212ce-ddc4-4ca1-b9a4-f40687d3c828",
      "name": "HTTP-Get upload link",
      "credentials": {
        "googleOAuth2Api": {
          "id": "LgL98EcTf13Jsd6E",
          "name": "Google account"
        }
      }
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "7138bc63-e405-4c0a-a696-6d57e701cdb4",
              "name": "ChanelId",
              "value": "UCUKWzyXAAFUo4DCGfQhtVAg",
              "type": "string"
            },
            {
              "id": "b881458d-2403-45bb-bac5-f83ea36120aa",
              "name": "ThumbAPI_key",
              "value": "thumbapi_test",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [-320, 80],
      "id": "32a93a23-a9c4-46fd-bf4f-5eb919c41db5",
      "name": "Input values"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "a7d16741-06b4-4248-a06b-cf2971efb4d2",
              "name": "thumbnail",
              "value": "={{$binary.data}}",
              "type": "binary"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [592, 80],
      "id": "9b321aad-9dd6-4348-b3f3-9a81aa4e314d",
      "name": "Thumbnail"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "6c789f5b-6c63-4338-800b-e5d073e763bb",
              "name": "videoId",
              "value": "={{ $json.videoId }}",
              "type": "string"
            },
            {
              "id": "622c3edd-0ffb-4248-a46c-3e99dc598dec",
              "name": "title",
              "value": "={{ $json.title }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [224, 80],
      "id": "a88a834a-4bf8-4e37-9a35-ca871ea94a27",
      "name": "VideIdSet"
    },
    {
      "parameters": {
        "title": "={{ $json.title }}",
        "category": "=auto",
        "outputFormat": "png",
        "additionalOptions": {}
      },
      "type": "n8n-nodes-thumbapi.thumbApi",
      "typeVersion": 1,
      "position": [416, 80],
      "id": "4865c132-93be-4c3a-8364-fade8e1e0ca0",
      "name": "Generate a thumbnail",
      "credentials": {
        "thumbApiApi": {
          "id": "zyNQXTTfLk6jp3Tk",
          "name": "ThumbAPI account"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "const xml = $input.first().json.data;\n\n// Extract all entries\nconst entryMatches = xml.matchAll(/<entry>([\\s\\S]*?)<\\/entry>/g);\nconst entries = [...entryMatches];\n\n// Filter out shorts\nconst videos = entries.filter(e => {\n  return !e[1].includes('/shorts/');\n});\n\n// Get latest video\nconst latest = videos[0][1];\nconst videoId = latest.match(/<yt:videoId>(.*?)<\\/yt:videoId>/)[1];\nconst title = latest.match(/<title>(.*?)<\\/title>/)[1];\n\nreturn [{ json: { videoId, title } }];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [48, 80],
      "id": "5c77af43-1046-4307-8515-77ab70d71cbd",
      "name": "Code in JavaScript"
    },
    {
      "parameters": {
        "url": "=https://www.youtube.com/feeds/videos.xml?channel_id={{ $('Input values').item.json.ChanelId }}",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [-128, 80],
      "id": "015b75d1-ec21-4e81-bec0-17412ca166c0",
      "name": "HTTP Request"
    },
    {
      "parameters": {},
      "type": "n8n-nodes-base.manualTrigger",
      "typeVersion": 1,
      "position": [-512, 80],
      "id": "fad5b01b-e284-462c-a385-bfc375aa6ef6",
      "name": "When clicking 'Execute workflow'"
    }
  ],
  "connections": {
    "HTTP-Get upload link": {
      "main": [
        [
          {
            "node": "HTTP upload thumbnail",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Input values": {
      "main": [
        [
          {
            "node": "HTTP Request",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Thumbnail": {
      "main": [
        [
          {
            "node": "HTTP-Get upload link",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "VideIdSet": {
      "main": [
        [
          {
            "node": "Generate a thumbnail",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate a thumbnail": {
      "main": [
        [
          {
            "node": "Thumbnail",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code in JavaScript": {
      "main": [
        [
          {
            "node": "VideIdSet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "HTTP Request": {
      "main": [
        [
          {
            "node": "Code in JavaScript",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "When clicking 'Execute workflow'": {
      "main": [
        [
          {
            "node": "Input values",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "pinData": {},
  "meta": {
    "templateCredsSetupCompleted": true,
    "instanceId": "369c6f7060831c2832f4ab7ae05aa850d30f1cd4f9a7843cede962d50ce17b34"
  }
}

Ship It

The thumbnail step is the last manual choke point in most content pipelines. With this workflow it disappears entirely — Google handles the auth handshake, ThumbAPI handles the design, YouTube serves the result the moment your video goes live.

Start free on ThumbAPI — 50 credits per month, no credit card — paste the workflow above, swap in your channel ID, and let the pipeline run itself.

Need Help?

Stuck on the Google OAuth consent screen, getting a 403, or the upload PUT is misbehaving? Reach out — happy to help you get this running.

Aldin Kozica

Written by

Aldin Kozica

Full-stack developer from Bosnia and Herzegovina. I built ThumbAPI because I kept watching content teams waste hours on thumbnail design when the patterns are predictable enough to automate. The API is the tool I wished existed when building content pipelines for my own projects.

Generate Thumbnails With an API

Try ThumbAPI free. 50 credits per month, no credit card required. One API call, production-ready output.