{
  "openapi": "3.0.3",
  "info": {
    "title": "EasyTranscriber API",
    "description": "Transcribe and summarize YouTube videos programmatically. Features include caption extraction, AI-powered speech-to-text for videos without captions, AI summarization, YouTube search, and channel/playlist management.",
    "version": "1.0.0",
    "contact": {
      "name": "EasyTranscriber",
      "url": "https://easytranscriber.com"
    },
    "termsOfService": "https://easytranscriber.com/terms"
  },
  "servers": [
    {
      "url": "https://easytranscriber.com",
      "description": "Production"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "paths": {
    "/api/v1/transcribe": {
      "post": {
        "operationId": "transcribe",
        "summary": "Transcribe a YouTube video",
        "description": "Extract the full transcript of a YouTube video. Cached transcripts are returned instantly at no credit cost. Videos without captions are transcribed using AI speech-to-text (Deepgram). Max video duration: 4 hours.",
        "tags": ["Transcription"],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["url"],
                "properties": {
                  "url": {
                    "type": "string",
                    "description": "YouTube video URL",
                    "example": "https://youtube.com/watch?v=dQw4w9WgXcQ"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Transcript retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TranscribeResponse"
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "402": { "$ref": "#/components/responses/InsufficientCredits" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "500": {
            "description": "Transcription failed (credits refunded)",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          }
        }
      }
    },
    "/api/v1/summarize": {
      "post": {
        "operationId": "summarize",
        "summary": "Summarize a transcript",
        "description": "Generate an AI-powered summary from a transcript. Costs 1 credit. Transcripts longer than 100K characters are truncated.",
        "tags": ["Summarization"],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["transcript"],
                "properties": {
                  "transcript": {
                    "type": "string",
                    "description": "Transcript text to summarize"
                  },
                  "video_id": {
                    "type": "string",
                    "description": "Optional video ID to link summary to a saved transcription",
                    "example": "dQw4w9WgXcQ"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Summary generated successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SummarizeResponse"
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "402": { "$ref": "#/components/responses/InsufficientCredits" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "500": {
            "description": "Summarization failed (credit refunded)",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          }
        }
      }
    },
    "/api/v1/youtube/search": {
      "get": {
        "operationId": "searchYouTube",
        "summary": "Search YouTube",
        "description": "Search YouTube for videos or channels. Returns up to 10 results per page. Costs 1 credit.",
        "tags": ["YouTube Search"],
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": true,
            "description": "Search query",
            "schema": { "type": "string" },
            "example": "nextjs tutorial"
          },
          {
            "name": "type",
            "in": "query",
            "description": "Search type",
            "schema": {
              "type": "string",
              "enum": ["video", "channel"],
              "default": "video"
            }
          },
          {
            "name": "page_token",
            "in": "query",
            "description": "Pagination token from previous response",
            "schema": { "type": "string" }
          }
        ],
        "responses": {
          "200": {
            "description": "Search results",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SearchResponse"
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "402": { "$ref": "#/components/responses/InsufficientCredits" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/api/v1/youtube/channel/resolve": {
      "get": {
        "operationId": "resolveChannel",
        "summary": "Resolve a channel",
        "description": "Resolve a YouTube channel @handle, URL, or channel ID to channel details. Free, no credit cost.",
        "tags": ["Channel"],
        "parameters": [
          {
            "name": "channel",
            "in": "query",
            "required": true,
            "description": "@handle, channel URL, or UC... channel ID",
            "schema": { "type": "string" },
            "example": "@MrBeast"
          }
        ],
        "responses": {
          "200": {
            "description": "Channel details",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChannelResolveResponse"
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/api/v1/youtube/channel/search": {
      "get": {
        "operationId": "searchChannel",
        "summary": "Search within a channel",
        "description": "Search for videos within a specific YouTube channel. Costs 1 credit.",
        "tags": ["Channel"],
        "parameters": [
          {
            "name": "channel",
            "in": "query",
            "required": true,
            "description": "@handle, channel URL, or UC... channel ID",
            "schema": { "type": "string" },
            "example": "@MrBeast"
          },
          {
            "name": "q",
            "in": "query",
            "required": true,
            "description": "Search query",
            "schema": { "type": "string" },
            "example": "challenge"
          },
          {
            "name": "page_token",
            "in": "query",
            "description": "Pagination token",
            "schema": { "type": "string" }
          }
        ],
        "responses": {
          "200": {
            "description": "Channel search results",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChannelSearchResponse"
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "402": { "$ref": "#/components/responses/InsufficientCredits" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/api/v1/youtube/channel/videos": {
      "get": {
        "operationId": "getChannelVideos",
        "summary": "List channel uploads",
        "description": "List all uploads from a YouTube channel with pagination. Up to 50 videos per page. Costs 1 credit per page.",
        "tags": ["Channel"],
        "parameters": [
          {
            "name": "channel",
            "in": "query",
            "required": true,
            "description": "@handle, channel URL, or UC... channel ID",
            "schema": { "type": "string" },
            "example": "@MrBeast"
          },
          {
            "name": "page_token",
            "in": "query",
            "description": "Pagination token for next page",
            "schema": { "type": "string" }
          }
        ],
        "responses": {
          "200": {
            "description": "Channel videos",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChannelVideosResponse"
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "402": { "$ref": "#/components/responses/InsufficientCredits" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/api/v1/youtube/channel/latest": {
      "get": {
        "operationId": "getChannelLatest",
        "summary": "Latest channel videos",
        "description": "Get the latest ~15 videos from a YouTube channel via RSS feed. Free, no credit cost. Returns limited metadata.",
        "tags": ["Channel"],
        "parameters": [
          {
            "name": "channel",
            "in": "query",
            "required": true,
            "description": "@handle, channel URL, or UC... channel ID",
            "schema": { "type": "string" },
            "example": "@MrBeast"
          }
        ],
        "responses": {
          "200": {
            "description": "Latest videos",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChannelLatestResponse"
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/api/v1/youtube/playlist/videos": {
      "get": {
        "operationId": "getPlaylistVideos",
        "summary": "List playlist videos",
        "description": "List videos in a YouTube playlist with pagination. Up to 50 videos per page. Costs 1 credit per page.",
        "tags": ["Playlist"],
        "parameters": [
          {
            "name": "playlist",
            "in": "query",
            "required": true,
            "description": "Playlist ID (PLxxx) or YouTube playlist URL",
            "schema": { "type": "string" },
            "example": "PLrAXtmErZgOeiKm4sgNOknGvNjby9efdf"
          },
          {
            "name": "page_token",
            "in": "query",
            "description": "Pagination token for next page",
            "schema": { "type": "string" }
          }
        ],
        "responses": {
          "200": {
            "description": "Playlist videos",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlaylistVideosResponse"
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "402": { "$ref": "#/components/responses/InsufficientCredits" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/api/v1/youtube/comments": {
      "get": {
        "operationId": "getVideoComments",
        "summary": "Get video comments",
        "description": "Get comments from a YouTube video. Returns top-level comments with replies. Costs 1 credit per page.",
        "tags": ["Comments"],
        "parameters": [
          {
            "name": "video",
            "in": "query",
            "required": true,
            "description": "YouTube video URL or video ID",
            "schema": { "type": "string" },
            "example": "dQw4w9WgXcQ"
          },
          {
            "name": "max_results",
            "in": "query",
            "description": "Number of comment threads per page (1-100, default 20)",
            "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 20 }
          },
          {
            "name": "order",
            "in": "query",
            "description": "Sort order",
            "schema": {
              "type": "string",
              "enum": ["relevance", "time"],
              "default": "relevance"
            }
          },
          {
            "name": "page_token",
            "in": "query",
            "description": "Pagination token from previous response",
            "schema": { "type": "string" }
          }
        ],
        "responses": {
          "200": {
            "description": "Comments retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CommentsResponse"
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "402": { "$ref": "#/components/responses/InsufficientCredits" },
          "403": {
            "description": "Comments disabled on this video (credit refunded)",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "500": {
            "description": "Failed to fetch comments (credit refunded)",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          }
        }
      }
    },
    "/api/v1/credits": {
      "get": {
        "operationId": "getCredits",
        "summary": "Check credit balance",
        "description": "Check your current credit balance. Free, no credit cost.",
        "tags": ["Account"],
        "responses": {
          "200": {
            "description": "Credit balance",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "balance": {
                      "type": "integer",
                      "example": 42
                    }
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "API key from your dashboard. Keys are prefixed with `et_`."
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "example": "invalid_request"
              },
              "message": {
                "type": "string",
                "example": "Missing required field: url"
              }
            }
          }
        }
      },
      "TranscribeResponse": {
        "type": "object",
        "properties": {
          "video_id": { "type": "string", "example": "dQw4w9WgXcQ" },
          "video_title": { "type": "string", "example": "Rick Astley - Never Gonna Give You Up" },
          "transcript": { "type": "string", "example": "We're no strangers to love..." },
          "method": {
            "type": "string",
            "enum": ["captions", "deepgram"],
            "description": "`captions` = extracted from YouTube captions, `deepgram` = AI speech-to-text"
          },
          "cached": { "type": "boolean", "example": false },
          "credits_used": { "type": "integer", "example": 1 }
        }
      },
      "SummarizeResponse": {
        "type": "object",
        "properties": {
          "summary": { "type": "string", "example": "This video covers..." },
          "video_id": { "type": "string", "nullable": true, "example": "dQw4w9WgXcQ" },
          "credits_used": { "type": "integer", "example": 1 }
        }
      },
      "SearchResult": {
        "type": "object",
        "properties": {
          "video_id": { "type": "string", "nullable": true, "example": "dQw4w9WgXcQ" },
          "channel_id": { "type": "string", "example": "UCuAXFkgsw1L7xaCfnd5JJOw" },
          "title": { "type": "string", "example": "Video Title" },
          "channel": { "type": "string", "example": "Channel Name" },
          "published_at": { "type": "string", "format": "date-time", "example": "2009-10-25T06:57:33Z" },
          "thumbnail": { "type": "string", "nullable": true, "example": "https://i.ytimg.com/vi/dQw4w9WgXcQ/hqdefault.jpg" }
        }
      },
      "SearchResponse": {
        "type": "object",
        "properties": {
          "results": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/SearchResult" }
          },
          "next_page_token": { "type": "string", "nullable": true },
          "credits_used": { "type": "integer", "example": 1 }
        }
      },
      "ChannelResolveResponse": {
        "type": "object",
        "properties": {
          "channel_id": { "type": "string", "example": "UCX6OQ3DkcsbYNE6H8uQQuVA" },
          "name": { "type": "string", "example": "MrBeast" },
          "handle": { "type": "string", "example": "@MrBeast" },
          "subscribers": { "type": "string", "example": "300000000" },
          "thumbnail": { "type": "string", "example": "https://yt3.ggpht.com/..." }
        }
      },
      "VideoItem": {
        "type": "object",
        "properties": {
          "video_id": { "type": "string", "example": "abc123" },
          "title": { "type": "string", "example": "Video Title" },
          "published_at": { "type": "string", "format": "date-time" },
          "thumbnail": { "type": "string", "nullable": true }
        }
      },
      "ChannelSearchResponse": {
        "type": "object",
        "properties": {
          "channel_id": { "type": "string" },
          "results": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/SearchResult" }
          },
          "next_page_token": { "type": "string", "nullable": true },
          "credits_used": { "type": "integer", "example": 1 }
        }
      },
      "ChannelVideosResponse": {
        "type": "object",
        "properties": {
          "channel_id": { "type": "string" },
          "videos": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/VideoItem" }
          },
          "next_page_token": { "type": "string", "nullable": true },
          "total_results": { "type": "integer", "nullable": true },
          "credits_used": { "type": "integer", "example": 1 }
        }
      },
      "ChannelLatestResponse": {
        "type": "object",
        "properties": {
          "channel_id": { "type": "string" },
          "videos": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/VideoItem" }
          }
        }
      },
      "CommentReply": {
        "type": "object",
        "properties": {
          "author": { "type": "string", "example": "User123" },
          "text": { "type": "string", "example": "Great point!" },
          "likes": { "type": "integer", "example": 5 },
          "published_at": { "type": "string", "format": "date-time" },
          "updated_at": { "type": "string", "format": "date-time" }
        }
      },
      "Comment": {
        "type": "object",
        "properties": {
          "author": { "type": "string", "example": "JohnDoe" },
          "text": { "type": "string", "example": "This is a great video!" },
          "likes": { "type": "integer", "example": 42 },
          "published_at": { "type": "string", "format": "date-time" },
          "updated_at": { "type": "string", "format": "date-time" },
          "reply_count": { "type": "integer", "example": 3 },
          "replies": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/CommentReply" }
          }
        }
      },
      "CommentsResponse": {
        "type": "object",
        "properties": {
          "video_id": { "type": "string", "example": "dQw4w9WgXcQ" },
          "comments": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/Comment" }
          },
          "next_page_token": { "type": "string", "nullable": true },
          "total_results": { "type": "integer", "nullable": true },
          "credits_used": { "type": "integer", "example": 1 }
        }
      },
      "PlaylistVideosResponse": {
        "type": "object",
        "properties": {
          "playlist_id": { "type": "string" },
          "videos": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/VideoItem" }
          },
          "next_page_token": { "type": "string", "nullable": true },
          "total_results": { "type": "integer", "nullable": true },
          "credits_used": { "type": "integer", "example": 1 }
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Invalid request",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" },
            "example": {
              "error": { "code": "invalid_request", "message": "Missing required field: url" }
            }
          }
        }
      },
      "Unauthorized": {
        "description": "Authentication failed",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" },
            "example": {
              "error": { "code": "unauthorized", "message": "Invalid, expired, or revoked API key" }
            }
          }
        }
      },
      "InsufficientCredits": {
        "description": "Not enough credits",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" },
            "example": {
              "error": { "code": "insufficient_credits", "message": "Insufficient credits. Purchase more at /pricing." }
            }
          }
        }
      },
      "NotFound": {
        "description": "Resource not found",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" },
            "example": {
              "error": { "code": "not_found", "message": "Channel not found" }
            }
          }
        }
      },
      "RateLimited": {
        "description": "Rate limit exceeded",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" },
            "example": {
              "error": { "code": "rate_limited", "message": "Rate limit exceeded. Maximum 30 requests per minute." }
            }
          }
        }
      }
    }
  },
  "tags": [
    { "name": "Transcription", "description": "Extract video transcripts" },
    { "name": "Summarization", "description": "AI-powered video summaries" },
    { "name": "YouTube Search", "description": "Search YouTube content" },
    { "name": "Channel", "description": "Channel data and search" },
    { "name": "Comments", "description": "Video comment threads" },
    { "name": "Playlist", "description": "Playlist video listings" },
    { "name": "Account", "description": "Account and billing" }
  ]
}
