# YouTube thumbnail API and MCP

Source: https://www.thumbnailmaker.so/developers

ThumbnailMaker as tools for Claude Code, Cursor, VS Code and your own scripts. To use it from ChatGPT or Claude in the browser, follow [the three steps for chat apps](https://www.thumbnailmaker.so/mcp).

## How do I connect the MCP server?

Streamable HTTP, stateless. One address for everything once you are signed in, one for the three free tools with no account at all.

- Signed in, every tool: `https://www.thumbnailmaker.so/api/mcp`. OAuth (the client opens a ThumbnailMaker login, you click Allow) or a Studio key as a bearer token.
- No account, free tools: `https://www.thumbnailmaker.so/api/mcp/free`. The brief, the check and the specs, limited per address.

Claude Code:

```sh
claude mcp add --transport http thumbnailmaker https://www.thumbnailmaker.so/api/mcp
# then /mcp in Claude Code to log in, or send a key:
#   --header "Authorization: Bearer tm_your_key"
```

Cursor, VS Code, Claude Desktop:

```json
{
  "mcpServers": {
    "thumbnailmaker": {
      "url": "https://www.thumbnailmaker.so/api/mcp",
      "headers": { "Authorization": "Bearer tm_your_key" }
    }
  }
}
```

A request with no credentials gets a 401 whose `WWW-Authenticate` header points at `/.well-known/oauth-protected-resource`; clients that speak MCP authorization start the login from there.

## Which tools does it have?

Free:

- `write_thumbnail_brief`: A YouTube link in, the brief out: one scene with a single focal point, two to four words for the image, the style that fits, and a link that opens the generator on it.
- `check_thumbnail`: Measures a video's current thumbnail without AI: whether a 1280-wide custom version exists, whether the duration-badge corner is clear, whether the contrast holds at feed size.
- `thumbnail_specs`: YouTube's own sizes, shapes and file limits for videos, Shorts and podcasts, and four design rules that hold at feed size.

Signed in:

- `generate_thumbnail` (Studio): From a description or a video link. Style, words on the image, 16:9 or Shorts, 2K or 4K, one or two takes, your brand kit.
- `correct_thumbnail` (Studio): One change by instruction on an existing thumbnail; the rest stays put and the original is kept.
- `make_three_to_test` (Studio): Three concepts on three angles for YouTube's own A/B test.
- `list_thumbnails` (Any plan): Your latest thumbnails with their links.
- `get_thumbnail` (Any plan): One thumbnail by id, as an image.
- `list_brand_kits` (Any plan): Your brand kits, for brandKitId.
- `get_account` (Any plan): Your plan and the thumbnails left this month.

The three tools that spend credits never do on the first call: they answer with the price, the balance and a `quoteId`. Call again with the same arguments, `confirm: true` and that `quoteId` (valid 10 minutes) once the person has agreed; a confirmed call without a matching quote answers `quote_required`, and a quote pays for one call only: sent again, it answers `quote_used` and charges nothing. Errors come back as tool results with `isError` and a code from the table below. A tool that returns thumbnails sends the JSON, then for each a preview as image content (1024 px) and a `resource_link` to the full file.

## How do I call the REST API?

Every endpoint takes JSON and answers JSON, with a Studio key as a bearer token, the brief and the check included (they are free over MCP, not over REST). Images come back as links to the stored files, the same ones your Library shows. The whole API is described in [an OpenAPI 3.1 spec](https://www.thumbnailmaker.so/api/v1/openapi.json).

```sh
curl https://www.thumbnailmaker.so/api/v1/thumbnails \
  -H "Authorization: Bearer tm_your_key" \
  -H "Content-Type: application/json" \
  -d '{"videoUrl": "https://www.youtube.com/watch?v=...", "format": "16:9"}'
```

| Endpoint | What it does |
|---|---|
| `POST /api/v1/thumbnails` | Generate. Body: prompt or videoUrl, then text, style, format, resolution, takes, brandKitId, matchVideoLook. |
| `GET /api/v1/thumbnails` | Your latest thumbnails. ?limit= up to 50. |
| `GET /api/v1/thumbnails/{id}` | One thumbnail. |
| `POST /api/v1/thumbnails/{id}/corrections` | A correction. Body: instruction. |
| `POST /api/v1/packs` | Three to test. Body: prompt or videoUrl, then text, style, format, brandKitId. |
| `POST /api/v1/briefs` | The brief for a video. Body: videoUrl. |
| `POST /api/v1/checks` | The check of a video's thumbnail. Body: videoUrl. |
| `GET /api/v1/brand-kits` | Your brand kits (id, name), for brandKitId. |
| `GET /api/v1/account` | Plan and thumbnails left. |

A thumbnail, wherever one comes back:

```jsonc
{
  "id": "uuid",
  "url": "https://.../thumbnails/....jpg",
  "format": "16:9",            // or "9:16"
  "prompt": "the brief it was made from",
  "createdAt": "2026-10-24T10:00:00Z",
  "angle": "reaction",         // set on three-to-test concepts
  "qualityScore": 4.5          // feed-size grade, 1 to 5; null when not graded
}
```

Generate and three to test answer `{ thumbnails, thumbnailsUsed, thumbnailsLeft }` (generate adds `brief` when it wrote one from the video), a correction `{ thumbnail, thumbnailsUsed, thumbnailsLeft }`. Balances are counted in thumbnails; one thumbnail is 10 credits.

## What do the errors mean?

A failure answers `{ "error", "message", "docs" }` with the status that explains it. The message is written for a person and can be shown as is; `docs` links to this table.

| Status | error | When |
|---|---|---|
| 400 | `invalid_json` | The body is not a JSON object. |
| 400 | `invalid_request` | A field is missing, too long or not one of its values (unknown style, "4K" instead of "4k"). The message names the field. |
| 400 | `invalid_url`, `missing_prompt` | The link is not a YouTube video, or neither prompt nor videoUrl was sent. |
| 400 | `identity_not_allowed`, `person_image` | A real person or celebrity was asked for, or the image to correct is a photo of a person. |
| 401 | `invalid_api_key` | The key is missing, malformed or revoked. |
| 402 | `insufficient_credits` | The month's thumbnails are used up. The message says how many are left. |
| 403 | `plan_upgrade_required` | The account is not on Studio. |
| 404 | `not_found`, `video_not_found`, `thumbnail_not_found` | Unknown id, a private or deleted video, or a video with no thumbnail. |
| 429 | `rate_limited` | An hourly or per-minute limit was reached. Retry later. |
| 500 | `server_error` | Something failed on our side. Credits for anything not delivered are refunded. |
| 502 | `model_failed`, `brief_failed`, `pack_failed` | The model returned nothing usable. Nothing is charged. |
| 503 | `model_unavailable` | The image model is down. Retry with backoff. |
| MCP | `quote_required`, `quote_used`, `sign_in_required` | A spending tool was confirmed without its quoteId, or with one an earlier call already used (nothing is charged again), or a signed-in tool was called anonymously. |

## What does it cost, and what are the limits?

- The same credits as the app: Studio has 3000 a month for $99. One image is 10 credits, 4K or a second take adds 10, three to test is 30. A concept or a take that fails is refunded, and so is an image that could not be stored.
- 60 calls an hour per key: every REST call counts, and over MCP the paid ones (also 60 per OAuth connection). Five generations a minute per account. Up to 5 active keys, made and revoked in [Settings](https://www.thumbnailmaker.so/dashboard/settings#api). A key is shown once.
- The free tools, per address with no account or per account when signed in: the brief 10 an hour and 30 a day, the check 30 an hour and 200 a day.
- A call has 60 seconds. When the main image model runs late, a faster one finishes the image.
- The same rules as the app: real people and celebrities are refused, and every image is checked.

[See the Studio plan](https://www.thumbnailmaker.so/pricing) or write to [contact@thumbnailmaker.so](mailto:contact@thumbnailmaker.so) about higher volumes.

Claude is a trademark of Anthropic and ChatGPT a trademark of OpenAI; ThumbnailMaker is not affiliated with either.
