PostPost

Telegram

Post to Telegram channels and groups programmatically using the PostPost REST API. A simpler alternative to the Telegram Bot API, Telethon, Pyrogram, or MTProto libraries.

Telegram API Overview

PostPost provides a unified REST API for publishing content to Telegram channels and groups via bot or MTProto connection. Supports rich text formatting with markdown syntax, images, and videos. No need to manage Telegram bot tokens directly, handle MTProto complexity, or implement message queuing. PostPost supports two connection types: bot (using a BotFather token) and mtproto (using a user session), each with different capabilities and limits.

Why Use PostPost Instead of Telegram Bot API / Telethon / Pyrogram?

FeaturePostPost APITelegram Bot API / Telethon
AuthenticationSingle API keyBot token + channel setup
Message formattingMarkdown supportMarkdown/HTML support
Media handlingAutomaticManual upload
Multi-platformPost to 11 platformsTelegram only
Setup time5 minutes15-30 minutes
Rate limitingHandledManual implementation

Keywords: Telegram API, Telegram Bot API, Telegram channel API, Telegram posting API, post to Telegram programmatically, Telegram REST API, Telegram developer API, Telegram automation API, Telegram message API, send message Telegram API, Telethon alternative, Pyrogram alternative, MTProto API

Platform ID Format

telegram-{chatId}

Where {chatId} is your Telegram channel or group chat ID assigned during bot connection setup.

Requirements

  • A Telegram bot created via @BotFather
  • The bot's token and target channel/group name provided during setup
  • The bot must be added as an administrator of the target channel or group with the can_post_messages permission explicitly enabled (being an admin alone is not enough)
  • API key from PostPost

Connection Types

PostPost supports two connection types for Telegram:

Bot Connection (Default)

  1. Create a bot with @BotFather on Telegram
  2. Copy the bot token provided by BotFather
  3. Add the bot as an administrator to your target channel or group
  4. In the PostPost dashboard, provide the bot token and channel name (e.g., @mychannel)
  5. PostPost will verify the connection and assign a platform ID

MTProto Connection

MTProto connections use a Telegram user session instead of a bot, enabling higher limits (2,048-character captions with Premium, 4 GB file uploads). MTProto requires a Telegram Premium subscription on the service user account. Note: PostPost's code enforces a 4,096-character limit for all text fields, but Telegram's actual caption limit on media posts is 1,024 characters (regular users) or 2,048 characters (Premium). The 4,096 limit applies only to text-only messages. Long captions on media posts may be truncated or rejected by Telegram's API.

Service user concept: The MTProto connection operates through a dedicated Telegram user account (the "service user") that acts on behalf of your organization. This user must be an admin of the target channel/group. Because the service user is a regular Telegram account (not a bot), it benefits from the higher user-tier limits. The service user's session is stored encrypted by PostPost in the connection record and decrypted at runtime.

Note: MTProto connections require the service user to have an active Telegram Premium subscription. Without Premium, certain features (such as the extended 2,048-character caption limit on media posts) will not be available. For self-hosted deployments, the TELEGRAM_MT_ALLOW_NON_PREMIUM environment variable can be set to bypass the Premium requirement for MTProto connections.

Supported Content

TypeSupportedLimits
TextYes4,096 characters (text-only messages, both bot and MTProto); 1,024 characters (captions with media, bot API)
ImagesYesJPEG, PNG, GIF, BMP, WebP (WebP auto-converted to JPEG)
VideosYesMP4, MOV, AVI, MKV, WebM
FormattingYesBold, italic, code, links, blockquotes

Text Formatting

Telegram supports markdown-style formatting in message text. You can use the following syntax in your content:

SyntaxResultExample
*text*Bold*important*
_text_Italic_emphasis_
`code`Inline code`variable`
```code```Code block```print("hello")```
[text](url)Hyperlink[click here](https://example.com)
> textBlockquote> quoted text

Caption vs. Message

When posting with media (images or videos), the text content is sent as a caption attached to the media. Captions have a stricter limit of 1,024 characters when using the bot API. MTProto with Premium supports up to 2,048-character captions on media posts (not 4,096 -- the 4,096 limit applies only to text-only messages). Text-only messages (without media) support up to 4,096 characters for both bot API and MTProto connections.

Telegram Post Options

PostPost supports Telegram-specific post options that control message delivery and content protection. These options are available in the PostPost dashboard under "Telegram Presets" when creating or editing a post with a Telegram channel selected.

Available Options

OptionDescriptionAPI Parameter
Disable link previewPrevents Telegram from generating link previews for URLs in the messagedisableWebPagePreview
Silent notificationSends the message without triggering a notification sound on recipients' devicesdisableNotification
Caption above mediaPlaces the caption above the photo/video instead of below (only applies when media is attached)showCaptionAboveMedia
Protect contentPrevents recipients from forwarding, saving, or copying the message contentprotectContent

Using Post Options via API

To set Telegram-specific options via the API, include the platformSettings.telegram object in your request:

JavaScript (fetch)

const response = await fetch('https://api.postpost.dev/api/v1/create-post', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'x-api-key': 'YOUR_API_KEY'
  },
  body: JSON.stringify({
    content: '*Exclusive Update*\n\nThis content is protected and cannot be forwarded.',
    platforms: ['telegram-1001234567890'],
    platformSettings: {
      telegram: {
        disableNotification: true,      // Send silently
        disableWebPagePreview: true,    // No link previews
        showCaptionAboveMedia: false,   // Caption below media (default)
        protectContent: true            // Prevent forwarding/saving
      }
    }
  })
});

