Telegram quote image generator

Quote API reimagined.

Turn Telegram-style messages into polished quote images through a small, self-hostable API. Build the message, identity, avatar, formatting, media and output you need.

API surface

Endpoints

Choose JSON/base64 output or request the image bytes directly.

Build a request

Playground

Fill in the common fields below. The request JSON is generated for you, so you can use it as a starting point for your bot or app.

Request/quote/generate
PreviewWAITING
No image yetGenerate a request to see the result here.
Ready when you are.
Reference

Documentation

Everything supported by the current generator, including request fields, message objects, formatting, media, output modes and examples.

Quick start

Send JSON to POST /quote/generate. The deployed route is exposed under /quote; the original generator method is /generate.

curl -X POST https://YOUR-DOMAIN/quote/generate \
  -H "Content-Type: application/json" \
  -d '{
    "messages": [{
      "from": { "id": 1, "name": "Test User" },
      "text": "Hello world!",
      "avatar": true
    }]
  }'
For the public playground, no bot token is required unless your deployment is configured to require one. Authenticated calls can pass botToken; the server also accepts BOT_TOKEN from the environment.

Request parameters

Content-Type must be application/json. Only messages is required.

FieldTypeRequiredDescription
botTokenstringNoTelegram bot token. Falls back to BOT_TOKEN.
typestringNoquote, image, or stories. Stories uses 720×1280 output.
formatstringNopng or webp for quote output.
extstringNopng/webp; selects direct image response handling.
backgroundColorstringNoHEX, CSS color name, random, gradient using #111/#222, or // for a transparent variant.
widthnumberNoLayout width before scaling.
heightnumberNoLayout height before scaling.
scalenumberNoScaling factor from 1 to 20. Default is 2.
emojiBrandstringNoEmoji rendering set, such as apple, google, twitter, joypixels, or blob.
messagesarrayYesOne or more message objects.

Message object

Messages can represent one person or a full conversation.

FieldTypeDescription
fromobjectSender: id, first_name, last_name, name, username, and optional photo.url/photo.big_file_id.
textstringMessage text, up to 4096 characters.
entitiesarrayTelegram-style formatting entities.
avatarbooleanWhether the avatar is shown.
replyMessageobjectQuoted/replied message shown above the message.
mediaobject|arrayImage/sticker/media source. URL or Telegram file ID.
mediaTypestringsticker for stickers; otherwise text/image.
mediaCropbooleanCrop media to preserve the requested proportions.
voiceobjectVoice message waveform, e.g. { waveform: [0,4,8] }.

Text entities

Use Telegram entity objects with type, offset, and length. Some entity types also accept extra data.

bolditalicunderlinestrikethroughcodetext_linkhashtagcustom_emoji
"entities": [
  { "type": "bold", "offset": 0, "length": 5 },
  { "type": "italic", "offset": 6, "length": 5 },
  { "type": "text_link", "offset": 12, "length": 4, "url": "https://example.com" },
  { "type": "custom_emoji", "offset": 17, "length": 2, "custom_emoji_id": "..." }
]

Reply messages

Set replyMessage to display a short quoted message above the current message. It can include name, text, entities, chatId, and optional from sender information.

Media

Media accepts a URL or Telegram file ID. An array can contain multiple files; the generator uses the last file, or the second file when mediaCrop is enabled.

"media": { "url": "https://example.com/image.jpg" }

"media": { "file_id": "AgACAg...", "width": 800, "height": 600 }

"media": [
  { "file_id": "AgACAg...1" },
  { "file_id": "AgACAg...2" }
]

Voice messages

Provide waveform samples as numbers. The renderer turns them into a visual waveform.

"voice": {
  "waveform": [0, 4, 8, 16, 12, 8, 4, 8, 16, 12, 8, 4, 0]
}

Output formats

There are three practical endpoint forms:

  • /quote/generate — JSON containing the generated image as base64.
  • /quote/generate.png — direct PNG response.
  • /quote/generate.webp — direct WebP response.

The generated result also reports its actual width, height, type and extension.

Backgrounds & layout

backgroundColor accepts HEX values and CSS color names. Use random for a random background, or two colors separated by a slash for a gradient such as #ff69b4/#6cace4. A value beginning with // creates a semi-transparent variant. Set width, height, and scale to control the render size.

Emoji brands

The bundled emoji sets currently include:

applegoogletwitterjoypixelsblob

Features at a glance

Multiple messagesCompose conversations in one image.
Custom identityName, username, ID and avatar source.
Text formattingTelegram-style entities and links.
RepliesShow quoted context above a message.
MediaURL, file ID, multiple files and cropping.
VoiceRender waveform data as a voice message.
Stories720×1280 story layout.
PNG / WebPBase64 JSON or direct image output.
Emoji setsApple, Google, Twitter, JoyPixels and Blob.

Errors & limits

Common validation errors include query_empty, messages_empty, and empty_messages. Unknown methods return method not found. The API rate limit is 20 requests per IP per 55-second window; calls matching the configured BOT_TOKEN are whitelisted.

{
  "ok": false,
  "error": {
    "code": 429,
    "message": "Rate limit exceeded. See "Retry-After""
  }
}

Examples

JavaScript

const response = await fetch('https://YOUR-DOMAIN/quote/generate', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    backgroundColor: '#f68ac9',
    scale: 2,
    messages: [{
      from: {
        id: 1,
        name: 'Test User',
        photo: { url: 'https://YOUR-DOMAIN/default-avatar.svg' }
      },
      avatar: true,
      text: 'Hello world!'
    }]
  })
})
const data = await response.json()
const image = Buffer.from(data.result.image, 'base64')

Python

import base64, requests

payload = {
  'messages': [{
    'from': {'id': 1, 'name': 'Test User'},
    'text': 'Hello world!',
    'avatar': True
  }]
}
r = requests.post('https://YOUR-DOMAIN/quote/generate', json=payload)
data = r.json()
image = base64.b64decode(data['result']['image'])
open('quote.png', 'wb').write(image)