Skip to main content
Glama
DLHellMe
by DLHellMe

api_scrape_channel

Extract posts from Telegram channels using API methods. Specify URL, date ranges, and post limits to collect channel content programmatically.

Instructions

Scrape a Telegram channel using the API (fast and efficient)

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
urlYesThe Telegram channel URL (e.g., https://t.me/channelname)
max_postsNoMaximum number of posts to scrape (0 for unlimited)
date_fromNoStart date in ISO format (optional)
date_toNoEnd date in ISO format (optional)

Implementation Reference

  • Core handler function executing the 'api_scrape_channel' tool: validates connection, processes scrape options, calls TelegramApiScraper.scrape(), handles large results by saving to JSON file and returning summary with sample, or formats small results with MarkdownFormatter.
    async handleApiScrapeChannel(this: any, args: any): Promise<any> {
      if (!this._apiScraper || !this._apiScraper.isConnected()) {
        return {
          content: [{
            type: 'text',
            text: 'āŒ Not connected to Telegram API. Please use telegram_api_login first.'
          }]
        };
      }
      
      try {
        const options = {
          url: args.url || args.channel,
          maxPosts: args.max_posts !== undefined ? args.max_posts : (args.limit !== undefined ? args.limit : 0),
          dateFrom: args.date_from ? new Date(args.date_from) : undefined,
          dateTo: args.date_to ? new Date(args.date_to) : undefined
        };
        
        const result = await this._apiScraper.scrape(options);
        
        if (result.error) {
          return {
            content: [{
              type: 'text',
              text: `āŒ Scraping failed: ${result.error}`
            }]
          };
        }
        
        // Check if we have a large amount of data
        const postCount = result.posts?.length || 0;
        const isLargeData = postCount > 50;
        
        if (isLargeData) {
          // Save to file to avoid Claude's limits
          const fs = await import('fs/promises');
          const path = await import('path');
          
          const timestampParts = new Date().toISOString().split('.');
          const timestamp = (timestampParts[0] || '').replace(/:/g, '-');
          const urlParts = (args.url || args.channel || '').split('/');
          const channelName = urlParts.pop() || 'unknown';
          const filename = `${channelName}_${timestamp}_api.json`;
          const filepath = path.join('scraped_data', filename);
          
          await fs.mkdir('scraped_data', { recursive: true });
          await fs.writeFile(filepath, JSON.stringify(result, null, 2));
          
          // Return summary instead of full data
          const summary = {
            channel: result.channel_name,
            total_posts: postCount,
            date_range: postCount > 0 ? {
              first: result.posts[postCount - 1].date,
              last: result.posts[0].date
            } : null,
            saved_to_file: filename,
            sample_posts: result.posts?.slice(0, 5) || []
          };
          
          return {
            content: [{
              type: 'text',
              text: `šŸ“Š Channel Summary (${postCount} posts - Full data saved to file)\n\n` +
                `Channel: ${summary.channel}\n` +
                `Total posts: ${summary.total_posts}\n` +
                `Date range: ${summary.date_range ? `${new Date(summary.date_range.first).toLocaleDateString()} - ${new Date(summary.date_range.last).toLocaleDateString()}` : 'N/A'}\n` +
                `\nšŸ’¾ Full data saved to: scraped_data/${filename}\n\n` +
                `Sample of first 5 posts:\n` +
                summary.sample_posts.map((p: any) => `\nšŸ“… ${new Date(p.date).toLocaleDateString()}\n${p.content?.substring(0, 100)}...\nšŸ‘ ${p.views || 0} views`).join('\n---\n') +
                `\n\nāœ… To analyze all posts, use the saved JSON file.`
            }]
          };
        } else {
          // Small data - return as usual
          const formatter = new (await import('./formatters/markdown-formatter.js')).MarkdownFormatter();
          const markdown = formatter.format(result);
          
          return {
            content: [{
              type: 'text',
              text: `${markdown}\n\nāœ… Scraped using Telegram API - Fast and reliable!`
            }]
          };
        }
      } catch (error) {
        return {
          content: [{
            type: 'text',
            text: `āŒ API scraping failed: ${error instanceof Error ? error.message : 'Unknown error'}`
          }]
        };
      }
    },
  • Input schema definition for the 'api_scrape_channel' tool, defining parameters like url (required), max_posts, date_from, date_to.
    {
      name: 'api_scrape_channel',
      description: 'Scrape a Telegram channel using the API (fast and efficient)',
      inputSchema: {
        type: 'object',
        properties: {
          url: {
            type: 'string',
            description: 'The Telegram channel URL (e.g., https://t.me/channelname)'
          },
          max_posts: {
            type: 'number',
            description: 'Maximum number of posts to scrape (0 for unlimited)',
            default: 0
          },
          date_from: {
            type: 'string',
            description: 'Start date in ISO format (optional)'
          },
          date_to: {
            type: 'string',
            description: 'End date in ISO format (optional)'
          }
        },
        required: ['url']
      }
    },
  • src/server.ts:98-99 (registration)
    Registration in the tool dispatch switch statement: routes 'api_scrape_channel' calls to handleApiScrapeChannel.
    case 'api_scrape_channel':
      return await this.handleApiScrapeChannel(args);
  • src/server.ts:763-764 (registration)
    Binding the handler from apiHandlers to the server class instance for use in tool dispatch.
    private handleApiLogin = apiHandlers.handleApiLogin.bind(this);
    private handleApiScrapeChannel = apiHandlers.handleApiScrapeChannel.bind(this);
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries full burden. It mentions 'fast and efficient' as a behavioral trait, but doesn't disclose critical information like authentication requirements, rate limits, data format returned, error handling, or whether this is a read-only operation. For a scraping tool with no annotation coverage, this leaves significant behavioral gaps.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is extremely concise - a single sentence that efficiently communicates the core functionality. Every word earns its place: 'scrape' (action), 'Telegram channel' (resource), 'using the API' (method), and 'fast and efficient' (performance characteristic).

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a scraping tool with no annotations and no output schema, the description is insufficient. It doesn't explain what data is returned, authentication requirements, rate limits, or how it differs from sibling scraping tools. Given the complexity of web scraping operations and the lack of structured metadata, the description should provide more complete context.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so all parameters are documented in the schema. The description doesn't add any parameter-specific information beyond what's already in the schema. With complete schema coverage, the baseline score of 3 is appropriate as the description doesn't compensate but also doesn't need to.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the action ('scrape') and resource ('Telegram channel'), and specifies the method ('using the API') with performance characteristics ('fast and efficient'). However, it doesn't explicitly differentiate from sibling tools like 'scrape_channel', 'scrape_channel_authenticated', or 'scrape_channel_full', which appear to offer similar functionality.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus alternatives. With multiple sibling tools for scraping channels (e.g., 'scrape_channel', 'scrape_channel_authenticated', 'scrape_channel_full'), there's no indication of what makes this API-based approach preferable or when other methods might be more appropriate.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/DLHellMe/telegram-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server