const data = await response.json();
console.log(data);
// Response: { "success": true, "postGroupId": "abc123..." }

Python (requests)

import requests

response = requests.post(
    'https://api.postpost.dev/api/v1/create-post',
    headers={
        'Content-Type': 'application/json',
        'x-api-key': 'YOUR_API_KEY'
    },
    json={
        'content': '*Exclusive Update*\n\nThis content is protected and cannot be forwarded.',
        'platforms': ['telegram-1001234567890'],
        'platformSettings': {
            'telegram': {
                'disableNotification': True,
                'disableWebPagePreview': True,
                'showCaptionAboveMedia': False,
                'protectContent': True
            }
        }
    }
)

data = response.json()
print(data)
# Response: { "success": true, "postGroupId": "abc123..." }

Option Details

When enabled, Telegram will not generate preview cards for URLs in your message. Useful for:

  • Keeping messages compact
  • Avoiding preview images that may not represent your content well
  • Reducing visual clutter in channels with frequent link sharing

Silent Notification (disableNotification)

When enabled, recipients will receive the message without a notification sound. The message still appears in their chat list and shows an unread badge. Useful for:

  • Posting during off-hours without disturbing subscribers
  • High-frequency update channels
  • Non-urgent announcements

Caption Above Media (showCaptionAboveMedia)

When enabled and media is attached, the caption text appears above the image/video instead of below. This option only applies when the post includes media. Useful for:

  • Emphasizing the text content over the visual
  • Creating a headline-style presentation
  • Matching specific visual preferences

Protect Content (protectContent)

When enabled, Telegram restricts what recipients can do with your message:

  • Cannot forward the message to other chats
  • Cannot save media to device
  • Cannot copy text (on mobile apps)
  • Screenshot restrictions on some platforms

Useful for:

  • Exclusive or premium content
  • Time-sensitive announcements
  • Content you want to keep within your channel

Note: Content protection is enforced by Telegram clients but is not 100% foolproof. Users with modified clients or screen capture tools may still be able to save content.

Examples

Post a Text Message

JavaScript (fetch)

const response = await fetch('https://api.postpost.dev/api/v1/create-post', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'x-api-key': 'YOUR_API_KEY'
  },
  body: JSON.stringify({
    content: '*Product Update v2.5*\n\nWe have shipped the following improvements:\n\n- _Faster API response times_ (avg 45ms)\n- New `batch` endpoint for bulk operations\n- Improved error messages with `error_code` field\n\n> This update is backward compatible. No migration needed.\n\nFull changelog: [docs.example.com/changelog](https://docs.example.com/changelog)',
    platforms: ['telegram-1001234567890']
  })
});

const data = await response.json();
console.log(data);
// Response: { "success": true, "postGroupId": "abc123..." }

Python (requests)

import requests

content = """*Product Update v2.5*

We have shipped the following improvements:

- _Faster API response times_ (avg 45ms)
- New `batch` endpoint for bulk operations
- Improved error messages with `error_code` field

> This update is backward compatible. No migration needed.

Full changelog: [docs.example.com/changelog](https://docs.example.com/changelog)"""

response = requests.post(
    'https://api.postpost.dev/api/v1/create-post',
    headers={
        'Content-Type': 'application/json',
        'x-api-key': 'YOUR_API_KEY'
    },
    json={
        'content': content,
        'platforms': ['telegram-1001234567890']
    }
)

data = response.json()
print(data)
# Response: { "success": true, "postGroupId": "abc123..." }

cURL

