For Claude · OpenCode · Codex · Cursor · Gemini · Copilot · anything

Give your AI agent the ytapis manual

One copy-paste prompt teaches any AI coding agent every endpoint, field, limit and known gotcha. Load it once, then describe the app you want. The agent writes code that calls the real API and handles empty results.

Illustration of an AI assistant receiving a copied prompt in a terminal
How it works

Three steps, about two minutes

Copy the master prompt

Use the Copy button in the box below, or download it as a file named AGENTS.md. The file contains the full API reference, data shapes, rules for the agent and a self-test.

Load it into your agent

Save it as the instruction file your tool reads (table below), or paste it into the tool’s rules or custom-instructions box. In a plain chat, paste it as your first message.

Describe your app, then let it test

Use the fill-in template in the “Describe the app” section below. Ask the agent to run its self-test with real calls and show you the output before it says the job is done.

Master prompt · full version

Copy this into your agent

Best for coding agents with a project folder (Claude Code, OpenCode, Codex, Cursor, Gemini CLI, Copilot, Windsurf, Aider, and more). It is long on purpose, so the agent knows the gotchas before it writes code.

Ready to copy Plain Markdown. Works in any editor or chat.
ytapis-agent-prompt.md · copy me
# ytapis — Master Build Prompt for AI Coding Agents

You are my coding assistant. I want you to build an application that uses **ytapis**, a free, unofficial YouTube data API. It needs no API key, no OAuth, and no sign-up. Read this whole document before you write any code.

---

## 1. What ytapis is

- **Purpose:** Search YouTube and read public video, channel, playlist, comment, transcript, live-stream, and Shorts data as clean JSON.
- **Base URL (HTTP API, hosted on Cloudflare Workers):** `https://ytapis.djalokyt27.workers.dev`
- **No authentication.** Do not add an API key, token, or OAuth header. None is needed.
- **CORS is open** (`Access-Control-Allow-Origin: *`), so browser JavaScript can call the API directly.
- **Unofficial.** ytapis reads public YouTube pages. It is not affiliated with or endorsed by YouTube or Google. It can break whenever YouTube changes its pages, so the code you write must handle failure gracefully.
- **Responses are JSON.** Errors look like `{ "error": "message" }` with a 4xx or 5xx status.

---

## 2. Ground rules for you (the agent)

1. **Use only the endpoints and fields listed in this document.** Do not invent endpoints, query parameters, or fields. If you need something that is not listed, say so and ask me.
2. **Prefer plain HTTP** (`fetch` in JavaScript/TypeScript, `requests` or `httpx` in Python, `curl` for testing) unless I explicitly ask for an SDK.
3. **Treat every field as optional.** Strings can be empty, numbers can be 0, and some fields can be `null`. Arrays can be empty `[]`. Never assume a result has data.
4. **Be a polite client.**
   - Cache responses (5 to 10 minutes is a good default).
   - Add a 200 to 500 ms delay between bulk requests.
   - Do not fire many requests in parallel.
   - Retry transient failures (HTTP 429, 5xx, network errors) with exponential backoff: about 3 to 5 attempts, starting near 500 ms and capping near 8 seconds.
5. **Respect YouTube and creators.** Do not download, re-host, or re-encode video files. Show attribution when you display results: the channel name, plus a link to the video on YouTube. Do not build a tool that scrapes YouTube at scale or bypasses protections.
6. **Show empty states honestly.** If an endpoint returns `[]`, display a clear message such as "No results right now." Never fabricate data to fill the gap.
7. **Validate input before you call the API.**
   - Video IDs are exactly 11 characters from `A-Z a-z 0-9 _ -`.
   - Limits are integers from 1 to 50 (see section 4 for the exact limits per endpoint).
   - Search queries must not be empty.
8. **Work in small verified steps.** Run the code, call the real API, and show me the actual output before you say the work is finished.

---

## 3. Known behaviour (read carefully)

