Telegram Downloader API

Telegram Downloader API documentation

One endpoint turns a public Telegram post into a direct download link. This page covers everything you need to call it from a server, a bot or an app.

Introduction

The API is a JSON REST API served over HTTPS. The base URL is:

https://telegramdownloader.net/api/v1

It works with links to a single post in a public channel or public group, for example https://t.me/durov/537. It returns the file in the highest quality Telegram stores for that post, along with a download link and, for video and audio, a link that plays in the browser.

Only successful calls are counted against your plan. If a post was deleted, has no file, or something fails on our side, the call is free.

Authentication

Create a key in your dashboard and send it with every request in the X-Api-Key header. Authorization: Bearer <key> also works.

X-Api-Key: tdk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Keys start with tdk_. We store only a hash of each key, so we can show it to you once, when you create it. Keep it on your server: never ship it inside a mobile app or a web page, where anyone can read it. If a key leaks, revoke it in the dashboard and create a new one.

Quick start

curl -X POST https://telegramdownloader.net/api/v1/resolve \
  -H "X-Api-Key: $TD_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://t.me/durov/537"}'

The response contains download_url. Open it in a browser and the file starts downloading.

POST /v1/resolve

POSThttps://telegramdownloader.net/api/v1/resolve

Prepares a file from a public post and returns signed links to it. Send the body as JSON. Form-encoded bodies are accepted too.

Request body

FieldTypeRequiredDescription
urlstringyesLink to one public post: https://t.me/channel/123. Links in the t.me/s/channel/123 form are accepted and normalised.
ipstringnoIP address of the person who will download the file. Send it whenever the link is for an end user: per-IP limits then apply to them and not to your server. Defaults to the IP that made the call.

Response 200

{
  "download_url": "https://dl.telegramdownloader.net/dl/ab12cd34ef.1792484409.7bb34bb53cfc1d12",
  "stream_url": "https://dl.telegramdownloader.net/s/ab12cd34ef.1792484409.7bb34bb53cfc1d12",
  "preview_url": "https://telegramdownloader.net/api/v1/thumb?u=…",
  "file_name": "interview-1080p.mp4",
  "file_size": 734003200,
  "mime_type": "video/mp4",
  "expires_at": "2026-10-22T12:00:00+00:00",
  "usage": {
    "used": 41,
    "limit": 1000,
    "remaining": 959,
    "period_ends_at": "2026-11-18T09:12:44+00:00"
  }
}
FieldDescription
download_urlStarts a download (Content-Disposition: attachment).
stream_urlSame file, served inline for playback in a <video> or <audio> tag. May be null.
preview_urlThumbnail image when Telegram has one, otherwise null. Valid for 24 hours.
file_name, file_size, mime_typeOriginal name, size in bytes and media type of the file.
expires_atISO 8601 time after which both links stop working.
usageYour balance after this call.

Using the download and stream links

# resume a download from 100 MB
curl -H "Range: bytes=104857600-" -o file.mp4 "$DOWNLOAD_URL"
Browsers can play MP4 (H.264), WebM and MP3 inline. Files in MKV or other containers often will not play in a browser: offer the download link for those.

GET /v1/usage

GEThttps://telegramdownloader.net/api/v1/usage

Returns your current balance. It is free and never counted.

{
  "plan": "Pro",
  "used": 41,
  "limit": 1000,
  "remaining": 959,
  "period_ends_at": "2026-11-18T09:12:44+00:00",
  "rate_limit_per_minute": 120
}

Errors

Errors use standard HTTP status codes and always have the same shape:

{ "error": { "code": "quota_exceeded", "message": "You have used all requests in your plan." } }
StatuscodeMeaningCounted
401missing_api_key, invalid_api_keyNo key, or the key is wrong or revoked.No
402no_active_plan, quota_exceededBuy a plan or add requests. The body includes buy_url.No
403channel_blockedDownloads from this channel are disabled.No
403ip_blockedThe end-user IP is blocked or rate limited.No
403account_suspendedContact support.No
404post_not_foundThe post does not exist or was deleted.No
404no_mediaThe post has no file attached.No
406content_protectedThe channel switched on Telegram's content protection.No
415file_type_blockedThis file type is not supported (for example APK).No
422invalid_url, invalid_ipThe link is not a public post link, or ip is not an IP address.No
429rate_limitedToo many calls this minute. Wait for Retry-After seconds.No
502upstream_errorThe file could not be prepared right now. Retry after a short wait.No
503service_unavailableTemporary outage. Retry with backoff.No

Quotas and rate limits

Each plan has a number of successful requests for its period and a per-minute rate limit. Every response from /v1/resolve carries these headers:

HeaderDescription
X-RateLimit-LimitCalls allowed per minute on your plan.
X-RateLimit-RemainingCalls left in the current minute.
X-Quota-RemainingSuccessful requests left in your plan (on 200 responses).
Retry-AfterSeconds to wait (on 429 responses).

When you buy a plan while one is active, the new requests are added to what you have left and the period starts again. We email you when you reach 80% and 100% of your requests.

Good practice

Code examples

Node.js

async function resolve(url, userIp) {
  const res = await fetch("https://telegramdownloader.net/api/v1/resolve", {
    method: "POST",
    headers: { "X-Api-Key": process.env.TD_KEY, "Content-Type": "application/json" },
    body: JSON.stringify({ url, ip: userIp }),
    signal: AbortSignal.timeout(90_000),
  });
  const data = await res.json();
  if (!res.ok) throw new Error(`${res.status} ${data.error.code}: ${data.error.message}`);
  return data;
}

Python

import os, requests

def resolve(url, user_ip=None):
    r = requests.post(
        "https://telegramdownloader.net/api/v1/resolve",
        headers={"X-Api-Key": os.environ["TD_KEY"]},
        json={"url": url, "ip": user_ip},
        timeout=90,
    )
    data = r.json()
    if r.status_code != 200:
        raise RuntimeError(f'{r.status_code} {data["error"]["code"]}')
    return data

PHP

function td_resolve(string $url, ?string $userIp = null): array {
    $ch = curl_init('https://telegramdownloader.net/api/v1/resolve');
    curl_setopt_array($ch, [
        CURLOPT_POST           => true,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT        => 90,
        CURLOPT_HTTPHEADER     => ['X-Api-Key: ' . getenv('TD_KEY'), 'Content-Type: application/json'],
        CURLOPT_POSTFIELDS     => json_encode(['url' => $url, 'ip' => $userIp]),
    ]);
    $data = json_decode(curl_exec($ch), true);
    if (curl_getinfo($ch, CURLINFO_HTTP_CODE) !== 200) {
        throw new RuntimeException($data['error']['message'] ?? 'Request failed');
    }
    return $data;
}

Telegram bot (python-telegram-bot)

async def on_message(update, context):
    link = update.message.text.strip()
    try:
        data = resolve(link, None)
    except RuntimeError as e:
        await update.message.reply_text(f"Could not get that file: {e}")
        return
    await update.message.reply_text(f'{data["file_name"]}\n{data["download_url"]}')

Get a key and try it

Sign up, confirm your email and your first call is a minute away.

Create an account