curl -X POST https://api.postpost.dev/api/v1/create-post \
  -H "Content-Type: application/json" \
  -H "x-api-key: YOUR_API_KEY" \
  -d '{
    "content": "*Product Update v2.5*\n\nWe have shipped the following improvements:\n\n- _Faster API response times_ (avg 45ms)\n- New `batch` endpoint for bulk operations\n- Improved error messages with `error_code` field\n\n> This update is backward compatible. No migration needed.\n\nFull changelog: [docs.example.com/changelog](https://docs.example.com/changelog)",
    "platforms": ["telegram-1001234567890"]
  }'
# Response: { "success": true, "postGroupId": "abc123..." }

Node.js (axios)

const axios = require('axios');

const content = `*Product Update v2.5*

We have shipped the following improvements:

- _Faster API response times_ (avg 45ms)
- New \`batch\` endpoint for bulk operations
- Improved error messages with \`error_code\` field

> This update is backward compatible. No migration needed.

Full changelog: [docs.example.com/changelog](https://docs.example.com/changelog)`;

const response = await axios.post('https://api.postpost.dev/api/v1/create-post', {
  content,
  platforms: ['telegram-1001234567890']
}, {
  headers: {
    'Content-Type': 'application/json',
    'x-api-key': 'YOUR_API_KEY'
  }
});

console.log(response.data);
// Response: { "success": true, "postGroupId": "abc123..." }

Post with an Image

JavaScript (fetch)

const response = await fetch('https://api.postpost.dev/api/v1/create-post', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'x-api-key': 'YOUR_API_KEY'
  },
  body: JSON.stringify({
    content: '*New Dashboard Preview*\n\nHere is a sneak peek at our redesigned analytics dashboard. Key improvements include real-time data refresh and customizable widgets.',
    platforms: ['telegram-1001234567890']
  })
});

const data = await response.json();
console.log(data);
// Response: { "success": true, "postGroupId": "abc123..." }

Python (requests)

import requests

response = requests.post(
    'https://api.postpost.dev/api/v1/create-post',
    headers={
        'Content-Type': 'application/json',
        'x-api-key': 'YOUR_API_KEY'
    },
    json={
        'content': '*New Dashboard Preview*\n\nHere is a sneak peek at our redesigned analytics dashboard. Key improvements include real-time data refresh and customizable widgets.',
        'platforms': ['telegram-1001234567890']
    }
)

data = response.json()
print(data)
# Response: { "success": true, "postGroupId": "abc123..." }

cURL

curl -X POST https://api.postpost.dev/api/v1/create-post \
  -H "Content-Type: application/json" \
  -H "x-api-key: YOUR_API_KEY" \
  -d '{
    "content": "*New Dashboard Preview*\n\nHere is a sneak peek at our redesigned analytics dashboard. Key improvements include real-time data refresh and customizable widgets.",
    "platforms": ["telegram-1001234567890"]
  }'
# Response: { "success": true, "postGroupId": "abc123..." }

Node.js (axios)

const axios = require('axios');

const response = await axios.post('https://api.postpost.dev/api/v1/create-post', {
  content: '*New Dashboard Preview*\n\nHere is a sneak peek at our redesigned analytics dashboard. Key improvements include real-time data refresh and customizable widgets.',
  platforms: ['telegram-1001234567890']
}, {
  headers: {
    'Content-Type': 'application/json',
    'x-api-key': 'YOUR_API_KEY'
  }
});

console.log(response.data);
// Response: { "success": true, "postGroupId": "abc123..." }

Note: To attach media to a Telegram post, first create the post, then upload media using the media upload workflow with the returned postGroupId.

Post with a Video

JavaScript (fetch)

const response = await fetch('https://api.postpost.dev/api/v1/create-post', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'x-api-key': 'YOUR_API_KEY'
  },
  body: JSON.stringify({
    content: '*Feature Demo*\n\nWatch our 60-second demo of the new collaboration tools.',
    platforms: ['telegram-1001234567890']
  })
});

const data = await response.json();
console.log(data);
// Response: { "success": true, "postGroupId": "abc123..." }

Python (requests)

import requests

response = requests.post(
    'https://api.postpost.dev/api/v1/create-post',
    headers={
        'Content-Type': 'application/json',
        'x-api-key': 'YOUR_API_KEY'
    },
    json={
        'content': '*Feature Demo*\n\nWatch our 60-second demo of the new collaboration tools.',
        'platforms': ['telegram-1001234567890']
    }
)

data = response.json()
print(data)
# Response: { "success": true, "postGroupId": "abc123..." }

cURL

curl -X POST https://api.postpost.dev/api/v1/create-post \
  -H "Content-Type: application/json" \
  -H "x-api-key: YOUR_API_KEY" \
  -d '{
    "content": "*Feature Demo*\n\nWatch our 60-second demo of the new collaboration tools.",
    "platforms": ["telegram-1001234567890"]
  }'