- **Search and Shorts return a plain JSON array**, not an object such as `{ "results": [...] }`. The array is the top-level JSON value.
- **`limit` is a maximum, not a guarantee.** YouTube often returns fewer items than requested. For example, `limit=100` returned only 13 results when last tested. Never assume you receive exactly `limit` items. A non-numeric `limit` such as `limit=abc` is not rejected, so validate it yourself.
- **Some endpoints return empty results right now.** When last tested, these returned `[]`: `/trending`, `/video/{id}/comments`, `/video/{id}/related`, `/video/{id}/transcript`, `/channel/{id}`, and `/playlist/{id}`. `/channel/{id}/metadata` returned mostly empty fields. Treat these endpoints as optional. Build the UI so it still works when they return nothing, and say so in the UI.
- **Some requests succeed with placeholder data.** `/video/{id}` returns a fallback object (for example, title "Video abc") for an invalid or unknown ID. It does not return a 404. Validate IDs yourself before you call it.
- **Missing required parameters.**
  - `/shorts` without `q` returns `400 {"error":"Missing query param \"q\""}`.
  - `/` without `q` returns an HTML landing page, not JSON. Always send `q`.
  - Unknown routes return `404 {"error":"Not found"}`.
- **Parameters the Worker reads:** `q` and `limit` (search and list routes), `lang` (transcript), and `token` (continue). It ignores `sort` on comments and does not read `gl` or `hl`.
- **Pagination over HTTP is limited.** The HTTP API does not hand you a continuation token. The `/continue` endpoint needs a token that comes from the SDK. Do not build pagination on the HTTP API unless you have a token. Use a larger `limit` instead.
- **Type notes:**
  - `durationSeconds`, `viewCountRaw`, and `viewCount` (in stats) are numbers.
  - `isLive`, `isUpcoming`, and `isVerified` are booleans. `/video/:id` always returns `isLive` and `isUpcoming` as false, so use `/video/:id/live` for live state.
  - `viewCount` and `duration` in `VideoResult` are display strings such as `"1.8B views"` and `"3:33"`. Use the numeric fields for sorting and math.

---

## 4. HTTP endpoints

All endpoints use `GET`. Replace `https://ytapis.djalokyt27.workers.dev` with the base URL shown in section 1.

| # | Path | Parameters | Returns |
|---|------|-----------|---------|
| 1 | `/?q=QUERY&limit=N` | `q` required. `limit` 1 to 50, default 15. | `VideoResult[]` |
| 2 | `/trending?limit=N` | `limit` 1 to 50, default 15. May be empty. | `VideoResult[]` |
| 3 | `/shorts?q=QUERY&limit=N` | `q` required. `limit` 1 to 50, default 15. | `VideoResult[]` |
| 4 | `/continue?token=TOKEN&limit=N` | Token must come from the SDK. | `VideoResult[]` |
| 5 | `/channel/CHANNEL_ID?limit=N` | Channel ID, for example `UC...`. May be empty. | `VideoResult[]` |
| 6 | `/channel/CHANNEL_ID/metadata` | Channel ID. | `ChannelMetadata` object (fields may be empty) |
| 7 | `/playlist/PLAYLIST_ID?limit=N` | Playlist ID. May be empty. | `VideoResult[]` |
| 8 | `/video/VIDEO_ID` | 11-character video ID. | `VideoResult` object |
| 9 | `/video/VIDEO_ID/comments?limit=N` | `limit` 1 to 100, default 20. Any `sort` value is ignored. May be empty. | Array of comment objects (`id`, `text`, `likeCount`, `publishedTime`, `replyCount`, `isPinned`, `isLikedByCreator`, `replies`, `author`) |
| 10 | `/video/VIDEO_ID/related?limit=N` | `limit` 1 to 50, default 15. May be empty. | Array of related-video objects |
| 11 | `/video/VIDEO_ID/stats` | 11-character video ID. | `VideoStats` object |
| 12 | `/video/VIDEO_ID/live` | 11-character video ID. | `LiveStreamInfo` object |
| 13 | `/video/VIDEO_ID/transcript?lang=en` | `lang` is an optional language code. May be empty. | `TranscriptEntry[]` |
| 14 | `/health` | none | `{ "status": "ok", "version": "2.0.0" }` |

### Example requests

