{"id":50474402,"url":"https://github.com/shnwazdeveloper/shnwazdev-quoteapi","last_synced_at":"2026-06-01T12:02:58.386Z","repository":{"id":356740629,"uuid":"1233870491","full_name":"shnwazdeveloper/shnwazdev-quoteapi","owner":"shnwazdeveloper","description":"Vercel-ready quote image generation API","archived":false,"fork":false,"pushed_at":"2026-05-09T14:00:13.000Z","size":101734,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-09T15:31:03.158Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/shnwazdeveloper.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-09T13:11:28.000Z","updated_at":"2026-05-09T14:00:16.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/shnwazdeveloper/shnwazdev-quoteapi","commit_stats":null,"previous_names":["shnwazdeveloper/quote-api-vercel"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/shnwazdeveloper/shnwazdev-quoteapi","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shnwazdeveloper%2Fshnwazdev-quoteapi","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shnwazdeveloper%2Fshnwazdev-quoteapi/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shnwazdeveloper%2Fshnwazdev-quoteapi/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shnwazdeveloper%2Fshnwazdev-quoteapi/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/shnwazdeveloper","download_url":"https://codeload.github.com/shnwazdeveloper/shnwazdev-quoteapi/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shnwazdeveloper%2Fshnwazdev-quoteapi/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33773782,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-01T02:00:06.963Z","response_time":115,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-01T12:02:55.737Z","updated_at":"2026-06-01T12:02:58.353Z","avatar_url":"https://github.com/shnwazdeveloper.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# shnwazdev-quoteapi\n\nA Vercel-hosted API for generating Telegram-style quote images from JSON message data. It can return base64 image JSON from `/generate`, direct image files from `/generate.png` or `/generate.webp`, and legacy-compatible responses from `/quote/generate`.\n\n## Table of Contents\n- [quote-api](#quote-api)\n  - [Table of Contents](#table-of-contents)\n  - [Installation](#installation)\n  - [Running](#running)\n  - [API](#api)\n    - [POST /generate](#post-generate)\n      - [Request Parameters](#request-parameters)\n      - [Message Object](#message-object)\n      - [Request Example](#request-example)\n      - [Response](#response)\n      - [Additional Examples](#additional-examples)\n        - [Text Entities Example](#text-entities-example)\n        - [Media Examples](#media-examples)\n        - [Voice Message Example](#voice-message-example)\n      - [Error Handling](#error-handling)\n    - [File Extension in URL](#file-extension-in-url)\n  - [Deployed Instance](#deployed-instance)\n  - [Usage Examples](#usage-examples)\n    - [JavaScript](#javascript)\n    - [Python](#python)\n\n---\n\n## Installation\n\n```bash\ngit clone https://github.com/shnwazdeveloper/shnwazdev-quoteapi.git\ncd shnwazdev-quoteapi\nnpm install\n```\n\n## Running\n\n```bash\n# Using environment variables\nexport BOT_TOKEN=your_telegram_bot_token\nexport QUOTE_API_URI_HTML=https://shnwazdev-quoteapi.vercel.app\nnpm start\n\n# Or directly\nnode index.js\n```\n\n---\n\n## API\n\n### POST /generate\n\nGenerates one or more \"quote\" images.\n\n#### Request Parameters\n\nContent-Type: `application/json`\n\n| Field               | Type     | Required | Description                                                                                                    |\n| ------------------- | -------- | -------- | -------------------------------------------------------------------------------------------------------------- |\n| `botToken`          | string   | No       | Telegram bot token (if not provided, uses `process.env.BOT_TOKEN`).                                             |\n| `type`              | string   | No       | Final content format:                                                                                           |\n|                     |          |          | - `quote` (framed quote)                                                                                        |\n|                     |          |          | - `image` (background + eternal image)                                                                          |\n|                     |          |          | - `stories` (720×1280 for stories)                                                                              |\n|                     |          |          | - otherwise — `null` (raw data).                                                                                |\n| `format`            | string   | No       | File format: `png` or `webp` (for `type === 'quote'`).                                                         |\n| `ext`               | string   | No       | Extension: `png`/`webp`. If specified, response `image` will be a `Buffer`, otherwise — `base64` string.        |\n| `backgroundColor`   | string   | No       | Background color (HEX, CSS-name or `random`).                                                                   |\n|                     |          |          | - If string contains slash, e.g., `\"#111/#222\"`, it creates a gradient.                                         |\n|                     |          |          | - If starts with `//`, creates a semi-transparent variant.                                                      |\n| `width`             | number   | No       | Layout width in px (before scaling).                                                                            |\n| `height`            | number   | No       | Layout height in px (before scaling).                                                                           |\n| `scale`             | number   | No       | Scaling factor (1–20). Default is `2`.                                                                          |\n| `emojiBrand`        | string   | No       | Emoji brand: `apple` (default), `google`, `twitter`, etc.                                                       |\n| `messages`          | array    | Yes      | List of messages (see [Message Object](#message-object)).                                                      |\n\n#### Message Object\n\n| Field                | Type           | Required | Description                                                                                              |\n| -------------------- | -------------- | -------- | -------------------------------------------------------------------------------------------------------- |\n| `from`               | object         | Yes      | Sender information:                                                                                     |\n|                      |                |          | - `id` (number): User ID                                                                                 |\n|                      |                |          | - `first_name`, `last_name`: User's name components                                                      |\n|                      |                |          | - `name`: Alternative to first_name/last_name                                                            |\n|                      |                |          | - `username`: User's username                                                                            |\n|                      |                |          | - `photo.url` or `photo.big_file_id`: Avatar image                                                       |\n|                      |                |          | - `emoji_status`: Custom emoji status ID (optional)                                                      |\n| `text`               | string         | No       | Message text (up to 4096 characters).                                                                    |\n| `entities`           | array          | No       | Telegram text styles: `bold`, `italic`, `underline`, `strikethrough`, `code`, links, hashtags, etc.      |\n|                      |                |          | Each entity is an object with: `type`, `offset`, `length`, and sometimes `custom_emoji_id`               |\n| `avatar`             | boolean        | No       | Whether to show avatar (true/false).                                                                     |\n| `replyMessage`       | object         | No       | If specified, shows a short quote above:                                                                 |\n|                      |                |          | - `name`: Name of the user being replied to                                                              |\n|                      |                |          | - `text`: Text of the replied message                                                                    |\n|                      |                |          | - `entities`: Text styles in the reply                                                                   |\n|                      |                |          | - `chatId`: ID of the chat where the original message was sent (defaults to sender ID if missing)         |\n|                      |                |          | - `from`: Optional user information about the reply author                                               |\n| `media`              | object\\|array  | No       | If array is passed, uses the last file (or second if `mediaCrop=true`).                                 |\n|                      |                |          | If object: `{ url }` or `{ file_id, width, height, is_animated }`                                        |\n| `mediaType`          | string         | No       | `sticker` for stickers, otherwise text/image.                                                            |\n| `mediaCrop`          | boolean        | No       | Whether to crop media to maintain proportions.                                                           |\n| `voice`              | object         | No       | Voice message: `{ waveform: [...number] }`. Displayed as a waveform.                                     |\n\n#### Request Example\n\n```http\nPOST /generate\nContent-Type: application/json\n\n{\n  \"backgroundColor\": \"#1b1429\",\n  \"width\": 512,\n  \"height\": 768,\n  \"scale\": 2,\n  \"emojiBrand\": \"apple\",\n  \"messages\": [\n    {\n      \"from\": {\n        \"id\": 66478514,\n        \"first_name\": \"Yuri 💜\",\n        \"last_name\": \"Ly\",\n        \"username\": \"LyoSU\",\n        \"photo\": { \"big_file_id\": \"AQAD...\" }\n      },\n      \"text\": \"Welcome to the quote generator!\",\n      \"entities\": [\n        {\n          \"type\": \"bold\",\n          \"offset\": 0,\n          \"length\": 7\n        },\n        {\n          \"type\": \"italic\",\n          \"offset\": 8,\n          \"length\": 3\n        }\n      ],\n      \"avatar\": true,\n      \"replyMessage\": {\n        \"name\": \"Charlie\",\n        \"text\": \"How's the weather today?\",\n        \"entities\": [],\n        \"chatId\": 123456789,\n        \"from\": {\n          \"id\": 123456789,\n          \"name\": \"Charlie\",\n          \"photo\": { \"url\": \"https://example.com/avatar.jpg\" }\n        }\n      }\n    }\n  ]\n}\n```\n\n#### Response\n\n```json\n{\n  \"image\": \"\u003cbase64 string or Buffer\u003e\",\n  \"type\": \"quote\",\n  \"width\": 512,\n  \"height\": 359,\n  \"ext\": \"png\"\n}\n```\n\n#### Additional Examples\n\n##### Text Entities Example\n\n```json\n\"entities\": [\n  {\n    \"type\": \"bold\",\n    \"offset\": 0,\n    \"length\": 5\n  },\n  {\n    \"type\": \"italic\",\n    \"offset\": 6,\n    \"length\": 6\n  },\n  {\n    \"type\": \"code\",\n    \"offset\": 13,\n    \"length\": 10\n  },\n  {\n    \"type\": \"text_link\",\n    \"offset\": 24,\n    \"length\": 4,\n    \"url\": \"https://example.com\"\n  },\n  {\n    \"type\": \"custom_emoji\",\n    \"offset\": 29,\n    \"length\": 2,\n    \"custom_emoji_id\": \"5368324170671202286\"\n  }\n]\n```\n\n##### Media Examples\n\nUsing URL:\n```json\n\"media\": {\n  \"url\": \"https://example.com/image.jpg\"\n}\n```\n\nUsing file_id:\n```json\n\"media\": {\n  \"file_id\": \"AgACAgIAAxkBAAIQ7WR...\",\n  \"width\": 800,\n  \"height\": 600\n}\n```\n\nUsing multiple files (will use last or second if mediaCrop=true):\n```json\n\"media\": [\n  {\"file_id\": \"AgACAgIAAxkBAAIQ7WR...\"},\n  {\"file_id\": \"AgACAgIAAxkBAAIQ7WS...\"}\n]\n```\n\n##### Voice Message Example\n\n```json\n\"voice\": {\n  \"waveform\": [0, 4, 8, 16, 12, 8, 4, 8, 16, 12, 8, 4, 0]\n}\n```\n\n#### Error Handling\n\nPossible error responses:\n\n```json\n{\"error\": \"query_empty\"} // No parameters provided\n{\"error\": \"messages_empty\"} // No messages in the request\n{\"error\": \"empty_messages\"} // No valid messages could be processed\n```\n\n### File Extension in URL\n\nYou can request images directly with the desired file format by appending `.png` or `.webp` to the endpoint URL:\n\n```http\nPOST /generate.png\nContent-Type: application/json\n\n{\n  \"messages\": [...]\n}\n```\n\nThis is equivalent to setting `ext: \"png\"` in the request body. The response will contain a binary image instead of a base64 string.\n\nSimilarly, you can use:\n```http\nPOST /generate.webp\n```\n\nThis is especially useful for integrations that require direct file responses rather than processing base64 content.\n\n---\n\n## Deployed Instance\n\nThere is a deployed instance of this API available at:\n```\nhttps://shnwazdev-quoteapi.vercel.app/generate\n```\n\nYou can also use format-specific endpoints:\n```\nhttps://shnwazdev-quoteapi.vercel.app/generate.png\nhttps://shnwazdev-quoteapi.vercel.app/generate.webp\n```\n\nThe legacy route also works:\n```\nhttps://shnwazdev-quoteapi.vercel.app/quote/generate\n```\n\n---\n\n## Usage Examples\n\n### JavaScript\n\n```js\nconst axios = require('axios')\nconst fs = require('fs')\n\n// Minimal example with only required fields\nconst simpleExample = async () =\u003e {\n  try {\n    // Minimal required payload\n    const payload = {\n      messages: [{\n        from: {\n          id: 1,\n          name: \"User\"\n        },\n        text: \"Hello world!\"\n      }]\n    }\n\n    const response = await axios.post('https://shnwazdev-quoteapi.vercel.app/generate', payload)\n    if (response.data.error) {\n      console.error('Error:', response.data.error)\n      return\n    }\n\n    const buffer = Buffer.from(response.data.image, 'base64')\n    fs.writeFileSync('simple-quote.png', buffer)\n    console.log(\"Saved simple-quote.png\")\n  } catch (error) {\n    console.error('Request failed:', error.message)\n  }\n}\n\n// Complete example with all options\nconst completeExample = async () =\u003e {\n  try {\n    const payload = {\n      backgroundColor: \"#FFFFFF\",\n      width: 512,\n      height: 768,\n      scale: 2,\n      emojiBrand: \"apple\",\n      messages: [\n        {\n          from: {\n            id: 1,\n            name: \"Alice\",\n            photo: { url: \"https://dummyimage.com/100x100\" }\n          },\n          text: \"Hello World\",\n          avatar: true,\n          entities: [\n            {\n              type: \"bold\",\n              offset: 0,\n              length: 5\n            }\n          ],\n          replyMessage: {\n            name: \"Bob\",\n            text: \"How's the weather today?\",\n            entities: [],\n            chatId: 987654321\n          }\n        }\n      ]\n    }\n\n    // Option 1: Using the regular endpoint (returns base64)\n    const response = await axios.post('https://shnwazdev-quoteapi.vercel.app/generate', payload)\n    const buffer = Buffer.from(response.data.image, 'base64')\n    fs.writeFileSync('quote.png', buffer)\n    console.log(\"Saved quote.png\")\n\n    // Option 2: Using the PNG endpoint directly (returns binary)\n    const binaryResponse = await axios.post(\n      'https://shnwazdev-quoteapi.vercel.app/generate.png',\n      payload,\n      { responseType: 'arraybuffer' }\n    )\n    fs.writeFileSync('quote-direct.png', Buffer.from(binaryResponse.data))\n    console.log(\"Saved quote-direct.png\")\n  } catch (error) {\n    console.error('Request failed:', error.message)\n  }\n}\n\n// Run examples\nsimpleExample()\ncompleteExample()\n```\n\n### Python\n\n```py\nimport requests\nimport base64\nimport os\n\n# Minimal example with only required fields\ndef simple_example():\n    # Minimal required payload\n    payload = {\n        \"messages\": [{\n            \"from\": {\n                \"id\": 1,\n                \"name\": \"User\"\n            },\n            \"text\": \"Hello world!\"\n        }]\n    }\n\n    try:\n        r = requests.post('https://shnwazdev-quoteapi.vercel.app/generate', json=payload)\n        data = r.json()\n        if 'error' in data:\n            print(f\"Error: {data['error']}\")\n            return\n\n        img = base64.b64decode(data['image'])\n        with open('simple-quote.png', 'wb') as f:\n            f.write(img)\n        print(\"Saved simple-quote.png\")\n    except Exception as e:\n        print(f\"Request failed: {str(e)}\")\n\n# Complete example with all options\ndef complete_example():\n    payload = {\n        \"backgroundColor\": \"#FFFFFF\",\n        \"width\": 512,\n        \"height\": 768,\n        \"scale\": 2,\n        \"emojiBrand\": \"apple\",\n        \"messages\": [\n            {\n                \"from\": {\n                    \"id\": 1,\n                    \"name\": \"Bob\",\n                    \"photo\": { \"url\": \"https://dummyimage.com/100x100\" }\n                },\n                \"text\": \"Hello!\",\n                \"avatar\": True,\n                \"entities\": [\n                    {\n                        \"type\": \"bold\",\n                        \"offset\": 0,\n                        \"length\": 5\n                    }\n                ],\n                \"replyMessage\": {\n                    \"name\": \"Alice\",\n                    \"text\": \"Hi there!\",\n                    \"entities\": [],\n                    \"chatId\": 123456789\n                }\n            }\n        ]\n    }\n\n    try:\n        # Option 1: Using the regular endpoint (returns base64)\n        r = requests.post('https://shnwazdev-quoteapi.vercel.app/generate', json=payload)\n        data = r.json()\n        img = base64.b64decode(data['image'])\n        with open('quote.png', 'wb') as f:\n            f.write(img)\n        print(\"Saved quote.png\")\n\n        # Option 2: Using the PNG endpoint directly (returns binary)\n        r = requests.post('https://shnwazdev-quoteapi.vercel.app/generate.png', json=payload)\n        with open('quote-direct.png', 'wb') as f:\n            f.write(r.content)\n        print(\"Saved quote-direct.png\")\n    except Exception as e:\n        print(f\"Request failed: {str(e)}\")\n\n# Run examples\nsimple_example()\ncomplete_example()\n```\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fshnwazdeveloper%2Fshnwazdev-quoteapi","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fshnwazdeveloper%2Fshnwazdev-quoteapi","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fshnwazdeveloper%2Fshnwazdev-quoteapi/lists"}