transcribe
transcribe(url: str) -> TranscriptResponsePOST /transcribe0 cached; 1 new, or 2 over 2 hours
Pass a YouTube video URL. Returns the transcript only, not a summary. Maximum video duration: 4 hours.
easytranscriber-sdk 0.1.0 · Python 3.10+
Use synchronous or asynchronous Python clients for YouTube transcripts, separate summaries, discovery, and comments. The SDK returns typed dictionaries and leaves pagination under your control.
Try the API — no card requiredInstall the published easytranscriber-sdk package with Python 3.10 or newer. Import it as easytranscriber_sdk. The differently named PyPI package easytranscriber is unrelated; do not install it for this API.
pip install easytranscriber-sdkCreate an API key in your dashboard using the API-key button above. Keys begin with et_. Pass it explicitly as api_key; the client does not automatically read environment variables. It sends Authorization: Bearer <api_key> and Accept: application/json. Keep keys in server-side environment variables, never browser code or source control. Empty or whitespace-only keys raise ValueError.
import os
from easytranscriber_sdk import EasyTranscriber
# Set EASYTRANSCRIBER_API_KEY in your environment first.
# Your application reads it; the SDK requires an explicit api_key.
with EasyTranscriber(api_key=os.environ["EASYTRANSCRIBER_API_KEY"]) as client:
transcript = client.transcribe("https://www.youtube.com/watch?v=dQw4w9WgXcQ")
print(transcript["transcript"])
print(transcript["credits_used"])
# Separate request: costs 1 additional credit, even for cached transcripts.
summary = client.summarize(
transcript["transcript"],
video_id=transcript["video_id"],
language="English",
)
print(summary["summary"])transcribe() does not generate a summary. summarize() is a separate, non-streaming API call and always costs an additional credit. Use dictionary access such as transcript["transcript"], not attribute access such as transcript.transcript.
import asyncio
import os
from easytranscriber_sdk import AsyncEasyTranscriber
async def main() -> None:
async with AsyncEasyTranscriber(
api_key=os.environ["EASYTRANSCRIBER_API_KEY"]
) as client:
latest = await client.channel_latest("@NASA")
print(latest["videos"])
balance = await client.credits()
print(balance["balance"])
asyncio.run(main())AsyncEasyTranscriber has the same constructor options, method parameters, and result models as EasyTranscriber; await every API method. Reuse a client for connection pooling. Context managers close connections even when an exception occurs.
Without a context manager, use client.close() for the synchronous client or await client.aclose() for the asynchronous client, ideally in finally. Both expose the boolean is_closed property. Create a new client after closing one. Async retry waits use non-blocking sleep, and cancellation propagates.
Only api_key: str is required. All other constructor parameters are keyword-only.
| Option | Default | Behavior |
|---|---|---|
| base_url: str | https://www.easytranscriber.com/api/v1 | Complete API prefix; trailing slash is normalized. Use HTTPS for real API keys. |
| timeout: float | httpx.Timeout | None | 310.0 | HTTPX timeout in seconds per network operation, not a total deadline. Pass an httpx.Timeout for finer control, or None to disable timeouts. |
| max_retries: int | 2 | Additional attempts for HTTP 429 only (at most 3 attempts by default). Set 0 to disable. Must be a non-negative integer, not a bool. |
| retry_backoff: float | 0.5 | Initial exponential backoff in seconds, doubled after each retry. Finite and non-negative; bounded by max_retry_delay. |
| max_retry_delay: float | 60.0 | Maximum wait in seconds. Finite and non-negative. A longer server Retry-After surfaces the 429 instead of retrying early. |
The default timeout allows for the server processing window; backoff waits and retry attempts add time. Redirects are not followed. Invalid retry configuration raises ValueError.
Signatures below apply to both clients; the async versions must be awaited. The * marks keyword-only options. Optional None values are omitted from requests. REST paths are relative to /api/v1: POST parameters become JSON fields, while GET parameters become query parameters.
transcribe(url: str) -> TranscriptResponsePOST /transcribe0 cached; 1 new, or 2 over 2 hours
Pass a YouTube video URL. Returns the transcript only, not a summary. Maximum video duration: 4 hours.
summarize(transcript: str, *, video_id: str | None = None, language: str | None = None) -> SummaryResponsePOST /summarize1 additional credit per call
Pass transcript text, with an optional video ID and summary language. Use a supported display name such as English or Spanish, or Auto—not an ISO code. None omits the option. Returns a complete JSON summary, not a stream. A cached transcript does not make summarization free.
credits() -> CreditsResponseGET /creditsFree
Read the current credit balance. No arguments and no credits_used field.
search(q: str, *, type: SearchType = "video", page_token: str | None = None) -> SearchResponseGET /youtube/search1 credit per page
Search YouTube with a query string. SearchType is Literal["video", "channel"]. Channel results may have video_id=None.
resolve_channel(channel: str) -> ChannelResponseGET /youtube/channel/resolveFree
Resolve a channel @handle, channel URL, or UC… channel ID to channel details. Subscribers is a nullable string, not an integer.
channel_latest(channel: str) -> ChannelLatestResponseGET /youtube/channel/latestFree
Get recent uploads from a channel RSS feed. Accepts a @handle, channel URL, or UC… channel ID. No pagination or credits_used field.
channel_search(channel: str, q: str, *, page_token: str | None = None) -> ChannelSearchResponseGET /youtube/channel/search1 credit per page
Search within a channel using a query string. Channel accepts a @handle, channel URL, or UC… channel ID.
channel_videos(channel: str, *, page_token: str | None = None) -> ChannelVideosResponseGET /youtube/channel/videos1 credit per page
List channel uploads. Channel accepts a @handle, channel URL, or UC… channel ID. Fetch further pages explicitly.
playlist_videos(playlist: str, *, page_token: str | None = None) -> PlaylistVideosResponseGET /youtube/playlist/videos1 credit per page
List videos from a playlist ID or YouTube playlist URL. Fetch further pages explicitly.
comments(video: str, *, max_results: int = 20, order: CommentOrder = "relevance", page_token: str | None = None) -> CommentsResponseGET /youtube/comments1 credit per page
Pass a YouTube video URL or ID. CommentOrder is Literal["relevance", "time"]. The server bounds max_results to 1–100 (zero falls back to 20). Replies contain the replies returned by YouTube, not necessarily every reply.
All models below are exported from easytranscriber_sdk as TypedDict contracts. They describe plain JSON dictionaries, with no data envelope and no runtime schema validation. Unknown fields are preserved. JSON null becomes None; dates stay strings. Nullable fields are not the same as omitted fields.
| Model | Fields and types |
|---|---|
| TranscriptResponse | video_id: str; video_title: str | None; transcript: str; method: str; cached: bool; credits_used: int |
| SummaryResponse | summary: str; video_id: str | None; credits_used: int |
| CreditsResponse | balance: int |
| SearchResult | video_id: str | None; channel_id: str; title: str; channel: str; published_at: str; thumbnail: str | None |
| SearchResponse | results: list[SearchResult]; next_page_token: str | None; credits_used: int |
| ChannelSearchResponse | All SearchResponse fields, plus channel_id: str |
| ChannelResponse | channel_id: str; name: str; handle: str | None; subscribers: str | None; thumbnail: str | None |
| LatestVideo | video_id: str; title: str; published_at: str; thumbnail: str |
| ChannelLatestResponse | channel_id: str; videos: list[LatestVideo] |
| Video | video_id: str; title: str; published_at: str; thumbnail: str | None |
| VideosPage | videos: list[Video]; next_page_token: str | None; total_results: int | None; credits_used: int |
| ChannelVideosResponse | All VideosPage fields, plus channel_id: str |
| PlaylistVideosResponse | All VideosPage fields, plus playlist_id: str |
| CommentReply | author: str; text: str; likes: int; published_at: str; updated_at: str |
| Comment | All CommentReply fields, plus reply_count: int; replies: list[CommentReply] |
| CommentsResponse | video_id: str; comments: list[Comment]; next_page_token: str | None; total_results: int | None; credits_used: int |
method is an unrestricted string, not an enum. LatestVideo.thumbnail is a string, whereas Video.thumbnail and SearchResult.thumbnail can be None. Free responses do not add a synthetic credits_used field. The exported option aliases are SearchType and CommentOrder, as shown in the method reference.
import os
from easytranscriber_sdk import EasyTranscriber
with EasyTranscriber(api_key=os.environ["EASYTRANSCRIBER_API_KEY"]) as client:
page = client.channel_videos("@NASA") # 1 credit
for video in page["videos"]:
print(video["video_id"], video["title"])
# Deliberate second request, not an automatic iterator: another credit.
token = page["next_page_token"]
if token is not None:
next_page = client.channel_videos("@NASA", page_token=token)
print(next_page["videos"])The SDK never fetches another page automatically. Pass next_page_token unchanged as page_token=; HTTPX encodes it. None means there is no next page. The same pattern applies to search, channel_search, playlist_videos, and comments. Every requested page costs one credit. channel_latest has no page token.
import os
import httpx
from easytranscriber_sdk import APIError, APIResponseError, EasyTranscriber
with EasyTranscriber(
api_key=os.environ["EASYTRANSCRIBER_API_KEY"], max_retries=2
) as client:
try:
result = client.transcribe("https://www.youtube.com/watch?v=dQw4w9WgXcQ")
print(result["transcript"])
except APIError as error:
print(error.status, error.code, error.message)
# Fix 400/401 input or credentials; 402 requires credits.
# A surfaced 429 exhausted retries or requested too long a wait.
except APIResponseError as error:
print("Unexpected successful response:", error.status)
# Do not blindly repeat a potentially billed request.
except httpx.TimeoutException:
print("Timed out; the server may already have charged credits.")
except httpx.RequestError as error:
print("Transport failure; outcome unknown:", error)
# Check balance/history before deciding whether to try again.Non-success HTTP responses raise APIError with status: int, code: str, and message: str. The SDK extracts those strings from the API error envelope:
{"error":{"code":"insufficient_credits","message":"Insufficient credits"}}If an error body lacks usable code or message strings, the corresponding fallbacks are http_error and the HTTP reason phrase (or Request failed). HTML error bodies are not included. A successful response that is not a JSON object raises APIResponseError, which has a status attribute. A JSON object is not otherwise schema-validated.
HTTPX transport errors, including httpx.TimeoutException and other httpx.RequestError exceptions, propagate unchanged. The SDK does not wrap them in APIError.
Default 429 waits are 0.5 then 1 second. Retry-After accepts seconds or an HTTP date; the client waits at least the larger of that value and the backoff. Invalid or non-finite headers fall back to backoff, and past dates are treated as zero. If the server requests more than max_retry_delay, the SDK raises the 429 immediately rather than retrying too early.
No automatic retries occur for network failures, timeouts, 5xx responses, or malformed successful responses. Do not add blind retries around billed calls, including billable GET requests: the server may have charged credits before a response was lost. Inspect balance/history and the failure before deciding whether another request is appropriate.
API access is available on the Free plan. Request limits are Free: 10/minute, Plus: 10/minute, Pro: 50/minute, and Scale: 300/minute. A free endpoint still makes an API request. Use returned credits_used values where present and credits()["balance"] for accounting.