```
GET https://ytapis.djalokyt27.workers.dev/?q=lofi%20hip%20hop&limit=10
GET https://ytapis.djalokyt27.workers.dev/video/dQw4w9WgXcQ
GET https://ytapis.djalokyt27.workers.dev/video/dQw4w9WgXcQ/stats
GET https://ytapis.djalokyt27.workers.dev/video/dQw4w9WgXcQ/live
GET https://ytapis.djalokyt27.workers.dev/health
```

Always URL-encode query strings. In JavaScript, use `encodeURIComponent(query)`. In Python, pass a `params` dictionary to `requests`.

---

## 5. Data shapes

### VideoResult (18 fields). Returned by search, trending, shorts, channel, playlist, and video.

| Field | Type | Example or notes |
|-------|------|------------------|
| `id` | string | `"dQw4w9WgXcQ"` |
| `title` | string | Video title |
| `author` | string | Channel name |
| `channelUrl` | string | Sometimes relative, such as `/@handle`. Prefix `https://www.youtube.com` when it starts with `/`. |
| `thumbnail` | string | Image URL, may be empty |
| `thumbnails` | array | `[{ "url", "width", "height" }]` |
| `fullUrl` | string | `https://www.youtube.com/watch?v=ID` |
| `embedUrl` | string | `https://www.youtube.com/embed/ID?rel=0` |
| `duration` | string | `"3:33"` or `"1:00:17"`. May be empty for live streams. |
| `durationSeconds` | number | `213` |
| `viewCount` | string | `"1.8B views"` (display text) |
| `viewCountRaw` | number | `1824726106` |
| `publishedTime` | string | `"3 years ago"` (display text, may be empty) |
| `description` | string | First part of the description |
| `channelAvatar` | string | Channel picture URL, may be empty |
| `isLive` | boolean | |
| `isUpcoming` | boolean | Unreliable. Returned as true for any non-live video with zero viewers on `/live`. Check `isLive` and `scheduledStartTime` together. |
| `isVerified` | boolean | |

### VideoStats. Returned by `/video/{id}/stats`.

`views` (number), `likes` (number or null), `comments` (always 0, because the Worker hard-codes it), `isLive` (boolean), `viewerCount` (number or null).

### LiveStreamInfo. Returned by `/video/{id}/live`.

`isLive`, `isUpcoming` (booleans), `viewerCount` (number), `viewerCountStr` (string), `startTime` (string), `scheduledStartTime` (string), `likesCount` (number), `dislikesCount` (number).

### ChannelMetadata. Returned by `/channel/{id}/metadata`.

`id`, `name`, `handle`, `url`, `avatar`, `banner`, `description`, `subscriberCount` (display string), `subscriberCountRaw` (number), `videoCount` (display string), `videoCountRaw` (number), `isVerified` (boolean), `socialLinks` (array).

### TranscriptEntry. Returned by `/video/{id}/transcript`.

`text` (string), `start` (seconds, number), `duration` (seconds, number).

### Comments

Each comment is an object with fields such as `text`, `author` (with a `name`), and `likeCount`. Inspect a real response before you write code that depends on the exact field names.

---

## 6. Optional SDKs (only if I ask for one)

- **TypeScript / JavaScript:** `npm install ytapis-core`. Import from `'ytapis-core'`. Do **not** install a package named `ytapis` on npm, because it does not exist.
  - `search(query, { limit })` returns an object with `results` and `continuation`.
  - Other verified exports: `getVideo`, `getVideoStats`, `getLiveStreamInfo`, `getTranscript`, `getComments`, `getRelatedVideos`, `getChannelMetadata`, `searchTrending`, `searchShorts`, `searchChannel`, `searchPlaylist`, `searchContinue`, `createClient`, `withRetry`, `LRUCache`.
- **Python:** `pip install ytapis`. Import from `ytapis`.
  - `search(query, limit=15)` returns a list of `VideoResult` objects with attributes such as `title`, `view_count`, `duration`, and `is_verified`.
  - Also exported: `get_video(video_id)`, `search_continue(...)`, and `search_dicts(query, limit)`.

---

## 7. Starter recipes