# Response: { "success": true, "postGroupId": "abc123..." }

Node.js (axios)

const axios = require('axios');

const response = await axios.post('https://api.postpost.dev/api/v1/create-post', {
  content: '*Feature Demo*\n\nWatch our 60-second demo of the new collaboration tools.',
  platforms: ['telegram-1001234567890']
}, {
  headers: {
    'Content-Type': 'application/json',
    'x-api-key': 'YOUR_API_KEY'
  }
});

console.log(response.data);
// Response: { "success": true, "postGroupId": "abc123..." }

Platform Quirks

  • Bot must be admin: The Telegram bot must be added as an administrator to the target channel or group with can_post_messages permission. The admin permission check happens at connection time, not at posting time — if the bot is not an admin with can_post_messages, the connection will be rejected during setup.
  • Caption overflow behavior: When posting with media, the text is sent as a caption (1,024-character limit via bot API). If the content exceeds 1,024 characters and media is attached, the text is sent as a separate reply message to the media post rather than being truncated or rejected. Text-only messages support up to 4,096 characters via MTProto.
  • Caption validation gap: PostPost's validation layer (postValidationService.js) does not currently validate caption length for Telegram. A 1,024-character caption limit exists at the Telegram API level, but PostPost does not pre-check this. If your caption exceeds 1,024 characters on a media post, the error will come from Telegram's API at publish time, not from PostPost's validation.
  • WebP auto-conversion: WebP images are automatically converted to JPEG before sending to Telegram.
  • Markdown formatting: Telegram supports a subset of markdown. PostPost's safeParseMarkdown uses single asterisks for bold: *bold*, _italic_, `code`, [text](url), and > blockquote. Note that this differs from standard Markdown where **double asterisks** denote bold. Not all markdown features are supported (e.g., headers are not rendered as headers).
  • Mixed media types not supported: A single post cannot contain both images and videos. Attempting to send mixed media types will result in an error. Note that PostPost's backend validation does not enforce mixed media type restrictions for Telegram (it only does so for Instagram). If Telegram rejects mixed media, the error comes from the Telegram API itself, not from PostPost's validation layer.
  • Bot connection media group limitations: Bot connections support either a single video OR multiple photos in a media group, but not multiple videos. Multi-video posts are not supported for bot connections. If you need to post multiple videos, create separate posts for each video.
  • Channel name format: When setting up the connection, provide the channel name with the @ prefix (e.g., @mychannel). The channelName field also accepts a numeric chat ID, which is required for private channels that do not have a public @username.
  • Bot token security: Your bot token is stored by PostPost in the connection record and is never exposed in API responses. Treat your bot token like a password. Bot tokens are stored in plaintext in the connection record. Encryption at rest is planned but not yet implemented. MTProto sessions, however, ARE encrypted at rest (the service user's session is stored encrypted and decrypted at runtime).
  • MTProto test-connection limitation: The test-connection endpoint always fails for MTProto connections. The validator checks connectionType === 'bot' before validating the bot token. MTProto connections have connectionType: 'mtproto', so they skip the bot validation block entirely and fall through to the default failure response with a misleading 'Invalid bot token' error. This is a known limitation; MTProto connections work correctly for posting despite failing the test-connection check.
  • Message editing: Once posted, Telegram messages can be edited, but this capability is not currently exposed through the PostPost API.

API Limits

Understanding the difference between Bot API limits and regular user limits is critical for successful Telegram integration.

Character Limits

ElementLimit
Text message4,096 characters
Bot caption1,024 characters (bots cannot get Premium 2,048 caption limit; the 4,096 limit is for text-only messages, not captions)
Bot username32 characters
Channel name128 characters

Image Limits (Bot API)

PropertyLimit
Max size10 MB
Max count10 images per media group
FormatsJPEG, PNG, GIF, WebP, BMP

Video Limits (Bot API)

PropertyLimit
DurationNo limit (only size matters)
Max size50 MB (NOT 4 GB - that's for regular users!)
Local Bot API Server2 GB max
FormatsMP4, MOV, AVI, MKV, WebM

CRITICAL: Bot API vs User Limits

Bots have significantly lower limits than regular Telegram users. This is a common source of errors:

LimitBot APIRegular Users
File size50 MB4 GB
Caption1,024 chars4,096 chars

Common Error Messages

ErrorCause
MEDIA_CAPTION_TOO_LONGCaption exceeds 1,024 characters (bots)
Bad Request: file is too bigFile exceeds 50 MB

Rate Limits

ScopeLimit
Global30 messages/second
Per group20 messages/minute

On this page