# ThumbAPI — Full Reference > AI-powered thumbnail generation API. Send a title and a format, get back a production-ready thumbnail for YouTube, Instagram, X, LinkedIn, blog posts, or OG images — in a single REST API call. This file ("llms-full.txt") provides the full markdown content of ThumbAPI's documentation, blog posts, and key comparison articles in one place, for ingestion by LLM-based tools (Claude, ChatGPT, Perplexity, etc.). For a shorter index of pages, see https://thumbapi.dev/llms.txt. - Site: https://thumbapi.dev - Dashboard: https://app.thumbapi.dev - Generate endpoint: POST https://api.thumbapi.dev/v1/generate - Auth: `x-api-key` header - Free tier: 50 credits per month, no credit card required ## Documentation ### Quickstart Source: https://thumbapi.dev/docs/quickstart # Quickstart — First Thumbnail in 5 Minutes This guide gets you from zero to a generated thumbnail as fast as possible. You need: an internet connection and a terminal. ## Step 1: Get Your API Key Sign up at [thumbapi.dev](https://thumbapi.dev). Your API key is created automatically. Find it in the dashboard under **API Keys**. Your key looks like: `tb_live_abc123def456...` Or use the public test key to verify your setup without burning credits: ``` x-api-key: thumbapi_test ``` The test key returns a static placeholder image, useful for confirming your code or workflow is wired up correctly before switching to your real key. ## Step 2: Make Your First Request ### cURL ```bash curl -X POST https://api.thumbapi.dev/v1/generate \ -H "x-api-key: tb_live_YOUR_KEY" \ -H "Content-Type: application/json" \ -d '{ "title": "How to Build a SaaS in 2026", "format": "youtube", "category": "tech-saas" }' ``` ### JavaScript (Node.js) ```javascript const response = await fetch("https://api.thumbapi.dev/v1/generate", { method: "POST", headers: { "x-api-key": process.env.THUMBAPI_KEY, "Content-Type": "application/json", }, body: JSON.stringify({ title: "How to Build a SaaS in 2026", format: "youtube", category: "tech-saas", }), }); const { image, format, outputFormat } = await response.json(); // image is a base64 WebP string ``` ### Python ```python import requests import os response = requests.post( "https://api.thumbapi.dev/v1/generate", headers={ "x-api-key": os.environ['THUMBAPI_KEY'], "Content-Type": "application/json", }, json={ "title": "How to Build a SaaS in 2026", "format": "youtube", "category": "tech-saas", }, ) data = response.json() # data["image"] is a base64 WebP string ``` ## Step 3: Save the Image The response `image` field contains a base64-encoded WebP image. To save it to disk: ### Node.js ```javascript const base64Data = image.replace(/^data:image\/webp;base64,/, ""); writeFileSync("thumbnail.webp", base64Data, "base64"); ``` ### Python ```python import base64 base64_data = data["image"].split(",")[1] with open("thumbnail.webp", "wb") as f: f.write(base64.b64decode(base64_data)) ``` ## What's in the Response | Field | Type | Description | |-------|------|-------------| | `image` | string | Base64-encoded WebP or PNG image (`data:image/webp;base64,...`) | | `format` | string | The format used (`youtube`, `blogpost`, `instagram`, `x`, `linkedin`) | | `outputFormat` | string | `webp` (default) or `png` | ## Next Steps - [Authentication deep dive](/docs/authentication) — API key security, env vars, error handling - [Endpoint reference](/docs/endpoints/generate) — every parameter and option - [Error codes](/docs/errors) — what to do when things go wrong ### Authentication Source: https://thumbapi.dev/docs/authentication # Authentication ThumbAPI uses **API key authentication** via the `x-api-key` header. Every request must include your key in this header. ## Getting Your API Key 1. Sign up at [thumbapi.dev](https://thumbapi.dev). Your API key is created automatically. 2. Find it in the dashboard under **API Keys**. 3. Your key looks like: `tb_live_abc123def456...` The free tier gives you 50 credits per month. No credit card required. ### Public Test Key Want to test the integration without creating an account or burning your credits? Use the public test key: ``` x-api-key: thumbapi_test ``` This key returns a static placeholder image so you can verify your connection (n8n, Make, Zapier, or your own code) before using a real key. ## Using Your Key Include the key in every API request: ``` x-api-key: tb_live_your_api_key_here ``` ### Example Request ```bash curl -X POST https://api.thumbapi.dev/v1/generate \ -H "x-api-key: tb_live_abc123def456" \ -H "Content-Type: application/json" \ -d '{"title": "Test", "format": "youtube"}' ``` ## Security Best Practices **Never hardcode your API key.** Use environment variables: ```bash # .env THUMBAPI_KEY=tb_live_abc123def456 ``` ```javascript // Node.js const key = process.env.THUMBAPI_KEY; ``` ```python # Python import os key = os.environ["THUMBAPI_KEY"] ``` **Never expose your key in client-side code.** ThumbAPI requests should only be made from your server or backend. **Rotate your key** if you suspect it has been compromised. Generate a new key from the dashboard and update your environment variables. ## Authentication Errors | Status | Error | Cause | |--------|-------|-------| | `401` | `Missing API key` | No `x-api-key` header sent | | `401` | `Invalid API key` | Key is wrong, expired, or revoked | | `403` | `API access not enabled` | Your plan does not include API access | ## Next Steps - [Quickstart](/docs/quickstart) — make your first request - [Rate limits](/docs/rate-limits) — understand your usage limits - [Error codes](/docs/errors) — full error reference ### Endpoints Generate Source: https://thumbapi.dev/docs/endpoints/generate # POST /v1/generate Generate a thumbnail from a title. This is the only endpoint you need. ``` POST https://api.thumbapi.dev/v1/generate ``` ## Headers | Header | Required | Value | |--------|----------|-------| | `x-api-key` | Yes | `YOUR_API_KEY` | | `Content-Type` | Yes | `application/json` | ## Request Body | Field | Type | Required | Description | |-------|------|----------|-------------| | `title` | string | Yes | The title to generate a thumbnail for (max 200 characters) | | `format` | string | Yes\* | `youtube` \| `instagram` \| `x` \| `blogpost` \| `linkedin`. \*Optional when `customAssetsId` is provided — the format is taken from the saved reference set. | | `category` | string | No | Content niche — biases reference search and final tone. See [Categories](#categories) below. Default: `auto`. | | `usePhoto` | boolean | No | Include a personal photo as the main subject (uses your saved photo unless `photoImage` is provided). Default: `false`. | | `useLogo` | boolean | No | Overlay a brand logo in the corner (uses your saved logo + saved position unless `logoImage` is provided). Default: `false`. Can be combined with `usePhoto`. | | `photoImage` | string | No | Inline base64 photo (data-URL). Max 2MB. Overrides the saved photo for this request. | | `logoImage` | string | No | Inline base64 logo (data-URL). Max 1MB. Overrides the saved logo for this request. | | `customAssetsId` | string | No | ID of a saved reference set to use as the style source. The set's format is auto-applied; the AI mirrors the references at 80–100% fidelity. | | `model` | string | No | Quality: `sd` (default, 1K render) \| `hd` (2K render, Pro+ only). See [Credit cost](#credit-cost) below. | | `outputFormat` | string | No | `webp` (default) \| `png` | ### Legacy parameters (still accepted) The following older parameter names continue to work — older SDKs and the WordPress plugin can keep sending them without changes: | Legacy field | Maps to | |--------------|---------| | `imageStyle: "faceless"` | `usePhoto: false, useLogo: false` | | `imageStyle: "with-image"` *or* `"with-photo"` | `usePhoto: true` | | `imageStyle: "with-logo"` | `useLogo: true` | | `personImage` | `photoImage` (when style is `with-image`/`with-photo`) or `logoImage` (when style is `with-logo`) | | `usePersonalPhoto: true` | `usePhoto: true` using the saved asset | You cannot combine photo + logo through the legacy `imageStyle` enum — use the new `usePhoto` / `useLogo` flags for that case. ### Format Dimensions | Format | Width | Height | Aspect | |--------|-------|--------|--------| | `youtube` | 1280 | 720 | 16:9 | | `blogpost` | 1200 | 630 | 1.91:1 | | `linkedin` | 1200 | 627 | 1.91:1 | | `x` | 1200 | 675 | 16:9 | | `instagram` | 1024 | 1024 | 1:1 | ### Categories Bias the generation toward a specific niche. The category influences both the reference grid search query and a soft final-tone hint appended to the prompt — it never overrides the references. `auto` (default), `tech-saas`, `business-finance`, `education-tutorial`, `fitness-wellness`, `medical-healthcare`, `lifestyle-vlog`, `food-cooking`, `travel`, `gaming`, `entertainment-comedy`, `news-commentary`, `creative-design`. ### Photo & Logo (independent flags) `usePhoto` and `useLogo` are independent — you can use either, both, or neither. - **`usePhoto: true`** — the API uses your saved Personal Photo, or the inline `photoImage` if you provide one. The photo is fed to the model so it integrates the face into the generated scene. - **`useLogo: true`** — the API overlays your saved Logo onto the final image programmatically (sharp composite, never sent to the model). Position, size, and drop shadow come from your saved Logo config. To override, save a different config in the dashboard or pass `logoImage` inline. - **Both `true`** — face in the scene + logo overlay in the corner. If you set `usePhoto: true` (or `useLogo: true`) without inline data and have no saved asset, the API returns `400`. ### Custom Reference Sets `customAssetsId` points to a 1–6 image set you uploaded as your own style template. When provided, the AI clones your set's style at 80–100% fidelity instead of auto-discovering YouTube references. The set's stored format wins — you don't need to send `format` separately (and it's ignored if you do). ## Credit cost Each generation deducts credits from your monthly plan quota. The cost depends on the model and which add-ons you enable: | Operation | Credits | |-----------|---------| | Base generation — `model: "sd"` (1K, default) | **10** | | Base generation — `model: "hd"` (2K, Pro+ only) | **20** | | `usePhoto: true` add-on | +2 | | `useLogo: true` add-on | +2 | | `customAssetsId` add-on | +2 | Examples: - Faceless 1K thumbnail → **10 credits** - 1K thumbnail with photo → **12 credits** - 2K thumbnail with photo + logo → **24 credits** (20 + 2 + 2) - 2K thumbnail with photo + logo + reference set → **26 credits** Your plan's monthly credit allowance: | Plan | Credits / month | |------|-----------------| | Free | 50 | | Creator | 750 | | Pro | 2,500 | | Business | 10,000 | Defaults: `model: "sd"` (1K), `category: "auto"`. The 2K quality (`model: "hd"`) is gated to Pro and Business plans — sending it on Free/Creator returns `403 UPGRADE_REQUIRED`. ## Example Requests ### Faceless (default) ```bash curl -X POST https://api.thumbapi.dev/v1/generate \ -H "x-api-key: tb_live_abc123" \ -H "Content-Type: application/json" \ -d '{ "title": "10 Tips to Grow Your YouTube Channel", "format": "youtube", "category": "education-tutorial" }' ``` ### With saved photo ```bash curl -X POST https://api.thumbapi.dev/v1/generate \ -H "x-api-key: tb_live_abc123" \ -H "Content-Type: application/json" \ -d '{ "title": "How I Built a $1M SaaS", "format": "youtube", "category": "business-finance", "usePhoto": true }' ``` ### Photo + logo (both) ```bash curl -X POST https://api.thumbapi.dev/v1/generate \ -H "x-api-key: tb_live_abc123" \ -H "Content-Type: application/json" \ -d '{ "title": "My 2026 Annual Review", "format": "youtube", "usePhoto": true, "useLogo": true }' ``` ### Logo only, inline ```bash curl -X POST https://api.thumbapi.dev/v1/generate \ -H "x-api-key: tb_live_abc123" \ -H "Content-Type: application/json" \ -d '{ "title": "New Product Launch", "format": "instagram", "useLogo": true, "logoImage": "data:image/png;base64,iVBORw0KGgo..." }' ``` ### Custom reference set ```bash curl -X POST https://api.thumbapi.dev/v1/generate \ -H "x-api-key: tb_live_abc123" \ -H "Content-Type: application/json" \ -d '{ "title": "Episode 47: The Future of AI", "customAssetsId": "ref_brand_v2" }' ``` ### 2K render (Pro+) ```bash curl -X POST https://api.thumbapi.dev/v1/generate \ -H "x-api-key: tb_live_abc123" \ -H "Content-Type: application/json" \ -d '{ "title": "Annual Report 2026", "format": "linkedin", "category": "business-finance", "model": "hd" }' ``` ## Response ```json { "image": "data:image/webp;base64,UklGRp4HAA...", "format": "youtube", "outputFormat": "webp" } ``` ### Response Fields | Field | Type | Description | |-------|------|-------------| | `image` | string | Base64-encoded WebP or PNG image with data URI prefix | | `format` | string | The format that was used (echoed back, useful when `customAssetsId` set it) | | `outputFormat` | string | `webp` or `png` | ## Error Responses | Status | Cause | |--------|-------| | `400` | Missing or invalid `title`, `format`, or category; asset toggle on without a saved asset; image exceeds size cap | | `401` | Missing or invalid API key | | `403` | Plan does not include the feature (saved photos/logos and custom reference sets require Pro or Business; webhooks require Business) | | `404` | `customAssetsId` does not exist | | `429` | Rate limit or generation limit exceeded | | `500` | Server error during generation | See [Error Codes](/docs/errors) for full details. ## Common Mistakes 1. **Asset toggle without a saved asset** — `usePhoto: true` / `useLogo: true` requires either the saved asset or an inline `photoImage` / `logoImage`. Otherwise the API returns `400`. 2. **Invalid format value** — must be exactly `youtube`, `instagram`, `x`, `blogpost`, or `linkedin`. No aliases. 3. **Empty title** — the `title` field must be a non-empty string (max 200 characters). 4. **Logo too large** — `logoImage` cap is **1 MB** (smaller than the 2 MB photo cap, since logos compress well). 5. **Sending `format` with `customAssetsId`** — harmless but ignored. The reference set's stored format wins. ## Next Steps - [Custom assets guide](/docs/custom-assets) — in-depth guide to photos, logos, and reference sets - [Code examples](/docs/examples) — copy-paste snippets in 3 languages - [Rate limits](/docs/rate-limits) — understand your quota - [Webhooks](/docs/webhooks) — async generation for high-volume use ### Errors Source: https://thumbapi.dev/docs/errors # Error Codes Every error response from ThumbAPI follows this format: ```json { "error": "Human-readable error message" } ``` ## HTTP Status Codes ### 400 — Bad Request The request body is missing required fields or contains invalid values. | Error Message | Cause | Fix | |---------------|-------|-----| | `title is required` | Missing or empty `title` field | Provide a non-empty `title` string | | `format must be one of: youtube, instagram, x, blogpost, linkedin` | Invalid `format` value | Use one of the five valid format strings (or omit `format` and pass `customAssetsId`) | | `category must be one of: auto, tech-saas, business-finance, ...` | Invalid `category` value | Use one of the 13 supported category strings, or omit to default to `auto` | | `Photo asset required: set photoImage or upload a Personal Photo` | `usePhoto: true` with no saved photo and no inline `photoImage` | Upload a photo in the dashboard or pass `photoImage` | | `Logo asset required: set logoImage or upload a Logo` | `useLogo: true` with no saved logo and no inline `logoImage` | Upload a logo in the dashboard or pass `logoImage` | | `photoImage exceeds 2MB cap` | Inline photo too large | Compress the photo below 2MB | | `logoImage exceeds 1MB cap` | Inline logo too large | Compress the logo below 1MB | ### 401 — Unauthorized Authentication failed. | Error Message | Cause | Fix | |---------------|-------|-----| | `Missing API key` | No `x-api-key` header | Add `x-api-key: YOUR_KEY` | | `Invalid API key` | Key is wrong, expired, or revoked | Check your key in the dashboard | ### 403 — Forbidden | Error Message | Cause | Fix | |---------------|-------|-----| | `API access not enabled` | Your plan does not include API access | Upgrade your plan | ### 429 — Too Many Requests | Error Message | Cause | Fix | |---------------|-------|-----| | `Generation limit exceeded` | You've used all generations for this billing period | Wait for reset or upgrade plan | | `Rate limit exceeded` | Too many requests per second | Add backoff/retry logic | ### 500 — Internal Server Error | Error Message | Cause | Fix | |---------------|-------|-----| | `Internal server error` | Something went wrong on our side | Retry after a few seconds. If persistent, contact support | ## Retry Strategy For `429` and `500` errors, implement exponential backoff: ```javascript async function generateWithRetry(body, maxRetries = 3) { for (let attempt = 0; attempt < maxRetries; attempt++) { const res = await fetch("https://api.thumbapi.dev/v1/generate", { method: "POST", headers: { "x-api-key": process.env.THUMBAPI_KEY, "Content-Type": "application/json", }, body: JSON.stringify(body), }); if (res.ok) return res.json(); if (res.status === 429 || res.status >= 500) { await new Promise(r => setTimeout(r, 1000 * Math.pow(2, attempt))); continue; } // Client error — don't retry throw new Error(`API error ${res.status}: ${(await res.json()).error}`); } throw new Error("Max retries exceeded"); } ``` ## Next Steps - [Rate limits](/docs/rate-limits) — understand your quota and limits - [Authentication](/docs/authentication) — fixing auth errors - [Endpoint reference](/docs/endpoints/generate) — request format details ### Rate Limits Source: https://thumbapi.dev/docs/rate-limits # Rate Limits ThumbAPI enforces two types of limits: **monthly credit quotas** and **per-second rate limits**. ## Monthly Credit Quotas Pricing is metered in credits. A standard 1K thumbnail costs **10 credits**; add-ons (`usePhoto`, `useLogo`, `customAssetsId`) add **+2 each**; a 2K (`hd`) render costs **20 credits** instead of 10. See [credit cost details](/docs/endpoints/generate#credit-cost). | Plan | Credits / Month | Price | Standard 1K thumbnails* | |------|-----------------|-------|--------------------------| | Free | 50 | $0 | ~5 | | Creator | 750 | $19/month | ~75 | | Pro | 2,500 | $49/month | ~250 | | Business | 10,000 | $199/month | ~1,000 | \*Approximate count assumes base 1K renders with no add-ons. Actual count depends on the mix of `sd`/`hd` and add-on flags you use. Credits reset on the first day of each billing period. Unused credits do not roll over. Free-tier output carries a small ThumbAPI watermark; Creator and higher plans return watermark-free images. ## Rate Limits | Plan | Requests / Second | |------|-------------------| | Free | 1 | | Creator | 2 | | Pro | 5 | | Business | 20 | Rate limits apply per API key. ## Response Headers Every response includes credit-quota information: ``` X-RateLimit-Limit: 2500 X-RateLimit-Remaining: 1847 X-RateLimit-Reset: 2026-05-01T00:00:00Z ``` | Header | Description | |--------|-------------| | `X-RateLimit-Limit` | Total credits allowed this billing period | | `X-RateLimit-Remaining` | Credits remaining this billing period | | `X-RateLimit-Reset` | When the quota resets (ISO 8601) | ## When You Hit a Limit **Credit quota exceeded:** HTTP `429` with `"Generation limit exceeded"`. Wait for the next billing period or upgrade your plan. **Per-second rate limit exceeded:** HTTP `429` with `"Rate limit exceeded"`. Implement backoff and retry. ## Best Practices 1. **Check `X-RateLimit-Remaining`** before making requests in batch jobs 2. **Implement exponential backoff** for 429 responses (see [Error Codes](/docs/errors)) 3. **Cache generated thumbnails** — don't regenerate the same title twice 4. **Use webhooks** (Pro+) for async generation instead of polling ## Upgrading Upgrade your plan at any time from the [dashboard](https://app.thumbapi.dev). Changes take effect immediately — no proration for the current period. ## Next Steps - [Error codes](/docs/errors) — handling 429 and other errors - [Webhooks](/docs/webhooks) — async generation for high volume - [Pricing](/#pricing) — compare plans ### Webhooks Source: https://thumbapi.dev/docs/webhooks # Webhooks Webhooks let you generate thumbnails asynchronously. Instead of waiting for the response, ThumbAPI sends the result to your callback URL when the thumbnail is ready. **Available on Pro and Business plans.** ## How It Works 1. You send a generate request with a `webhookUrl` field 2. The API returns `202 Accepted` immediately with a `jobId` 3. When the thumbnail is ready, ThumbAPI POSTs the result to your `webhookUrl` ## Sending a Webhook Request Add `webhookUrl` to your normal generate request: ```bash curl -X POST https://api.thumbapi.dev/v1/generate \ -H "x-api-key: tb_live_abc123" \ -H "Content-Type: application/json" \ -d '{ "title": "How to Build a SaaS", "format": "youtube", "category": "tech-saas", "webhookUrl": "https://your-app.com/api/webhooks/thumbapi" }' ``` ### Immediate Response (202) ```json { "jobId": "job_a1b2c3d4e5", "status": "processing", "webhookUrl": "https://your-app.com/api/webhooks/thumbapi" } ``` ## Webhook Payload When the thumbnail is ready, ThumbAPI sends a POST to your URL: ```json { "event": "generation.completed", "jobId": "job_a1b2c3d4e5", "data": { "image": "data:image/webp;base64,UklGRp4HAA...", "format": "youtube", "outputFormat": "webp" }, "timestamp": "2026-04-27T14:30:00Z" } ``` ### Failed Generation ```json { "event": "generation.failed", "jobId": "job_a1b2c3d4e5", "error": "Image generation timed out", "timestamp": "2026-04-27T14:30:45Z" } ``` ## Handling Webhooks Your endpoint must return `200` within 10 seconds. If it doesn't, ThumbAPI retries up to 3 times with exponential backoff. ### Node.js / Express Example ```javascript app.post("/api/webhooks/thumbapi", (req, res) => { const { event, jobId, data, error } = req.body; if (event === "generation.completed") { // Save the thumbnail saveImage(jobId, data.image); } else if (event === "generation.failed") { console.error(`Job ${jobId} failed: ${error}`); } res.sendStatus(200); // Always acknowledge }); ``` ## Security Verify webhook authenticity by checking the `X-ThumbAPI-Signature` header. This is an HMAC-SHA256 signature of the request body using your API key as the secret. ```javascript function verifyWebhook(body, signature, apiKey) { const expected = crypto .createHmac("sha256", apiKey) .update(JSON.stringify(body)) .digest("hex"); return crypto.timingSafeEqual( Buffer.from(signature), Buffer.from(expected) ); } ``` ## Next Steps - [Rate limits](/docs/rate-limits) — webhook requests count toward your quota - [Error codes](/docs/errors) — handling failed generations - [SDKs](/docs/sdks) — webhook support is built into the official SDKs ### Sdks Source: https://thumbapi.dev/docs/sdks # SDKs Official client libraries for ThumbAPI. These are thin wrappers around the REST API — you can always use the API directly with `fetch` or `requests`. ## JavaScript / Node.js ```bash npm install thumbapi ``` ```javascript const client = new ThumbAPI(process.env.THUMBAPI_KEY); const result = await client.generate({ title: "10 Tips to Grow Your Channel", format: "youtube", category: "education-tutorial", usePhoto: true, // optional — overlay your saved photo }); console.log(result.image); // base64 WebP console.log(result.outputFormat); // "webp" | "png" ``` ### Features - TypeScript types included - Automatic retry with exponential backoff - Configurable timeout - ESM and CJS support ## Python ```bash pip install thumbapi ``` ```python from thumbapi import ThumbAPI client = ThumbAPI(api_key=os.environ["THUMBAPI_KEY"]) result = client.generate( title="10 Tips to Grow Your Channel", format="youtube", category="education-tutorial", use_photo=True, # optional — overlay your saved photo ) print(result.image) # base64 WebP print(result.output_format) # "webp" | "png" ``` ### Features - Type hints included - Automatic retry with backoff - Sync and async support (`AsyncThumbAPI`) - Python 3.8+ Prefer raw HTTP with `requests` or `httpx`? The [Python thumbnail API tutorial](/blog/thumbnail-api-python) walks through sync, async, retries, and FastAPI/Flask integration without the SDK. ## Direct API Usage Don't want a library? The API is a single POST endpoint. See the [quickstart](/docs/quickstart) for raw `curl`, `fetch`, and `requests` examples. ## Coming Soon - **Go** SDK - **Ruby** SDK - **PHP** SDK [Request an SDK →](https://github.com/thumbapi/docs/issues) ## Next Steps - [Quickstart](/docs/quickstart) — first thumbnail in 5 minutes - [Endpoint reference](/docs/endpoints/generate) — raw API details - [Code examples](/docs/examples) — copy-paste snippets ### Examples Source: https://thumbapi.dev/docs/examples # Code Examples Copy-paste examples for common use cases. Every example works with the free tier. ## YouTube Thumbnail (Faceless) ### cURL ```bash curl -X POST https://api.thumbapi.dev/v1/generate \ -H "x-api-key: $THUMBAPI_KEY" \ -H "Content-Type: application/json" \ -d '{ "title": "10 Tips to Grow Your YouTube Channel", "format": "youtube", "category": "education-tutorial" }' ``` ### JavaScript ```javascript const res = await fetch("https://api.thumbapi.dev/v1/generate", { method: "POST", headers: { "x-api-key": process.env.THUMBAPI_KEY, "Content-Type": "application/json", }, body: JSON.stringify({ title: "10 Tips to Grow Your YouTube Channel", format: "youtube", category: "education-tutorial", }), }); const { image, format, outputFormat } = await res.json(); ``` ### Python ```python import requests, os res = requests.post( "https://api.thumbapi.dev/v1/generate", headers={"x-api-key": os.environ['THUMBAPI_KEY']}, json={ "title": "10 Tips to Grow Your YouTube Channel", "format": "youtube", "category": "education-tutorial", }, ) data = res.json() ``` ## OG Image (Blog Post) ```bash curl -X POST https://api.thumbapi.dev/v1/generate \ -H "x-api-key: $THUMBAPI_KEY" \ -H "Content-Type: application/json" \ -d '{ "title": "Understanding Vector Databases: A Practical Guide", "format": "blogpost" }' ``` Returns a 1200x630 image — the standard OG image size. ## LinkedIn Post ```bash curl -X POST https://api.thumbapi.dev/v1/generate \ -H "x-api-key: $THUMBAPI_KEY" \ -H "Content-Type: application/json" \ -d '{ "title": "How We Cut Onboarding Time in Half", "format": "linkedin", "category": "business-finance" }' ``` Returns a 1200x627 image, sized for LinkedIn feed shares. ## Instagram Post ```bash curl -X POST https://api.thumbapi.dev/v1/generate \ -H "x-api-key: $THUMBAPI_KEY" \ -H "Content-Type: application/json" \ -d '{ "title": "5 Morning Habits That Changed My Life", "format": "instagram" }' ``` Returns a 1024x1024 square image. ## With Saved Photo ```javascript const res = await fetch("https://api.thumbapi.dev/v1/generate", { method: "POST", headers: { "x-api-key": process.env.THUMBAPI_KEY, "Content-Type": "application/json", }, body: JSON.stringify({ title: "My Journey to 1M Subscribers", format: "youtube", usePhoto: true, }), }); ``` Pulls your saved Personal Photo from the dashboard — no need to upload per request. ## With Inline Photo ```javascript const photoBase64 = readFileSync("./photo.jpg", "base64"); const res = await fetch("https://api.thumbapi.dev/v1/generate", { method: "POST", headers: { "x-api-key": process.env.THUMBAPI_KEY, "Content-Type": "application/json", }, body: JSON.stringify({ title: "My Journey to 1M Subscribers", format: "youtube", usePhoto: true, photoImage: `data:image/jpeg;base64,${photoBase64}`, }), }); ``` `photoImage` is capped at 2MB. For logos use `logoImage` (capped at 1MB). ## Photo + Logo ```bash curl -X POST https://api.thumbapi.dev/v1/generate \ -H "x-api-key: $THUMBAPI_KEY" \ -H "Content-Type: application/json" \ -d '{ "title": "My 2026 Annual Review", "format": "youtube", "usePhoto": true, "useLogo": true }' ``` `usePhoto` and `useLogo` are independent — combine them for a personalized scene with a branded logo overlay. ## Custom Reference Set ```bash curl -X POST https://api.thumbapi.dev/v1/generate \ -H "x-api-key: $THUMBAPI_KEY" \ -H "Content-Type: application/json" \ -d '{ "title": "Episode 47: The Future of AI", "customAssetsId": "ref_brand_v2" }' ``` `format` is optional when `customAssetsId` is provided — the reference set's stored format wins. ## Save to Disk ### Node.js ```javascript const { image } = await res.json(); const base64 = image.replace(/^data:image\/webp;base64,/, ""); writeFileSync("thumbnail.webp", base64, "base64"); ``` ### Python ```python import base64 b64 = data["image"].split(",")[1] with open("thumbnail.webp", "wb") as f: f.write(base64.b64decode(b64)) ``` ## Error Handling ```javascript const res = await fetch("https://api.thumbapi.dev/v1/generate", { method: "POST", headers: { "x-api-key": process.env.THUMBAPI_KEY, "Content-Type": "application/json", }, body: JSON.stringify(requestBody), }); if (!res.ok) { const { error } = await res.json(); if (res.status === 429) { // Rate limited — retry with backoff } else if (res.status === 401) { // Auth error — check API key } else { throw new Error(`ThumbAPI error ${res.status}: ${error}`); } } ``` ## Next Steps - [Endpoint reference](/docs/endpoints/generate) — all parameters explained - [SDKs](/docs/sdks) — official libraries with built-in retry - [Webhooks](/docs/webhooks) — async generation for pipelines ### Custom Assets Source: https://thumbapi.dev/docs/custom-assets # Custom Assets ThumbAPI lets you save assets once and reuse them across every API call. Instead of encoding and sending an image with each request, upload it to the dashboard and reference it with a single parameter. There are three types of assets: | Asset type | What it does | API parameter | |---|---|---| | **Personal Photo** | Your face as the main subject of the thumbnail | `usePhoto: true` | | **Logo** | Your brand mark overlaid in the corner | `useLogo: true` | | **Reference Dataset** | 1–6 style reference images the AI uses as visual inspiration | `customAssetsId: "your_id"` | `usePhoto` and `useLogo` are independent — you can use either, both, or neither on the same request. All assets are managed in the [ThumbAPI dashboard](https://app.thumbapi.dev) under **Assets**. --- ## Personal Photo Use this when you want your face in thumbnails but don't want to send a base64 image on every request. ### Upload 1. Go to [app.thumbapi.dev](https://app.thumbapi.dev) → **Assets** → **Personal Photo** 2. Upload a clear, well-lit headshot (JPEG or PNG, max 2MB) 3. Save ### Use in API Calls ```json { "title": "My Journey to 1M Subscribers", "format": "youtube", "category": "lifestyle-vlog", "usePhoto": true } ``` The API pulls your saved photo automatically. No need to encode or send the image — just set `usePhoto: true`. ### Inline Alternative If you'd rather send the photo directly (e.g., for different photos per request), use `photoImage` instead: ```json { "title": "My Journey to 1M Subscribers", "format": "youtube", "usePhoto": true, "photoImage": "data:image/jpeg;base64,/9j/4AAQ..." } ``` The image must be under **2MB**. JPEG, PNG, and WebP are supported. --- ## Logo For businesses and branded channels that want their logo in every thumbnail. Logos are composited onto the final image programmatically — sharp, with the saved position, size, and drop shadow from your dashboard config. ### Upload 1. Go to [app.thumbapi.dev](https://app.thumbapi.dev) → **Assets** → **Logo** 2. Upload your logo (PNG with transparency works best, max 1MB) 3. Save ### Use in API Calls ```json { "title": "Q1 2026 Product Update", "format": "blogpost", "category": "business-finance", "useLogo": true } ``` The API composites your logo into the thumbnail design. This is ideal for: - Company blog OG images - Branded social media cards - Product launch thumbnails - Newsletter header images ### Inline Alternative You can send a logo inline via `logoImage` if you need different logos per request: ```json { "title": "Q1 2026 Product Update", "format": "blogpost", "useLogo": true, "logoImage": "data:image/png;base64,iVBORw0KGgo..." } ``` The image must be under **1MB** (logos compress well, so this is smaller than the 2MB photo cap). --- ## Photo + Logo Together `usePhoto` and `useLogo` are independent flags, so you can combine them on the same request to get your face in the scene plus your brand logo overlaid in the corner. ```json { "title": "My 2026 Annual Review", "format": "youtube", "usePhoto": true, "useLogo": true } ``` You can mix saved + inline freely — e.g. `usePhoto: true` (saved photo) with `useLogo: true` and a per-request `logoImage`. --- ## Reference Dataset A reference dataset is a collection of 1–6 images that define a visual style. When you include a dataset ID in your request, the AI clones your set's style at 80–100% fidelity — matching color palette, visual tone, typography weight, and layout energy to your references. This is different from a template. A template forces a fixed layout. A dataset guides style while still generating a unique composition for each title. ### When to Use a Dataset - You have 5+ published videos and want to keep a consistent thumbnail look - You're onboarding a new channel to automation and want to match an existing visual style - You manage multiple channels that each need a distinct identity - You're a business generating OG images and want them to match your brand guidelines ### Upload 1. Go to [app.thumbapi.dev](https://app.thumbapi.dev) → **Assets** → **Reference Sets** 2. Create a new dataset and name it (e.g., "Finance Channel — Clean Dark Style") 3. Pick the format the set is for (`youtube`, `blogpost`, `linkedin`, `instagram`, or `x`) 4. Upload 1–6 reference images — these can be your best existing thumbnails, competitor examples you want to emulate, or custom mockups 5. Save — you'll get a dataset ID like `m6XhjtZNdF0N2AFXUOiq` **Tips for good references:** - Quality over quantity — 3 well-chosen images beat 6 inconsistent ones - Pick images that share a similar color palette, contrast level, and typography weight - Include the kind of compositions you want (centered text, split layouts, etc.) ### Use in API Calls ```json { "title": "How the Roman Empire Actually Collapsed", "customAssetsId": "m6XhjtZNdF0N2AFXUOiq" } ``` That single parameter is the only change from a standard request. `format` is optional when `customAssetsId` is set — the set's stored format wins (and any `format` you send is ignored). ### Combining with Other Assets You can use a dataset together with a personal photo, a logo, or both: ```json { "title": "Why I Quit My 9-to-5", "usePhoto": true, "useLogo": true, "customAssetsId": "m6XhjtZNdF0N2AFXUOiq" } ``` This generates a thumbnail with your face in the scene, your logo in the corner, all styled to match your reference dataset. ### Managing Multiple Datasets Create a separate dataset for each channel, brand, or visual direction. Store the IDs in your application config and pass the right one per request: ```javascript const CHANNELS = { history: "m6XhjtZNdF0N2AFXUOiq", finance: "pQ9RksTmBv3X7YZWLNcj", trueCrime: "hN2VwxDkGe5A8CUJMPqr", }; const res = await fetch("https://api.thumbapi.dev/v1/generate", { method: "POST", headers: { "x-api-key": process.env.THUMBAPI_KEY, "Content-Type": "application/json", }, body: JSON.stringify({ title: videoTitle, customAssetsId: CHANNELS[channelName], }), }); ``` ### Updating a Dataset If you rebrand or change visual direction, upload new reference images and replace the dataset. Old thumbnails keep their original style. New ones follow the updated direction — no regeneration needed. --- ## Legacy Parameter Names The older `imageStyle` / `personImage` / `usePersonalPhoto` parameters still work for backward compatibility — existing SDKs and the WordPress plugin keep generating without changes: | Legacy field | New equivalent | |---|---| | `imageStyle: "faceless"` | Omit both flags (default) | | `imageStyle: "with-image"` *or* `"with-photo"` | `usePhoto: true` | | `imageStyle: "with-logo"` | `useLogo: true` | | `personImage` (with photo style) | `photoImage` | | `personImage` (with logo style) | `logoImage` | | `usePersonalPhoto: true` | `usePhoto: true` | You cannot combine photo + logo through the legacy `imageStyle` enum — use the new `usePhoto` / `useLogo` flags for that case. --- ## Error Handling | Scenario | Status | Message | |---|---|---| | `usePhoto: true` with no saved photo and no `photoImage` | `400` | Upload a personal photo in the dashboard first | | `useLogo: true` with no saved logo and no `logoImage` | `400` | Upload a logo in the dashboard first | | Invalid `customAssetsId` | `404` | Reference set not found | | `photoImage` over 2MB | `400` | Image too large (max 2MB) | | `logoImage` over 1MB | `400` | Image too large (max 1MB) | --- ## Next Steps - [POST /v1/generate reference](/docs/endpoints/generate) — full parameter list - [Code examples](/docs/examples) — copy-paste snippets in cURL, JS, Python - [Custom asset datasets blog post](/blog/custom-asset-datasets-brand-consistency) — in-depth guide with batch processing examples ## Comparisons ### ThumbAPI vs AI Generative Models: What Is Better? Source: https://thumbapi.dev/vs/ai-generative-models A practical comparison between ThumbAPI and AI generative models, focusing on context grounding, dataset retrieval, and hallucination reduction in visual workflows. # ThumbAPI vs AI Generative Models: What Is Better? When building systems for image generation, thumbnail creation, or visual intelligence, the real distinction is not simply “AI vs non-AI”. It is **general-purpose LLM generation vs LLMs guided by structured, real-world datasets**. Both approaches often use similar underlying model technology. The difference is how much **context and grounding** the model receives during generation. --- ## How AI Generative Models Work Modern generative models are typically built as large-scale LLMs or diffusion systems trained on broad datasets. They are designed to be **generalists**. This means: - They can handle many different tasks - They rely heavily on learned statistical patterns - They do not inherently have access to real-time external context ### The Context Limitation A key constraint is not capability, but **context precision**. When you prompt a model to generate or describe a visual concept, it: - Interprets the prompt in isolation - Reconstructs likely outputs based on training distribution - Does not necessarily anchor output to a specific real-world dataset entry In practice, this can lead to: - Generic or “averaged” visuals - Inconsistent specificity across runs - Outputs that are plausible but not tied to a concrete reference point This is not a failure of the model — it is a consequence of it being designed as a **general-purpose system**. --- ## How ThumbAPI Uses LLMs Differently ThumbAPI also uses LLM-based components, but in a different architecture. Instead of relying only on prompt-to-output generation, it introduces a **dataset grounding layer**. ### LLM + Retrieval + Dataset Context In ThumbAPI, the LLM is not operating in isolation. It is guided by: - Live and indexed datasets (e.g. YouTube, Google Images, trending visual content) - Structured metadata tied to real content - Retrieval-based context injection before generation This means the LLM is working with **anchored inputs**, not only abstract prompts. --- ## Why Context Matters More Than Model Size A larger model is not automatically a better system. What often matters more is: - quality of input context - relevance of retrieved examples - grounding in real-world data Without that, even powerful models tend to converge toward: - generic interpretations - “average-looking” outputs - lower specificity in production use cases --- ## Hallucination vs Grounded Generation Hallucination in generative systems is often misunderstood. It is not only about “wrong facts”, but also about: - incorrect visual assumptions - invented styles or compositions ignoring real-world constraints ### Pure LLM approach - Generates based on probability distribution - No guarantee of reference accuracy ### ThumbAPI approach - Uses dataset retrieval to constrain generation space - LLM operates inside a defined context window of real examples Result: - Reduced likelihood of unsupported outputs - Higher consistency with actual content patterns --- ## A More Accurate Comparison For a concrete walkthrough of how the dataset grounding layer works in practice, see the [custom asset datasets documentation](/docs/custom-assets) and the deeper write-up in [Custom Asset Datasets for Brand Consistency](/blog/custom-asset-datasets-brand-consistency). --- ## Pros and Cons --- ## When Each Approach Makes Sense ### Generative models are strong when: - you need creative exploration - there is no fixed reference point - variability is desirable ### ThumbAPI is stronger when: - outputs must align with real-world content - consistency across large-scale workflows is required - visual results depend on actual trending or existing media --- ## Conclusion The difference is not that one system “has AI” and the other does not. Both rely on LLMs. The difference is **how much structured context the LLM receives before generation**. ThumbAPI improves reliability by combining: - LLM reasoning - dataset retrieval - real-world visual grounding While general-purpose generative models prioritize flexibility, ThumbAPI prioritizes **contextual accuracy and consistency in production environments**. ## Blog ### Automating Content Creation: A Developer's Guide Source: https://thumbapi.dev/blog/automating-content-creation A practical, developer-focused guide to automating content creation pipelines with APIs, including thumbnail generation, scheduling, and integration examples. Content creation at scale is an engineering problem. Once you move past one person making one piece of content at a time, you are dealing with pipelines: research, writing, editing, asset creation, formatting, publishing, and distribution. Each step has inputs, outputs, and constraints, and many of those steps are repetitive, rule-based, and ripe for automation. This guide is aimed at developers who want to automate parts of the content pipeline with real code and real integration patterns, not vague advice about "leveraging AI". ## Why Automate Content Creation The argument for automation is straightforward: time and consistency. A solo content creator publishing one blog post per week typically spends half a workday or more per piece once you add up research, writing, editing, cover image, formatting, uploading, and distribution. Scaling that to daily publication means hiring people, accepting lower quality, or automating the parts of the pipeline that don't actually require a human: formatting, image generation, scheduling, cross-posting. The creative and strategic decisions stay with you; the mechanical parts go to scripts. Consistency is equally important. Manual processes introduce variability: the cover image quality varies depending on how much time the designer had, the metadata is sometimes incomplete, the distribution steps are sometimes forgotten. Automated pipelines execute the same steps the same way every time, which means consistent quality, consistent metadata, and consistent distribution. ## What to Automate (and What Not To) Not every step in the content pipeline should be automated. The decision depends on two factors: how repetitive the step is, and how much creative judgment it requires. ### High-Value Automation Targets - **Thumbnail and cover image generation:** Highly repetitive, follows well-defined patterns, and needs design execution rather than strategic creativity. This is the single highest-ROI automation target for most content operations. - **Metadata generation:** SEO titles, meta descriptions, alt text, tags, and categories can be generated from the content itself with high accuracy. - **Cross-platform formatting:** Reformatting content from one platform to another (blog post to social media thread, video description to article summary) follows predictable rules. - **Publishing and distribution:** Uploading to CMS platforms, posting to social media, sending newsletters. All mechanical steps that are perfectly suited for automation. - **Scheduling:** Determining optimal publish times based on audience data and managing the editorial calendar. - **Analytics collection:** Pulling performance data from multiple platforms into a unified dashboard. ### Better Left to Humans - **Strategic content planning:** Deciding what to create, for whom, and why. This requires understanding of audience needs, competitive positioning, and business goals. - **Core creative writing:** The actual words of a blog post, the script of a video, the narrative structure. AI can assist with drafts and outlines, but the editorial voice and strategic framing should be human-directed. - **Quality review:** Final review before publication to catch factual errors, tone issues, and brand alignment problems. ## APIs for Content Automation The building blocks of a content automation pipeline are APIs. Here are the key categories: ### AI and Language APIs Large language model APIs (OpenAI, Anthropic, Google) can generate drafts, summaries, metadata, social posts, and email copy from a source document. The pattern is straightforward: send the source content as context along with a specific instruction, and receive structured output. For reliable output, get specific in your prompts and use structured output formats (JSON mode). Instead of asking for "a social media post," ask for a JSON object with fields for the post text, hashtags, and suggested publish time, with specific constraints on length and tone. ### Thumbnail and Image APIs Manual image creation is one of the biggest bottlenecks in content pipelines. ThumbAPI solves this specifically for thumbnails: send a POST request to `/v1/generate` with your title and format, and receive a production-ready thumbnail image. The API supports YouTube, Instagram, X, and blog post formats with multiple image styles. For general-purpose image generation, tools like DALL-E and Midjourney offer APIs, but they require more prompt engineering to produce consistent, on-brand results. ThumbAPI is purpose-built for thumbnails, which means the output is optimized for click-through rate without manual prompt tuning. ### CMS and Publishing APIs WordPress, Ghost, Contentful, Sanity, and most modern CMS platforms expose APIs for creating and updating content. These APIs allow you to programmatically create posts, upload images, set metadata, and publish content without manual intervention. ### Distribution APIs Social media platforms (Twitter/X API, LinkedIn API, Facebook Graph API), email platforms (SendGrid, Mailchimp, Resend), and messaging platforms (Slack, Discord) all offer APIs for programmatic publishing. These are the final step in the pipeline: getting the content in front of an audience. ## Thumbnail Automation With ThumbAPI Here's a concrete example: automating thumbnail generation inside a blog publishing pipeline. ### The Use Case You have a blog powered by a headless CMS. When a writer publishes a new article, the system should automatically generate a cover image, attach it to the post, and publish. No manual design step, no delay. ### The Implementation The integration pattern is a webhook-triggered function. When the CMS publishes a new post, it fires a webhook. Your function receives the webhook, extracts the title, calls ThumbAPI to generate a thumbnail, and updates the post with the generated image. Here's a simplified implementation in Node.js: ```ts // Webhook handler for new blog posts export async function handleNewPost(post: { id: string; title: string; slug: string; }) { // 1. Generate thumbnail via ThumbAPI const thumbResponse = await fetch( "https://api.thumbapi.dev/v1/generate", { method: "POST", headers: { "x-api-key": process.env.THUMBAPI_KEY, "Content-Type": "application/json", }, body: JSON.stringify({ title: post.title, format: "blogpost", imageStyle: "faceless", }), } ); const { image } = await thumbResponse.json(); // 2. Decode base64 image and upload to CMS // image is a data URL (`data:image/webp;base64,...`), so strip the prefix first. const base64 = image.split(",")[1]; const imageBuffer = Buffer.from(base64, "base64"); const imageUrl = await uploadToCMS(imageBuffer, post.slug); // 3. Update the post with the cover image await updatePost(post.id, { coverImage: imageUrl }); } ``` This pattern works with any CMS that supports webhooks and API-based image uploads. The specific CMS integration code varies, but the ThumbAPI call is the same regardless of your CMS. ### Scaling to Multiple Platforms The same pattern extends to multi-platform publishing. When a new post is published, generate thumbnails for each platform in parallel: ```ts // Generate thumbnails for all platforms in parallel const formats = ["blogpost", "youtube", "instagram", "x"]; const thumbnails = await Promise.all( formats.map(async (format) => { const res = await fetch( "https://api.thumbapi.dev/v1/generate", { method: "POST", headers: { "x-api-key": process.env.THUMBAPI_KEY, "Content-Type": "application/json", }, body: JSON.stringify({ title: post.title, format, imageStyle: "faceless", }), } ); const data = await res.json(); return { format, image: data.image }; }) ); // Each thumbnail is correctly sized for its platform: // blogpost: 1200x630, youtube: 1280x720, // instagram: 1080x1080, x: 1200x675 ``` This generates four platform-optimized thumbnails from a single title, each correctly sized and composed for its target platform. ## Integration With Workflow Automation Tools Not every automation needs custom code. Workflow automation platforms like Zapier and n8n allow you to build content pipelines visually, connecting APIs through a drag-and-drop interface. ### Zapier A typical Zapier workflow for content automation: 1. **Trigger:** New post published in WordPress/Ghost/Notion 1. **Action:** Call ThumbAPI to generate a thumbnail 1. **Action:** Upload the thumbnail to cloud storage 1. **Action:** Update the post with the thumbnail URL 1. **Action:** Post to Twitter/X with the generated thumbnail 1. **Action:** Post to LinkedIn with a different format thumbnail This entire workflow runs automatically every time you publish. No manual steps, no design work, no platform-specific resizing. ### n8n n8n offers the same workflow capabilities with more technical flexibility. If you want more control over the logic (conditional branching, data transformation, error handling), n8n is a code-friendly alternative to Zapier. The ThumbAPI integration works the same way: an HTTP request node calls the API, and subsequent nodes handle the response. ## Building a Full Content Pipeline Here's a complete content pipeline architecture for a blog that publishes daily: 1. **Content queue:** Writers submit articles to a headless CMS (Contentful, Sanity, Ghost) with status "draft." 1. **Editorial review:** An editor reviews and approves the draft, changing status to "approved." 1. **Automation trigger:** A webhook fires when status changes to "approved." 1. **Metadata generation:** An LLM API generates SEO title, meta description, tags, and social media copy from the article text. 1. **Thumbnail generation:** ThumbAPI generates cover images for blog, social media, and newsletter formats. 1. **CMS update:** The post is updated with generated metadata and images, and status changes to "scheduled." 1. **Publishing:** At the scheduled time, the CMS publishes the post. 1. **Distribution:** Social media posts and newsletter entries are created automatically using the pre-generated copy and platform-specific thumbnails. 1. **Analytics:** Performance data is pulled daily into a dashboard for editorial team review. In this pipeline, the human work is concentrated in steps 1 and 2: creating the content and reviewing it. Everything from step 3 onward is automated. The result is a daily publishing operation that requires one writer and one editor, not a writer, an editor, a designer, a social media manager, and a newsletter operator. ## Error Handling and Reliability Automated pipelines need robust error handling. API calls fail, webhooks get dropped, rate limits get hit. Here are the patterns that make content automation reliable: - **Retry with backoff:** When an API call fails, retry with exponential backoff (1s, 2s, 4s, 8s). ThumbAPI returns standard HTTP status codes, so you can distinguish between retryable errors (429, 503) and permanent failures (400, 401). - **Idempotency:** Design your pipeline so that running the same step twice produces the same result. If the thumbnail generation step runs twice for the same post, the second run should overwrite the first instead of creating a duplicate. - **Dead letter queues:** When a step fails after all retries, route the failed item to a dead letter queue for manual review instead of silently dropping it. - **Monitoring and alerts:** Set up alerts for pipeline failures, unusual latency, and rate limit warnings. You want to know something is broken before your publishing schedule gets hit. ## Getting Started If you are new to content automation, start small. Pick the single most time-consuming mechanical step in your workflow (for most people that's thumbnail or cover image creation) and automate it. Validate that the automated output meets your quality bar. Then expand to the next step. ThumbAPI is designed to be an easy starting point: one API call, one endpoint, production-ready output. The free tier gives you 50 credits per month to validate the approach before committing. Combined with a workflow tool like Zapier or n8n, you can have a working thumbnail automation pipeline in under an hour. Automating content isn't about replacing the parts that need taste and judgment. It's about getting the boring parts (resizing, formatting, publishing, distribution) off your plate so the hours you spend on content go into writing and strategy instead of fighting export settings in Canva. [Start free with 50 credits per month](https://app.thumbapi.dev/signup) and wire the thumbnail step into your pipeline this afternoon. ### Batch Thumbnail Generation for 100 Videos with Python Source: https://thumbapi.dev/blog/batch-thumbnail-generation-python Build a Python script that processes a CSV of video titles and generates production-ready thumbnails for all of them in a single run using ThumbAPI. One thumbnail in Photoshop takes me maybe three minutes if I already have a layered template open. A hundred thumbnails in one sitting is the kind of task I'll abandon halfway through. This guide is the Python script I actually use when a client drops a spreadsheet of titles on me and wants the artwork by morning. CSV of titles in, folder of thumbnails out, via ThumbAPI. By the end you'll have something you can drop straight into a content pipeline without touching it again. ## What We're Building A Python script that: 1. Reads a list of video titles from a CSV file 2. Calls the ThumbAPI generate endpoint for each title 3. Saves each thumbnail to disk with a clean filename 4. Handles rate limits and errors without crashing 5. Logs results so you know what succeeded and what failed It's the same shape of script I've shipped to two different agencies. Nothing fancy. The boring parts (retries, idempotent re-runs, slugged filenames) are what actually make it survive contact with a real content calendar. If you're new to calling ThumbAPI from Python and want the single-request, async, and FastAPI/Flask variants first, the [Python thumbnail API tutorial](/blog/thumbnail-api-python) is the on-ramp to this batch script. ## Prerequisites - Python 3.8 or higher - A ThumbAPI API key — [start free with 50 credits per month](https://app.thumbapi.dev/signup) - The `requests` library (`pip install requests`) ## Step 1 — Prepare Your Input CSV Create a file called `videos.csv` with your video titles: ```csv title,format,style "How the Roman Empire Actually Collapsed",youtube,faceless "The Deepest Cave Ever Explored",youtube,faceless "Why the Stock Market Crashes Every Decade",youtube,faceless "The Psychology of Persuasion Explained",youtube,faceless "10 Ancient Civilizations Nobody Talks About",youtube,faceless ``` Adding `format` and `style` columns gives you flexibility to mix YouTube, blog, and Instagram thumbnails in a single batch run. ## Step 2 — The Batch Generation Script ```python import requests import base64 import csv import time import logging from pathlib import Path # --- Config --- API_KEY = "your_api_key_here" API_URL = "https://api.thumbapi.dev/v1/generate" INPUT_CSV = "videos.csv" OUTPUT_DIR = Path("thumbnails") DELAY_BETWEEN_REQUESTS = 2 # seconds CUSTOM_ASSETS_ID = None # set your dataset ID here if using brand styles # --- Setup --- OUTPUT_DIR.mkdir(exist_ok=True) logging.basicConfig( level=logging.INFO, format="%(asctime)s — %(levelname)s — %(message)s", handlers=[ logging.FileHandler("batch_run.log"), logging.StreamHandler() ] ) def generate_thumbnail(title, format="youtube", style="faceless"): payload = { "title": title, "format": format, "imageStyle": style, "outputFormat": "webp" } if CUSTOM_ASSETS_ID: payload["customAssetsId"] = CUSTOM_ASSETS_ID try: response = requests.post( API_URL, headers={ "x-api-key": API_KEY, "Content-Type": "application/json" }, json=payload, timeout=60 ) response.raise_for_status() data = response.json() image_b64 = data["image"].split(",")[1] return base64.b64decode(image_b64) except requests.exceptions.HTTPError as e: logging.error(f"HTTP error for '{title}': {e.response.status_code}") return None except Exception as e: logging.error(f"Unexpected error for '{title}': {e}") return None def slugify(text): return text.lower().replace(" ", "-").replace("'", "").replace(",", "")[:60] def run_batch(): with open(INPUT_CSV, newline="", encoding="utf-8") as f: reader = csv.DictReader(f) rows = list(reader) total = len(rows) success = 0 failed = 0 logging.info(f"Starting batch — {total} thumbnails to generate") for i, row in enumerate(rows, 1): title = row["title"].strip() fmt = row.get("format", "youtube").strip() style = row.get("style", "faceless").strip() filename = f"{i:03d}-{slugify(title)}.webp" output_path = OUTPUT_DIR / filename # Skip if already generated (safe to re-run) if output_path.exists(): logging.info(f"[{i}/{total}] Skipping (exists): {filename}") success += 1 continue logging.info(f"[{i}/{total}] Generating: {title}") image_bytes = generate_thumbnail(title, fmt, style) if image_bytes: output_path.write_bytes(image_bytes) logging.info(f"[{i}/{total}] Saved: {filename}") success += 1 else: logging.warning(f"[{i}/{total}] Failed: {title}") failed += 1 if i < total: time.sleep(DELAY_BETWEEN_REQUESTS) logging.info(f"Batch complete — {success} succeeded, {failed} failed") if __name__ == "__main__": run_batch() ``` ## Step 3 — Run It ```bash python batch_thumbnails.py ``` Your `thumbnails/` folder now contains production-ready WebP files at 1280x720. ## Handling Large Batches For 100 videos with a 2-second delay between requests, the full batch takes roughly 45–50 minutes. Two options: ### Option A — Run overnight Schedule the script via cron to run while you sleep: ```bash # Run every night at 2am (cron) 0 2 * * * /usr/bin/python3 /path/to/batch_thumbnails.py ``` ### Option B — Resume interrupted runs The script already handles this. The `if output_path.exists(): continue` check means you can stop and restart at any point without re-generating thumbnails you already have. ## Using a Brand Style Dataset If your channel has a consistent visual identity, upload reference images to ThumbAPI once and use the returned asset ID in every batch run: ```python CUSTOM_ASSETS_ID = "m6XhjtZNdF0N2AFXUOiq" # your dataset ID ``` Every thumbnail in the batch will use your channel's color palette, typography style, and visual tone automatically. ## Mixed Format Batches Your CSV can mix formats for different platforms in a single run: ```csv title,format,style "How the Roman Empire Collapsed",youtube,faceless "The Roman Empire — A Deep Dive",blogpost,faceless "5 Facts About Rome You Didn't Know",instagram,faceless ``` The script handles each row independently, so you can generate YouTube thumbnails, blog covers, and Instagram images in one pass. ## Time Saved at Scale
Videos Manual design time Batch script time
10 ~8 hours ~4 minutes
50 ~40 hours ~20 minutes
100 ~80 hours ~45 minutes
Run it once at the end of the day, walk away, and you wake up to a folder of 1280x720 WebPs named after their titles. That's the whole pitch. [Grab a free API key](https://app.thumbapi.dev/signup) and point the script at your CSV — 50 credits per month, no credit card. ### Best Thumbnail Styles for 2026 Source: https://thumbapi.dev/blog/best-thumbnail-styles-2026 A comprehensive breakdown of the thumbnail styles driving the most clicks in 2026, organized by niche, with actionable execution advice. Thumbnail design moves fast. The heavy text overlays, cluttered compositions, and stock photo backgrounds that worked in 2022 look dated now and underperform. Platforms, audiences, and the competitive landscape have all shifted. If you're still running the same approach you used two years ago, you're almost certainly leaving clicks on the table. What follows is what's actually performing in 2026, based on top channels across multiple niches, A/B testing data, and observable trends across YouTube, Instagram, and blog platforms. ## The Shift Toward Simplicity The biggest trend in thumbnail design over the past two years is the move toward radical simplicity. The thumbnails performing best in 2026 have fewer elements, more whitespace, larger text, and clearer focal points than their predecessors. Two things are driving this. First, most YouTube watch time happens on mobile, where the thumbnail renders at roughly 320x180 pixels. Complex designs that look great at full size on a desktop turn into illegible mush at that resolution. If you can't read your own thumbnail on a phone held at arm's length, it won't work for anyone else. Second, viewers decide fast. A normal browse session scrolls past dozens of thumbnails in seconds, and most impressions get a glance measured in fractions of a second before the viewer clicks or moves on. That isn't enough time for a complex message. It's enough time for one idea, one emotion, or one reason to click. ### What Simplicity Looks Like in Practice - Maximum of 3-4 words of text (if any text at all) - One dominant visual element, not three competing for attention - Large, bold typography that is readable at 320px width - Clean backgrounds with strong figure-ground separation - A single clear emotion or value proposition ## Style 1: The Clean Face Close-Up The most consistently high-performing thumbnail style across YouTube in 2026 is a tight close-up of a face against a clean, often gradient background, with minimal or no text. The face occupies 40-60% of the frame, the expression is clear and emotionally charged, and the background is either a solid color, a subtle gradient, or a heavily blurred scene. This style works because it strips away everything except the two most powerful thumbnail elements: a face and an expression. Nothing competes for attention, nothing has to be decoded, nothing gets lost at small sizes. You see this style most often on personal-brand channels — Ali Abdaal's productivity videos, Emma Chamberlain's lifestyle uploads, and a long tail of commentary and podcast clips. Their thumbnails are usually nothing more than a face and a colored background, and they reliably generate strong CTR because the expression is doing all the work. **Best for:** Personal brand channels, vlogs, commentary, educational content, podcasts. ## Style 2: Bold Typography, No Image A growing trend in 2026 is the text-only thumbnail: a bold, often provocative statement rendered in oversized typography against a high-contrast background. No photos, no illustrations, just words. This style only works when the copy itself is strong enough to drive curiosity. It strips away all visual noise and bets everything on the words. The typography has to be excellent — carefully chosen fonts, deliberate color contrast, strategic size variation to create hierarchy. Business, self-improvement, and philosophy channels have adopted this style heavily. It signals seriousness and substance, which contrasts nicely with the more sensational thumbnail styles dominating entertainment categories. The risk: text-only thumbnails feel generic if the typography isn't distinctive. Helvetica on a white background isn't a style, it's a placeholder. Channels that make this work invest in custom fonts, unique color palettes, and layouts that are instantly recognizable as theirs. Note that the risk grows as more channels adopt it. If three of the channels in a viewer's feed all use bold white text on a dark gradient, none of them stand out. ## Style 3: The Cinematic Still Some of the most visually striking thumbnails in 2026 look like frames pulled from a film. Wide-angle shots, dramatic lighting, shallow depth of field, color grading that sells a specific mood. They tell a story through composition rather than text or facial expression. This style requires the highest production quality and is mostly limited to channels that already produce cinematic content: travel, documentary, cooking, and adventure. The thumbnail is essentially a marketing still from the video itself, carefully selected and graded for maximum visual impact. It works because it promises production value. When a thumbnail looks like a Netflix poster, the viewer infers the content behind it is equally polished. For channels that deliver on that promise, the CTR lift is substantial. **Best for:** Travel, food, documentary, adventure, automotive, luxury/lifestyle. ## Style 4: The Graphic Explainer For educational and tutorial content, the graphic explainer works extremely well: a clean diagram, illustration, or visual metaphor that communicates the video's topic at a glance. A simplified flowchart showing a process, a before/after comparison, an illustrated concept map. This style works because it shows the video's value visually. A thumbnail with a clean diagram of "How DNS Works" tells the viewer exactly what they'll learn in a way that a face or text alone can't. It also signals that the content is structured and well-explained. The graphic explainer has become the dominant style for programming tutorials, science education, and how-to content. It's inherently faceless, which makes it ideal for channels without a specific creator on camera. A practical example: search "how kubernetes works" and the top results are almost all clean diagrams with a few labeled boxes and arrows. There's a reason. ## Style 5: The Contrast Split Comparison content (product A vs. product B, before vs. after, old method vs. new method) has its own visual language. The contrast split thumbnail divides the frame into two halves with a clear dividing line or vs. indicator, each side representing one option. The power here is clarity. The viewer instantly understands the premise: two things are being compared. Color coding each side (often red vs. blue, or warm vs. cool) amplifies the contrast and makes the thumbnail pop even at small sizes. Channels in tech, gaming, fitness, and finance use this style heavily, and it performs consistently because comparison content is high-intent. Viewers searching for "iPhone vs Pixel" are actively deciding, which makes them more likely to click. **Best for:** Product comparisons, before/after, versus content, tier lists, ranking content. ## Style 6: The Minimal Object A clean background with a single, beautifully photographed or rendered object dead center. No text, no face, just the object. This style is dominating product reviews and certain aesthetic niches like design, architecture, and minimalist lifestyle. It works through curiosity and aesthetics. A beautifully lit product against a dark background triggers the "what is that?" response. The lack of context forces the viewer to click to find out. Apple's marketing has trained an entire generation to associate this visual style with premium quality. When your thumbnail looks like an Apple product shot, viewers unconsciously assign a premium-quality perception to your content. **Best for:** Product reviews, tech, design, architecture, EDC (everyday carry), minimalist content. ## Trends by Niche ### Gaming Gaming thumbnails in 2026 lean toward cinematic in-game screenshots with dramatic color grading, minimal text, and character-focused compositions. The cluttered style with arrows, circles, and impact fonts is declining in the competitive gaming space, though it still works for content targeting younger audiences. ### Finance and Business Bold text thumbnails and clean data visualizations dominate. The trend is away from clickbait imagery (stacks of money, luxury cars) toward cleaner, more credible designs that signal expertise rather than hype. ### Health and Fitness Transformation before/after thumbnails remain highly effective. The trend is toward more authentic, less edited images — audiences in 2026 are suspicious of heavily retouched fitness content and reward authenticity. ### Programming and Tech Tutorials Graphic explainer style with clean code snippets, diagrams, and tech iconography. Dark backgrounds with syntax-highlighted code fragments are performing particularly well, signaling to the target audience that the content is technical and substantive. ## How to Choose Your Style The right thumbnail style depends on three variables: your content category, your audience's expectations, and your production capabilities. Don't pick a style because it's trendy. Pick it because it communicates your video's value in the clearest, most compelling way for your specific audience. A cinematic travel thumbnail would look absurd on a JavaScript tutorial. A bold text thumbnail would feel empty on a cooking channel. Whatever you pick, commit to consistency. The channels growing fastest in 2026 have instantly recognizable thumbnail styles. When a viewer sees your thumbnail in their feed, they should know it's your channel before they read the title. That recognition gets built through consistent use of the same style, color palette, typography, and composition rules across all your thumbnails. ## Scaling Thumbnail Production The hard part of maintaining a high-quality, consistent thumbnail style is production time. A polished thumbnail takes 20-60 minutes per video. For daily uploaders, multi-channel operators, or content platforms, that time adds up fast. AI thumbnail generation tools change the equation. ThumbAPI, for example, generates thumbnails in seconds through a single API call. You specify the format, the style, and the title, and the API returns a production-ready image. The `faceless` style is well-suited for graphic explainer and text-driven thumbnails. `with-image` and `with-logo` handle face-based and brand-based thumbnails respectively. The point isn't to replace creative direction. It's to remove the mechanical design work. You still decide what story the thumbnail tells. The API handles the typography, composition, color grading, and platform-specific formatting. Pick the style that fits your niche, then [start free with 50 credits per month](https://app.thumbapi.dev/signup) and lock it in across every upload. ### Faceless YouTube Channels: Does the Thumbnail Strategy Work? Source: https://thumbapi.dev/blog/faceless-youtube-channels An in-depth analysis of thumbnail strategies for faceless YouTube channels: what works, what doesn't, and how to automate faceless thumbnail creation at scale. The standard YouTube advice is to put your face in the thumbnail. It builds personal connection, signals authenticity, and statistically generates higher CTR. So why are faceless channels — channels where the creator never appears on camera — thriving? And how do they solve the thumbnail problem? Faceless channels are one of the fastest-growing segments on YouTube. Kurzgesagt (19M+ subscribers), Lemmino (5M+), and ColdFusion (5M+) have built massive audiences without ever showing a face, and thousands of smaller channels have followed their lead, building businesses around content where the creator stays anonymous. This post is about what actually works for them, why it works, and how automation is shifting the math for this category. ## What Makes a Channel Faceless A faceless channel is any YouTube channel where the creator doesn't appear on camera. This encompasses a wide range of content types: - **Animated explainers:** Channels like Kurzgesagt and 3Blue1Brown use custom animation to explain complex topics. - **Screen recording tutorials:** Programming, software, and design tutorials where the screen is the content. - **Compilation and curation:** Channels that curate footage from other sources (with proper licensing) around a theme. - **Narrated documentaries:** Long-form content with a narrator but no on-camera presence. - **Ambient and relaxation:** Nature sounds, lo-fi music, ambient scenes. - **Automated and AI-generated:** Content created partially or fully with automation tools, from AI voiceover to scripted editing pipelines. Motivations vary. Some creators value privacy. Some are building a brand bigger than any individual. Some are running multiple channels and can't be the face of all of them. And some are building automated content businesses where the lack of a face is a feature, because the business doesn't depend on a single person showing up on camera. ## The Thumbnail Challenge for Faceless Channels Faceless channels start with a real disadvantage. As discussed in our analysis of why faces get more clicks, the human face is the most powerful visual element you can put in a thumbnail. It activates hardwired neurological responses, builds familiarity, and communicates emotion instantly. Without it, faceless channels have to work harder to stop the scroll. The problem breaks down into three pieces. ### 1. Attention Without a Face In a feed where most thumbnails contain a face, a faceless thumbnail is competing against a neurological advantage. The brain processes faces faster than almost any other visual element, so faceless designs have to do the same job (stop the scroll, communicate one idea) using composition, color, and typography instead of an expression. ### 2. Brand Recognition Without a Person Face-based channels build brand recognition through the creator's appearance. Viewers learn to spot a specific person in their feed. Faceless channels need to build that same recognition through visual style alone: colors, typography, composition patterns, and design language. ### 3. Emotional Connection Without Expression Facial expressions communicate emotion instantly. Without a face, the thumbnail needs to generate an emotional response through other means: color psychology, visual tension, curiosity gaps, or the intrinsic appeal of the subject matter itself. ## Thumbnail Strategies That Work Despite these challenges, plenty of faceless channels hit CTR numbers that match or exceed face-based channels in their category. Here are the strategies they use. ### Strategy 1: The Signature Visual Language The most successful faceless channels develop a visual language so distinctive that their thumbnails are recognizable at a glance. Kurzgesagt is the gold standard: their illustrations use a specific color palette, character style, and composition approach that is unmistakable. You see a Kurzgesagt thumbnail and you know it's Kurzgesagt before reading a single word. Building that signature language requires deliberate design choices: a limited, consistent color palette (typically 3-4 primary colors); consistent typography (the same font family across all thumbnails); consistent composition rules (where elements are placed, how the frame is divided); and a consistent rendering style (flat illustration, 3D, photographic, etc.). The investment pays compound returns. Every new thumbnail reinforces the brand. Every impression trains the audience to recognize your content. Over time, your thumbnails become their own trust signal. Viewers click because they recognize the visual style and trust the quality it represents. ### Strategy 2: The Curiosity Object A single intriguing object or scene that raises a question in the viewer's mind. This leverages information gap theory: when people realize they don't know something, they feel a discomfort that's only resolved by finding out. A thumbnail showing an unusual object, an impossible scene, or a mysterious visual creates that gap. The viewer thinks "what is that?" or "how does that work?" and clicks to find out. Science channels, mystery/true crime channels, and history channels use this heavily. Specificity matters. A generic landscape doesn't create curiosity. A landscape with a single anomalous element (something out of place, something unexplained) does. ### Strategy 3: Bold Typography as the Main Event For some faceless channels, the text is the thumbnail. Large, bold, carefully designed typography that communicates the video's value without any supporting imagery. The text itself is the visual element. This works when the topic is inherently compelling: business strategy, psychology, philosophy, personal finance. When the title is "The $100B Mistake That Killed Kodak," the words are more compelling than any image could be. Execution requirements are high. The typography has to be excellent: custom or premium fonts, careful kerning, strategic use of color and size to create hierarchy and emphasis. Bad typography is worse than no text at all. ### Strategy 4: Data-Driven Visuals Charts, graphs, numbers, and comparative visuals that communicate measurable outcomes. This works for educational and analytical content where the viewer is looking for information. A thumbnail showing "$0 to $10,000/month" with a growth curve tells the viewer exactly what the video delivers. The best data-driven thumbnails are heavily simplified. Not a screenshot of a spreadsheet, but a clean, stylized visualization that communicates the trend or comparison at a glance. One number, one graph, or one comparison, not a data dump. ### Strategy 5: Scene-Setting Imagery For content about places, events, or experiences, a well-composed scene that drops the viewer into the context can be highly effective. Many travel, history, and documentary channels use this. The thumbnail is essentially a cinematic still that creates atmosphere and promises an experience. ## Automation and Faceless Thumbnails Faceless channels have a natural advantage for thumbnail automation: they don't need to photograph a person. Every element of a faceless thumbnail (background, typography, graphics, color grading) can be generated programmatically. This makes faceless channels the ideal use case for AI thumbnail generation. ThumbAPI's `faceless` image style generates complete thumbnails from a title alone. You provide the title and format, the API returns a production-ready thumbnail with appropriate imagery, typography, and composition. No photography, no design software, no manual work. For channels producing daily content or operating multiple channels in parallel, this changes the math. A thumbnail that takes most of half an hour to design manually finishes in under 30 seconds through the API. At scale — daily uploads, multiple channels, or a platform generating covers for user content — that's the difference between a sustainable operation and a bottleneck. The API approach also makes testing practical. Instead of designing one thumbnail per video, you can generate multiple variants and A/B test them. YouTube's built-in A/B testing combined with automated generation means you can optimize your strategy with data instead of intuition. ## Does the Faceless Strategy Work? Yes, with caveats. The data shows that face-based thumbnails have a statistical advantage in average CTR. But averages obscure a huge range of individual outcomes. The best faceless thumbnails outperform the average face-based thumbnail by a wide margin, and the worst face-based thumbnails underperform the average faceless thumbnail by an equally wide margin. The variable that matters most isn't whether there's a face. It's the quality of the execution: visual clarity, emotional resonance, consistency, and how clearly the thumbnail communicates value. A meticulously designed Kurzgesagt thumbnail will outperform a lazily slapped-together face thumbnail from a small channel every time. The faceless strategy works if you invest in strong visual design, stay consistent, and develop a recognizable visual brand. It works especially well when the content category doesn't require personal connection: education, tutorials, documentaries, ambient content, and topic-focused analysis. Where it struggles is in categories built on parasocial relationships, like vlogs, lifestyle content, and personal commentary. In those, the face isn't just a design element, it's the product, and content without it loses its primary appeal. If the faceless path fits your content type, production capabilities, and business model, go that way and execute at the highest level you can. The strategy works when the thumbnails are good enough. [Generate your first faceless thumbnail free](https://app.thumbapi.dev/signup) — 50 credits per month, no credit card. ### Generate YouTube Thumbnails with JavaScript and Node.js Source: https://thumbapi.dev/blog/generate-thumbnails-javascript-nodejs Complete guide to integrating ThumbAPI into JavaScript and Node.js projects. Covers single requests, batch generation, TypeScript types, and integration patterns. Most of the JS code I write against ThumbAPI lives in three places: a Node script that runs after I push a markdown file to my blog, a small CMS plugin a client uses to generate cover art on publish, and a one-off batch runner I keep on my desktop. The snippets below are the trimmed-down versions of those, with the stuff that only matters to my setup ripped out so you can paste them in and have something working in a few minutes. If you're starting from an empty `package.json` and want the ordered walkthrough — env setup, first request, Express route, batch loop — the [step-by-step Node.js guide](/blog/thumbnail-api-nodejs) is the shorter path in. Working in Python instead of Node? The [Python thumbnail API tutorial](/blog/thumbnail-api-python) is the mirror of this guide — same patterns, with `requests`, `httpx`, FastAPI, and Flask. ## Prerequisites - Node.js 18 or higher (native `fetch` is available without extra packages) - A ThumbAPI API key — [start free with 50 credits per month](https://app.thumbapi.dev/signup) No additional packages required. The examples below use Node's built-in `fetch` and `fs` modules. ## Single Thumbnail — Minimal Example The smallest useful version. One title goes in, one WebP lands on disk. ```js const API_KEY = "your_api_key_here"; async function generateThumbnail(title) { const response = await fetch("https://api.thumbapi.dev/v1/generate", { method: "POST", headers: { "x-api-key": API_KEY, "Content-Type": "application/json" }, body: JSON.stringify({ title, format: "youtube", imageStyle: "faceless", outputFormat: "webp" }) }); if (!response.ok) { throw new Error(`API error: ${response.status} ${response.statusText}`); } const data = await response.json(); const base64 = data.image.split(",")[1]; const buffer = Buffer.from(base64, "base64"); fs.writeFileSync("thumbnail.webp", buffer); console.log(`Saved — ${data.dimensions.width}x${data.dimensions.height}`); } generateThumbnail("How the Roman Empire Actually Collapsed"); ``` ## Using a Custom Asset Dataset If you've uploaded a style reference dataset to ThumbAPI, pass the asset ID to keep every thumbnail visually consistent with your channel: ```js async function generateThumbnail(title, customAssetsId = null) { const body = { title, format: "youtube", imageStyle: "faceless", outputFormat: "webp" }; if (customAssetsId) { body.customAssetsId = customAssetsId; } const response = await fetch("https://api.thumbapi.dev/v1/generate", { method: "POST", headers: { "x-api-key": API_KEY, "Content-Type": "application/json" }, body: JSON.stringify(body) }); return await response.json(); } // With dataset const result = await generateThumbnail( "How the Roman Empire Actually Collapsed", "m6XhjtZNdF0N2AFXUOiq" ); ``` ## Batch Generation from an Array Processing a list of titles sequentially with a delay to respect rate limits: ```js const API_KEY = "your_api_key_here"; const OUTPUT_DIR = "./thumbnails"; const DELAY_MS = 2000; if (!fs.existsSync(OUTPUT_DIR)) fs.mkdirSync(OUTPUT_DIR); const videos = [ { title: "How the Roman Empire Actually Collapsed", format: "youtube" }, { title: "The Deepest Cave Ever Explored", format: "youtube" }, { title: "Why the Stock Market Crashes Every Decade", format: "youtube" }, ]; function slugify(text) { return text.toLowerCase().replace(/[^a-z0-9]+/g, "-").slice(0, 60); } function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } async function generateOne(title, format, index) { const response = await fetch("https://api.thumbapi.dev/v1/generate", { method: "POST", headers: { "x-api-key": API_KEY, "Content-Type": "application/json" }, body: JSON.stringify({ title, format, imageStyle: "faceless", outputFormat: "webp" }) }); if (!response.ok) throw new Error(`${response.status}`); const data = await response.json(); const buffer = Buffer.from(data.image.split(",")[1], "base64"); const filename = `${String(index).padStart(3, "0")}-${slugify(title)}.webp`; fs.writeFileSync(path.join(OUTPUT_DIR, filename), buffer); return filename; } async function runBatch() { console.log(`Starting batch — ${videos.length} thumbnails`); for (let i = 0; i < videos.length; i++) { const { title, format } = videos[i]; try { const filename = await generateOne(title, format, i + 1); console.log(` Saved: ${filename}`); } catch (err) { console.error(` Failed: ${err.message}`); } if (i < videos.length - 1) await sleep(DELAY_MS); } } runBatch(); ``` ## Returning Base64 Directly (No File Write) When you're integrating thumbnail generation into an API response or a CMS pipeline, you might not want to write to disk at all. Return the base64 string directly and pass it downstream: ```js async function getThumbnailBase64(title) { const response = await fetch("https://api.thumbapi.dev/v1/generate", { method: "POST", headers: { "x-api-key": API_KEY, "Content-Type": "application/json" }, body: JSON.stringify({ title, format: "youtube", imageStyle: "faceless", outputFormat: "webp" }) }); const data = await response.json(); return data.image; // Full data URI, ready for or upload } // Use in an Express handler app.post("/generate-thumbnail", async (req, res) => { const { title } = req.body; const imageDataUri = await getThumbnailBase64(title); res.json({ thumbnail: imageDataUri }); }); ``` ## TypeScript Version If your project uses TypeScript, here's a typed implementation: ```ts interface ThumbnailRequest { title: string; format: "youtube" | "blogpost" | "instagram" | "x"; imageStyle: "faceless" | "with-image" | "with-logo"; outputFormat?: "webp" | "png"; customAssetsId?: string; } interface ThumbnailResponse { image: string; format: string; dimensions: { width: number; height: number; }; } async function generateThumbnail( params: ThumbnailRequest ): Promise { const response = await fetch("https://api.thumbapi.dev/v1/generate", { method: "POST", headers: { "x-api-key": process.env.THUMBAPI_KEY!, "Content-Type": "application/json" }, body: JSON.stringify(params) }); if (!response.ok) { const error = await response.text(); throw new Error(`ThumbAPI error ${response.status}: ${error}`); } return response.json() as Promise; } ``` ## Error Handling Reference
HTTP Status Meaning What to do
200 Success Decode and use the image
401 Invalid API key Check your x-api-key header
422 Invalid parameters Check format and imageStyle values
429 Rate limit hit Add a delay and retry
500 Server error Retry after a short wait
That's the full shape of it. The single-thumbnail function is what I reach for inside an Express handler or a Next.js route; the batch loop is what I run from my terminal when a client sends me a CSV. If you're hitting 429s in a tight loop, bump `DELAY_MS` to 3000 before adding any retry logic, that fixes it nine times out of ten. [Grab a free API key](https://app.thumbapi.dev/signup) and drop the snippet into your project — 50 credits per month, no credit card. ### How to Auto-Generate YouTube Thumbnails with n8n (Step-by-Step) Source: https://thumbapi.dev/blog/auto-generate-youtube-thumbnails-n8n Build an n8n workflow that detects new YouTube uploads, generates a thumbnail with ThumbAPI, and saves it to Drive — fully automated, with batch and multi-platform patterns. Thumbnail creation is the last manual step in most content pipelines. You write the video title, edit the footage, write the description — and then you open Canva or Photoshop and spend 20 minutes designing an image you could have generated in under 30 seconds. This guide shows you how to close that gap entirely. By the end, you will have an n8n workflow that detects new YouTube uploads, sends the video title to ThumbAPI, and saves a production-ready thumbnail to Google Drive automatically. No design software, no manual steps. The guide is written for developers and technically comfortable creators. If you want a higher-level overview of content automation, the [developer's guide to automating content creation](/blog/automating-content-creation) covers the broader picture. For the tool-agnostic version of this workflow — straight HTTP from Node.js, Python, or CI without n8n in front — see [how to generate YouTube thumbnails automatically with an API](/blog/generate-youtube-thumbnails-automatically). This post is the n8n-specific deep dive. ## What You Are Building The core workflow is: 1. Detect a new YouTube video (via RSS or webhook) 2. Extract the video title 3. Call ThumbAPI with the title and target format 4. Receive a base64-encoded WebP thumbnail 5. Upload the thumbnail to Google Drive (or your CMS) 6. Optionally notify your team via Slack This workflow runs entirely inside n8n. You can self-host it on your own server or use n8n Cloud — the node configuration is identical either way. ## Prerequisites Before starting, you need: - An n8n instance (self-hosted or n8n Cloud) - A ThumbAPI account with an API key — the free tier is enough to test - A YouTube channel ID (find it in YouTube Studio under Settings → Channel → Advanced settings) - A Google Drive account if you want to use the Drive destination in the examples ## Step 1: Store Your ThumbAPI Key as a Credential Never put API keys directly into HTTP Request nodes. n8n's credential manager encrypts them and keeps them out of your workflow JSON exports. Go to **Settings → Credentials → Add Credential** and choose **Header Auth**: ```text Name: ThumbAPI Header Name: x-api-key Header Value: YOUR_THUMBAPI_KEY ``` Save the credential. You will select it in every HTTP Request node that talks to ThumbAPI. > Why `x-api-key`? ThumbAPI authenticates via the `x-api-key` header, not a Bearer token. Make sure the header name matches exactly. ## Step 2: Create the Workflow and Add a Trigger Open n8n and click **New Workflow**. The trigger node determines what kicks off thumbnail generation. The two most useful options for YouTube content are: ### Option A: RSS Feed Trigger (Recommended for YouTube) YouTube publishes an RSS feed for every channel. The **RSS Feed Read** node polls it on a schedule and fires when a new video appears. ```text Node: RSS Feed Read Feed URL: https://www.youtube.com/feeds/videos.xml?channel_id=YOUR_CHANNEL_ID Poll Every: 15 minutes ``` Replace `YOUR_CHANNEL_ID` with your actual channel ID. n8n will pass the video title as `$json.title` to downstream nodes. ### Option B: Webhook Trigger (For CMS or Custom Pipelines) If you publish video metadata to a CMS before uploading to YouTube, use a **Webhook** trigger instead. Your CMS fires a POST request to the n8n webhook URL when content is ready. ```text Node: Webhook Method: POST Path: /new-video ``` The webhook payload should include at minimum a `title` field. Everything else is optional. ## Step 3: Add the ThumbAPI HTTP Request Node Click **+** after your trigger and add an **HTTP Request** node. This is the node that calls ThumbAPI to generate the thumbnail. Configure it as follows: ```text Method: POST URL: https://api.thumbapi.dev/v1/generate Authentication: Generic Credential Type → Header Auth Credential: ThumbAPI (the one you created in Step 1) Send Body: true Body Content Type: JSON ``` Set the JSON body: ```json { "title": "{{ $json.title }}", "format": "youtube", "imageStyle": "faceless", "outputFormat": "webp" } ``` The `{{ $json.title }}` expression pulls the video title from the trigger output. If your trigger uses a different field name, adjust the expression accordingly. ### Format and Style Options | Parameter | Options | | -------------- | ------------------------------------------------ | | `format` | `youtube`, `instagram`, `x`, `blogpost`, `linkedin` | | `imageStyle` | `faceless`, `with-image`, `with-logo` | | `outputFormat` | `webp` (default), `png` | For YouTube, `faceless` generates text-and-graphic designs optimized for click-through. If you have uploaded a profile photo to the ThumbAPI dashboard, switch to `with-image` to include your face in every generated thumbnail. ## Step 4: Test the HTTP Request Node Before building out the rest of the workflow, test this node in isolation. Click **Execute Node**. If the request succeeds, you will see the response in n8n's output panel: ```json { "image": "data:image/webp;base64,UklGR...", "format": "youtube", "outputFormat": "webp", "dimensions": { "width": 1280, "height": 720 } } ``` The `image` field is the full base64-encoded WebP. A typical YouTube thumbnail is 50–150 KB encoded, which n8n handles without issues. If the node fails, check: - The credential header name is exactly `x-api-key` (not `Authorization`) - The API key is correct and active in your ThumbAPI dashboard - The `title` field in your request body is not empty ## Step 5: Decode the Image and Upload to Google Drive The base64 string needs to be converted into n8n binary data before you can upload it. Add a **Code** node between the HTTP Request node and your destination: ```js const base64String = $input.item.json.image; const base64Data = base64String.includes(',') ? base64String.split(',')[1] : base64String; const outputFormat = $input.item.json.outputFormat || 'webp'; return { json: { title: $input.item.json.title, format: $input.item.json.format, outputFormat, }, binary: { data: { data: base64Data, fileName: `thumbnail-${Date.now()}.${outputFormat}`, mimeType: `image/${outputFormat}`, }, }, }; ``` After this node, add a **Google Drive** node: ```text Operation: Upload File File Name: {{ $json.title }}.webp Binary Property: data Parent Folder: YouTube Thumbnails ``` The thumbnail is now in Google Drive with the video title as the filename. ## Step 6: Add a Slack Notification (Optional) If you work with a team, a Slack notification makes the workflow visible. Add a **Slack** node after the Drive upload: ```text Resource: Message Operation: Send Channel: #thumbnails Text: Thumbnail generated for: {{ $json.title }} Drive: {{ $json.webViewLink }} ``` The workflow now fully handles detection, generation, upload, and notification without any manual intervention. ## Complete Workflow JSON Here is the complete workflow you can import directly into n8n. Replace `YOUR_CHANNEL_ID` and `YOUR_GOOGLE_DRIVE_FOLDER_ID` before importing, and attach your `ThumbAPI` Header Auth credential to the HTTP Request node after import. ```json { "name": "YouTube Thumbnail Automation — ThumbAPI", "nodes": [ { "parameters": { "url": "https://www.youtube.com/feeds/videos.xml?channel_id=YOUR_CHANNEL_ID", "pollTimes": { "item": [{ "mode": "everyMinute", "value": 15 }] } }, "name": "RSS Feed — YouTube Channel", "type": "n8n-nodes-base.rssFeedRead", "typeVersion": 1, "position": [450, 300] }, { "parameters": { "method": "POST", "url": "https://api.thumbapi.dev/v1/generate", "authentication": "genericCredentialType", "genericAuthType": "httpHeaderAuth", "sendBody": true, "specifyBody": "json", "jsonBody": "={\n \"title\": \"{{ $json.title }}\",\n \"format\": \"youtube\",\n \"imageStyle\": \"faceless\",\n \"outputFormat\": \"webp\"\n}" }, "name": "ThumbAPI — Generate Thumbnail", "type": "n8n-nodes-base.httpRequest", "typeVersion": 3, "position": [670, 300], "credentials": { "httpHeaderAuth": { "name": "ThumbAPI" } } }, { "parameters": { "jsCode": "const base64String = $input.item.json.image;\nconst base64Data = base64String.includes(',') ? base64String.split(',')[1] : base64String;\nconst outputFormat = $input.item.json.outputFormat || 'webp';\n\nreturn {\n json: {\n title: $input.item.json.title,\n format: $input.item.json.format,\n outputFormat\n },\n binary: {\n data: {\n data: base64Data,\n fileName: `thumbnail-${Date.now()}.${outputFormat}`,\n mimeType: `image/${outputFormat}`\n }\n }\n};" }, "name": "Decode Base64 Image", "type": "n8n-nodes-base.code", "typeVersion": 1, "position": [890, 300] }, { "parameters": { "operation": "upload", "name": "{{ $json.title }}.webp", "parents": { "folderId": "YOUR_GOOGLE_DRIVE_FOLDER_ID" }, "binaryPropertyName": "data" }, "name": "Google Drive — Upload", "type": "n8n-nodes-base.googleDrive", "typeVersion": 3, "position": [1110, 300] } ], "connections": { "RSS Feed — YouTube Channel": { "main": [[{ "node": "ThumbAPI — Generate Thumbnail", "type": "main", "index": 0 }]] }, "ThumbAPI — Generate Thumbnail": { "main": [[{ "node": "Decode Base64 Image", "type": "main", "index": 0 }]] }, "Decode Base64 Image": { "main": [[{ "node": "Google Drive — Upload", "type": "main", "index": 0 }]] } } } ``` ## Handling Multiple Platforms at Once If you distribute the same content across YouTube, Instagram, and your blog, you can generate all three thumbnails in parallel from a single trigger. Add a Code node that fans out the request: ```js const title = $input.first().json.title; const formats = ['youtube', 'instagram', 'blogpost']; return formats.map((format) => ({ json: { title, format }, })); ``` Connect this to a **Loop Over Items** node, then the HTTP Request node. Each iteration calls ThumbAPI with a different format, producing three platform-optimized thumbnails from one video title. Platform dimensions ThumbAPI generates: | Format | Dimensions | | ----------- | ------------ | | `youtube` | 1280 × 720 | | `instagram` | 1080 × 1080 | | `blogpost` | 1200 × 630 | | `x` | 1200 × 675 | | `linkedin` | 1200 × 627 | ## Cleaning Titles Before Generation YouTube titles sometimes contain HTML entities, emoji, or other characters that render poorly on thumbnails. Add a Code node before the HTTP Request to sanitize the input: ```js const title = $input.first().json.title; const clean = title // Decode common HTML entities .replace(/&/g, '&') .replace(/</g, '<') .replace(/>/g, '>') .replace(/'/g, "'") .replace(/"/g, '"') // Strip emoji (optional — depends on your thumbnail style) .replace(/[\u{1F600}-\u{1F64F}]/gu, '') // Trim to 80 characters for clean thumbnail text .trim() .substring(0, 80); return [{ json: { ...$input.first().json, title: clean } }]; ``` Titles over 80 characters tend to overflow on the generated thumbnail. ThumbAPI handles truncation internally, but providing a clean title gives the layout engine more room to lay out the design well. ## Error Handling for Production Workflows Automated workflows break silently if you do not add error handling. These are the minimum safeguards for a production n8n thumbnail pipeline: **Enable "Continue On Fail" on the HTTP Request node.** If ThumbAPI returns an error for one item in a batch, the workflow continues processing the rest rather than halting entirely. **Add retry logic.** In the HTTP Request node settings, enable **Retry On Fail** with 2 retries and a 3-second interval. This handles transient network issues without any manual intervention. **Add a Wait node between batch items.** If you are processing several videos at once, add a Wait node (1–2 seconds) between iterations to avoid hitting rate limits on the ThumbAPI side. **Route errors to a Slack notification.** Connect the error output of the HTTP Request node to a Slack node that posts the failed item's title to a `#thumbnail-errors` channel. You want to know about failures before they affect your publishing schedule. ## Why n8n Over Other Automation Tools n8n's main advantages for thumbnail automation come down to control and cost: **Self-hosted option.** Your API keys and content titles stay on your own infrastructure. For teams with data governance requirements, this matters. **No per-execution fees.** Self-hosted n8n has no task-based pricing. The only cost is your ThumbAPI plan and whatever server hosts n8n. **Code nodes.** The ability to run arbitrary JavaScript between nodes makes title cleaning, conditional logic, and data transformation straightforward — things that require workarounds in purely visual automation tools. **Full request visibility.** You can inspect the exact request body sent to ThumbAPI and the exact response received. When something goes wrong, debugging takes minutes instead of hours. If you prefer a no-code setup, the same thumbnail automation pattern works in Make.com and Zapier, with slightly less flexibility in the transformation steps. ## Getting Started The fastest path from zero to working thumbnail automation: 1. [Start free with ThumbAPI](https://app.thumbapi.dev/signup) — 50 credits per month, no credit card 2. Add your API key as a Header Auth credential in n8n 3. Build the workflow from Step 2 in this guide (or import the JSON above) 4. Run it against one video title and check the Drive output 5. If the quality meets your bar, activate the workflow and let it run on every new upload The entire setup takes under 30 minutes if you already have an n8n instance. The thumbnail generation itself runs in under 25 seconds per request. For most content operations, the time savings pay for the ThumbAPI plan within the first week. For the canonical n8n setup — including a copy/paste test workflow that uses a public test key (no account required) — see the [ThumbAPI + n8n integration guide](/integrations/n8n). ### How to Automate YouTube Thumbnail Uploads — Full Guide Source: https://thumbapi.dev/blog/n8n-youtube-auto-thumbnails A real walkthrough — Google Cloud OAuth2 setup, the credential wiring nobody documents, and a paste-ready workflow that pushes a ThumbAPI-generated image directly to YouTube. 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](/blog/auto-generate-youtube-thumbnails-n8n) covers that variant. ## Watch the Full Walkthrough Prefer to follow along on video? The whole setup — Google Cloud OAuth2, n8n credential wiring, the community node, and pushing the generated thumbnail back to YouTube — is covered end-to-end in this video.