**Search and show results in plain HTML and JavaScript**

```js
const API = 'https://ytapis.djalokyt27.workers.dev';

async function searchVideos(query, limit = 10) {
  const url = `${API}/?q=${encodeURIComponent(query)}&limit=${limit}`;
  const res = await fetch(url, { headers: { Accept: 'application/json' } });
  if (!res.ok) throw new Error(`API returned ${res.status}`);
  const videos = await res.json();      // plain array
  return Array.isArray(videos) ? videos : [];
}
```

**Check which live streams are on air (poll slowly)**

```js
async function isLive(videoId) {
  const res = await fetch(`${API}/video/${videoId}/live`);
  if (!res.ok) return false;
  const info = await res.json();
  return Boolean(info.isLive);
}
```

**Retry with exponential backoff**

```js
async function withBackoff(fn, retries = 4, base = 500, cap = 8000) {
  for (let attempt = 0; ; attempt++) {
    try { return await fn(); }
    catch (err) {
      if (attempt >= retries) throw err;
      const delay = Math.min(cap, base * 2 ** attempt) * (0.5 + Math.random() / 2);
      await new Promise(r => setTimeout(r, delay));
    }
  }
}
```

---

## 8. What I want you to do now

1. **Ask me at most three short questions** if anything is unclear. Cover: the language or framework I want, where the app runs (browser, server, or command line), and what the output should look like. If I do not answer, choose sensible defaults (for example, a single HTML file with plain JavaScript, or Node.js 20 with Express) and state the choice.
2. **Plan** in five bullet points or fewer.
3. **Build** the app in small steps. Keep the code readable, with comments only where they help.
4. **Test with real calls.** Run `/health`, then search for "cats" with `limit=3`, then fetch video `dQw4w9WgXcQ` and its stats. Show me the output.
5. **Handle failure states** in the UI: loading, empty results, API errors, and offline mode.
6. **Finish with** a summary that lists the files you created, the exact command or steps to run the app, and any known limitations, including endpoints that returned empty results during your tests.

Tip: after you download AGENTS.md, place it in your project root. Claude Code, OpenCode, Codex and Cursor all read it there.

Short version

For chat apps with small instruction limits

Use this in ChatGPT custom instructions, Claude Projects, a Gemini Gem or any chat where you cannot attach files. It keeps the endpoints, the shapes and the top rules.

About 600 words
ytapis-short-prompt.md
You are helping me build with ytapis, a free, unofficial YouTube data API. No API key, no OAuth.

Base URL: https://ytapis.djalokyt27.workers.dev
GET only. JSON responses. CORS is open.

Endpoints:
- GET /?q=QUERY&limit=N : array of videos. q is required. limit 1-50, may return fewer.
- GET /shorts?q=QUERY&limit=N : array of Shorts. q is required.
- GET /trending?limit=N : array. Often empty right now.
- GET /video/ID : one video object. Validate IDs (11 characters). Invalid IDs return placeholder data, not 404.
- GET /video/ID/stats : {views, likes, comments (always 0), isLive, viewerCount}
- GET /video/ID/live : {isLive, isUpcoming, viewerCount, viewerCountStr, startTime, scheduledStartTime, likesCount, dislikesCount}. isUpcoming is unreliable; check isLive and scheduledStartTime.
- GET /video/ID/transcript?lang=en : array of {text, start, duration}. Often empty.
- GET /video/ID/comments?limit=N : array. Often empty. sort is ignored.
- GET /video/ID/related?limit=N : array. Often empty.
- GET /channel/CHANNEL_ID/metadata : channel object. Fields may be empty.
- GET /channel/CHANNEL_ID?limit=N and GET /playlist/PLAYLIST_ID?limit=N : arrays. Often empty.
- GET /health : {status, version}

Video object fields: id, title, author, channelUrl, thumbnail, fullUrl, embedUrl, duration (text), durationSeconds (number), viewCount (text), viewCountRaw (number), publishedTime, description, channelAvatar, isLive, isUpcoming, isVerified.

Rules:
- Treat every field as optional. Handle empty arrays and errors in the UI.
- Never invent endpoints or fields. Use only the list above.
- Cache responses, and add a delay between bulk requests.
- Show attribution (channel name and a YouTube link). Do not re-host videos.
- For npm use the package "ytapis-core". There is no npm package called "ytapis". For Python use "pip install ytapis".

Before you write code, ask me at most three questions about the stack and the output. Then build it, test it with real calls, and show me the output.
Setup guide

Where the prompt goes, for every popular tool

Each tool reads a different file name. The table shows where to put the prompt and how to start. File names change over time, so check your tool’s docs if something does not load.

C

Claude Code

Anthropic · terminal agent
CLAUDE.md (or AGENTS.md)
  1. In your project folder, save the master prompt as CLAUDE.md. If you already keep one shared AGENTS.md, add the line @AGENTS.md to CLAUDE.md instead.
  2. Run claude in that folder. The file loads automatically.
  3. Send your app description (see the template below).

Keep CLAUDE.md focused. If it grows, move the reference to a separate file and import it.

O

OpenCode

Open-source terminal agent
AGENTS.md
  1. Save the master prompt as AGENTS.md in your project root.
  2. Start OpenCode in that folder with opencode.
  3. First message: “Read AGENTS.md and confirm the endpoint list.” Then describe the app.

AGENTS.md is also read by Codex, Cursor and Copilot, so one file covers several tools.

X

OpenAI Codex CLI

Terminal agent
AGENTS.md
  1. Save the master prompt as AGENTS.md in the project root (subfolders can have their own).
  2. Run codex in that folder.
  3. Describe the app. Codex reads the file before it edits anything.
Cu

Cursor

AI code editor
AGENTS.md or .cursor/rules/ytapis.md
  1. Option A: save as AGENTS.md in the project root.
  2. Option B: put it in .cursor/rules/ytapis.md for project rules, or paste the short prompt into Cursor’s global rules in Settings.
  3. Open the chat, make sure the file is in context, then describe the app.
G

Gemini CLI

Google · terminal agent
GEMINI.md (or point it at AGENTS.md)
  1. Save the master prompt as GEMINI.md in the project root.
  2. Or keep one AGENTS.md and set Gemini to read it in .gemini/settings.json: {"context":{"fileName":["AGENTS.md","GEMINI.md"]}}
  3. Run gemini in the folder and describe the app.
Gh

GitHub Copilot

VS Code, JetBrains, Copilot Chat
.github/copilot-instructions.md
  1. Create the folder .github if it is missing, then save the prompt as copilot-instructions.md inside it.
  2. Open Copilot Chat in agent mode and ask it to follow the instructions.
W

Windsurf

AI IDE
AGENTS.md (or Windsurf Rules)
  1. Save as AGENTS.md in the project root.
  2. Or paste the short prompt into Windsurf’s rules or memories settings for a global rule.
Cl

Cline / Roo Code

VS Code extensions
.clinerules (or custom instructions)
  1. Save the prompt as .clinerules in the project root.
  2. Or paste the short prompt into the extension’s custom instructions box.
Ai

Aider

Terminal pair programmer
CONVENTIONS.md + --read
  1. Save the prompt as CONVENTIONS.md in the project root.
  2. Start Aider with aider --read CONVENTIONS.md so it is loaded as read-only context.
Ch

ChatGPT · Claude.ai · Gemini (web)

Browser chats, no local files
Custom instructions · Project · Gem
  1. ChatGPT: paste the short prompt into custom instructions or a project’s instructions.
  2. Claude.ai: create a Project and paste the short prompt into its instructions.
  3. Gemini: create a Gem with the short prompt. Or start a new chat and paste the full prompt as the first message.

Browser chats cannot run the API for you. They write the code, and you run it on your machine.

?

Any other agent

Anything with rules or a system prompt
System prompt · rules · custom instructions
  1. If the tool reads a project file, save the prompt under the name it expects.
  2. If it has a rules or system-prompt box, paste the short prompt there.
  3. If it accepts attachments, attach ytapis-agent-prompt.md and say “read this first”.
Step 2 · Describe the app

A message template that works

After the prompt is loaded, send one message with this shape. The more specific it is, the better the first draft.

Your first message (fill in the brackets)
Read the ytapis guide first (AGENTS.md / CLAUDE.md / the pasted prompt).
Then build: [describe the app in 2–3 sentences, for example "a page where I paste a YouTube link and get a summary"].

Stack: [plain HTML + JS / Next.js / Python + Flask / Node CLI / other].
Where it runs: [browser / my server / my terminal].
Output: [what the user sees or what the script prints].

Rules:
- Use only the endpoints in the guide. Do not invent any.
- Handle empty lists, errors and loading states.
- Cache responses and add delays between bulk requests.

Before you finish: run the self-test from the guide with real calls, show me the output, and list the files you created and the exact command to run the app.

Ready-to-paste examples

Trend tool (Python)

“Build a Python command-line tool that takes a list of keywords, fetches the top 15 results for each from ytapis, and writes a CSV with title, channel, views and duration. Use requests and add a half-second delay between calls.”

Shorts page (HTML)

“Build a single index.html with plain JavaScript that searches ytapis Shorts for a topic typed in an input box and shows each result as an embedded player. No build step.”

Live alert bot (Node.js)

“Create a Node 20 script that checks a list of video IDs once a minute using /video/{id}/live and posts to a Discord webhook from an environment variable when a stream goes live. Alert only on the change.”

Video dashboard (Next.js)

“Create a Next.js app with a search page and a video page. Use server routes to call ytapis so the browser never talks to the API directly. Cache results for 10 minutes and show stats on the video page.”

Verify it

How to tell the agent learned the API

Ask this one question right after loading the prompt. A good agent answers correctly from the file, with no guessing.

Self-test question
Without writing code yet, answer from the guide:
1. Which package name do I install for TypeScript, and why not "ytapis"?
2. What does GET /shorts return when q is missing?
3. Which fields are numbers, and which are display strings?
4. Which endpoints can return an empty list right now?
5. Is an invalid video ID a 404?

Correct answers

  1. ytapis-core. The npm package named ytapis does not exist.
  2. A 400 with {"error": "Missing query param \"q\""}.
  3. Numbers: durationSeconds, viewCountRaw. Display strings: duration, viewCount.
  4. Trending, comments, related, transcript, channel uploads and playlists.
  5. No. It returns placeholder data with a 200, so validate IDs first.
Troubleshooting

When something goes sideways

The agent says it cannot find the endpoints or ignores the file

The file is probably not in the folder the agent reads. Put it in the project root with the exact name from the setup table. Start a new session, then ask: “What does the ytapis guide say about empty results?” If it cannot answer, paste the prompt into the chat directly.

The agent runs npm install ytapis and it fails

Correct. There is no npm package called ytapis. Use npm install ytapis-core for TypeScript or JavaScript. The master prompt already says this, so check that the agent loaded the latest version.

The agent invents endpoints, parameters or fields

Reply: “Only use the endpoints in the guide. Show me every URL you call, and remove any that are not listed.” If it keeps guessing, use the short prompt with the endpoint list at the top, or ask it to answer the self-test first.

Results come back empty (trending, comments, transcript…)

This is normal right now. Those endpoints return [] upstream. The app should show a clear message and fall back to search. See the endpoint status for the current list.

Requests fail with 429, timeouts or network errors

Slow down. Cache results for several minutes, add a delay between calls and retry with exponential backoff. Ask the agent to add that logic. The recipe is in the master prompt.

The prompt is too long for my chat

Use the short version. For agents that read files, keep the full prompt in the project and tell the agent: “Read the guide when you need endpoint details.”

Browser shows a CORS error or “Failed to fetch”

The API allows every origin, so a CORS error usually means the URL is wrong or the request is going somewhere else (for example, a typo, or localhost in a deployed site). Open the URL directly in a browser tab. You should see JSON.

Use it responsibly

ytapis is unofficial. It reads public YouTube pages and is not affiliated with or endorsed by YouTube or Google. Build responsible tools: attribute creators and link to their videos, do not re-host or download video files, keep request rates polite, and follow YouTube’s Terms of Service and copyright rules. The master prompt tells agents to do the same.