# Telegram Keyword Search Scraper (`khadinakbar/telegram-keyword-search-scraper`) Actor

Search public Telegram channels, groups, and messages by keyword. Use for community discovery and brand monitoring. Do not use for known-channel history — use telegram-channel-scraper. Returns type, title, username, t.me URL, snippet, and rank. $0.003 per saved result.

- **URL**: https://apify.com/khadinakbar/telegram-keyword-search-scraper.md
- **Developed by:** [Khadin Akbar](https://apify.com/khadinakbar) (community)
- **Categories:** Social media, Lead generation, MCP servers
- **Stats:** 8 total users, 4 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.00 / 1,000 telegram result founds

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Telegram Keyword Search Scraper

Search **public Telegram channels, groups, and messages by keyword** and get one dataset row per match: type, title, username, t.me URL, snippet, and rank. No Telegram login, cookies, or Bot API key. Built for community discovery, brand monitoring, OSINT, and AI agents that start with a topic instead of a known `@username`.

This Actor reads public Telegram search pages. It does not open private chats, invite-only groups, or login-gated member lists. For a channel you already know, continue with [Telegram Channel Scraper](https://apify.com/khadinakbar/telegram-channel-scraper) to paginate that channel's public message history.

### Best fit for this Actor

- You have a **keyword** such as `bitcoin` or `remote python jobs` and want public Telegram entities that match it.
- You need a **flat row** per channel, group, or public post, with a canonical `https://t.me/...` URL you can open or pass downstream.
- Start here for discovery. Then use [Telegram Channel Scraper](https://apify.com/khadinakbar/telegram-channel-scraper) when you already have usernames and want message history, views, and reactions.

### Brand-monitoring workflow: from a keyword to a public Telegram list

A crypto desk tracking mentions of `bitcoin` passes that keyword with `maxResultsPerQuery` set to 20 and `includeTypes` left on channel, group, and message. The dataset returns public titles, usernames, t.me links, and snippets. They export CSV, keep the `@username` list, and schedule the same query weekly, comparing new URLs against last week's export. When they need posts from one kept channel, they pass that username into Telegram Channel Scraper.

### Quick start input

```json
{
  "queries": ["bitcoin"],
  "maxResultsPerQuery": 5,
  "includeTypes": ["channel", "group", "message"]
}
```

`maxResultsPerQuery` is a hard cap per keyword after type filtering and URL dedupe, so an agent can bound spend before calling.

### Input reference

| Field | Type | What it controls |
|---|---|---|
| `queries` | array (required) | Keywords to search. Example: `bitcoin`. Up to 50 unique queries. Not a t.me URL. |
| `maxResultsPerQuery` | integer | Billed rows saved per keyword. Default 20, prefill 5, max 100. |
| `includeTypes` | array | `channel`, `group`, and/or `message`. Default all three. Bots and invite links are skipped. |
| `language` | string | Optional ISO 639-1 hint such as `en` or `ru`. Empty = mixed-language public results. |
| `proxyConfiguration` | object | Optional Apify proxy. Direct HTTP is the default; enable Residential on 403/429. |

### What data you receive

Each dataset item is one public Telegram match, or one CLEAR/failed row for a query with no billed matches.

| Field | Description |
|---|---|
| `query` | Keyword that produced the row |
| `type` | `channel`, `group`, or `message` |
| `title`, `username`, `url`, `snippet` | Public identity and excerpt |
| `messageId` | Numeric id on `t.me/{username}/{id}` message URLs |
| `rank`, `score` | Order inside this query |
| `source`, `provider`, `scrapedAt` | Provenance |
| `matchFound`, `outcome` | `FOUND` for billed matches; `VALID_EMPTY` / `UPSTREAM_FAILED` for clear rows |

```json
{
  "query": "bitcoin",
  "type": "channel",
  "title": "Example Bitcoin News",
  "username": "example_btc_news",
  "url": "https://t.me/example_btc_news",
  "snippet": "Public channel covering bitcoin market news.",
  "memberCount": null,
  "messageDate": null,
  "messageId": null,
  "score": 100,
  "rank": 1,
  "source": "lyzem-search",
  "provider": "lyzem",
  "scrapedAt": "2026-08-18T08:00:00.000Z",
  "matchFound": true,
  "outcome": "FOUND"
}
```

You can download the dataset as JSON, CSV, Excel, or HTML from the run's Output tab.

### Public-data contract

- **Public search only.** Results are entities a public Telegram search index currently lists. Private chats, hidden members, and login-gated data are out of scope.
- **Honest empty queries.** A real keyword with no public hits writes a `VALID_EMPTY` CLEAR row and is not billed as `result-found`.
- **Blocked search fails honestly.** If the public index is blocked and Google `site:t.me` fallback also returns nothing useful, the run ends `UPSTREAM_FAILED`.

### Why agents choose this Actor

- Bounded cost: `$0.003` per saved match plus a `$0.00005` start event, with `maxResultsPerQuery` as the ceiling.
- One schema for channels, groups, and public posts.
- CLEAR rows keep a multi-keyword batch complete when some terms have no public hits.
- Provenance is explicit: `source`, `provider`, `url`, and ISO `scrapedAt`.
- Terminal `OUTPUT.outcome` values (`COMPLETE`, `PARTIAL`, `VALID_EMPTY`, `INVALID_INPUT`, `UPSTREAM_FAILED`) are stable enough to branch on.
- Channel archives stay on [Telegram Channel Scraper](https://apify.com/khadinakbar/telegram-channel-scraper), so this tool does not collide with known-username history jobs.

### Agent checklist

1. Pass keywords, not t.me URLs. Known channel usernames belong on Telegram Channel Scraper.
2. Set `maxResultsPerQuery` to the budget you can spend at `$0.003` per match.
3. After the run, read `OUTPUT.outcome`, then the dataset. Preserve `url` as the source link.
4. Treat `VALID_EMPTY` as a truthful no-match, not a crash.
5. If `includeTypes` is `message`, expect post URLs shaped like `https://t.me/{username}/{id}`.

### Use through the API

```bash
curl "https://api.apify.com/v2/acts/khadinakbar~telegram-keyword-search-scraper/runs?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "queries": ["bitcoin"],
    "maxResultsPerQuery": 5,
    "includeTypes": ["channel", "group", "message"]
  }'
```

When the Actor completes, read dataset items from the default dataset and the `OUTPUT` record from the default key-value store.

### Use with AI agents through Apify MCP

> Search public Telegram for the keyword "bitcoin". Return type, title, username, url, and snippet. Cap at 5 results. Then inspect OUTPUT.outcome and keep the t.me URLs as provenance.

Connect through <https://mcp.apify.com>. After the tool call, read the dataset and the `OUTPUT` / `RUN_SUMMARY` records. Preserve source URLs.

### Pricing

This Actor uses Pay per event plus Apify platform usage. Open the live Pricing tab for current event details, and use Apify's run cost controls to keep the workflow aligned with your budget.

Worked examples at the launch prices (`apify-actor-start` $0.00005, `result-found` $0.003):

| Input | Rows billed | Event cost |
|---|---|---|
| One keyword, 5 matches | 5 | ~$0.015 |
| One keyword, no public hits (CLEAR) | 0 | start event only |
| 10 keywords, 2 matches each | 20 | ~$0.060 |
| Empty / invalid input | 0 | start event only |

Formula: `result-found count × $0.003 + start event + platform usage`. Platform usage is billed to the user on top of events.

### Outcome vocabulary

These values live in the key-value store records `OUTPUT` and `RUN_SUMMARY`.

| Outcome | Meaning |
|---|---|
| `COMPLETE` | Queries were processed and billed matches were saved. |
| `PARTIAL` | Some queries were incomplete or the event limit was hit; saved rows are still in the dataset. |
| `VALID_EMPTY` | Queries succeeded and produced no billed matches. |
| `INVALID_INPUT` | No keywords to search. Fix the input; no `result-found` events are charged. |
| `UPSTREAM_FAILED` | Public Telegram search did not return usable data for the requested work. |

### Connect the workflow

- After you collect public `@usernames`, scrape that channel's public posts with [Telegram Channel Scraper](https://apify.com/khadinakbar/telegram-channel-scraper).
- To watch how a brand is discussed on X/Twitter instead of Telegram, continue with [X Tweet Scraper](https://apify.com/khadinakbar/x-tweet-scraper).

### Best results

- Use specific keywords (`solana airdrop`) rather than one generic word when you want a tighter community list.
- Keep `maxResultsPerQuery` at 5-20 for first passes. Famous terms have thousands of public hits.
- Filter with `includeTypes: ["channel"]` when you only need broadcast channels for monitoring.
- Re-run on a schedule and diff `url` values; this Actor is a search snapshot, not a live Telegram firehose.

### Builder's note

I built this after fetching live public Telegram search HTML rather than assuming a JSON API. I found Lyzem returns static `.search-result` cards with `channel` / `group` / `post` type sprites and canonical `t.me` links, while TGStat's search page is a form that needs extra client-side work. In my testing, paging with `per-page=50` and stopping at `maxResultsPerQuery` kept the quality-test prefill inside five minutes. My goal was discovery for agents that only have a keyword, which is why empty queries write a CLEAR row instead of a hollow success.

### FAQ

**Do I need a Telegram account?**
No. The Actor searches public web indexes. You never supply a phone number, session, or Bot API token.

**Can it scrape a private group?**
No. Private chats and invite-only groups are outside the public-data contract.

**What if a keyword has no public hits?**
The run succeeds as `VALID_EMPTY` and writes an unbilled CLEAR row for that query.

**Can an AI agent use this?**
Yes. Pass keywords, cap `maxResultsPerQuery`, then inspect `OUTPUT.outcome` and the dataset URLs.

### Responsible use

This Actor returns public Telegram search listings for research, monitoring, and enrichment. Use it in line with Telegram terms, the search-index site terms, and applicable laws. Do not use it to access private data, bypass authentication, or harass people.

# Actor input Schema

## `queries` (type: `array`):

Public Telegram search keywords to run in one batch. Example: bitcoin. Up to 50 unique queries per run. This is not a t.me channel URL — for known-channel history use telegram-channel-scraper.

## `maxResultsPerQuery` (type: `integer`):

Hard cap of billed rows saved for each keyword after type filtering and URL dedupe. Defaults to 20. Prefill 5 keeps the quality test under five minutes. Maximum 100. Raise this for broader discovery; each saved match costs $0.003.

## `includeTypes` (type: `array`):

Limit output to public Telegram entity types. Example: channel. Defaults to channel, group, and message. Bots and private invite links are skipped. This is not a Telegram login filter.

## `language` (type: `string`):

Optional ISO 639-1 language hint for search ranking, for example en or ru. Leave empty for mixed-language public results. This is not a country code and does not unlock private chats.

## `proxyConfiguration` (type: `object`):

Optional Apify proxy. Leave off for the default direct HTTP path. Enable Apify Residential if public search pages return 403/429. This is not a Telegram account session.

## Actor input object example

```json
{
  "queries": [
    "bitcoin"
  ],
  "maxResultsPerQuery": 5,
  "includeTypes": [
    "channel",
    "group",
    "message"
  ]
}
```

# Actor output Schema

## `results` (type: `string`):

Public Telegram matches plus CLEAR/failed rows.

## `output` (type: `string`):

Final outcome, itemsPushed, billedResults, and chargedEventCounts.

## `runSummary` (type: `string`):

Per-query totals, per-type totals, retries, and warnings.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "queries": [
        "bitcoin"
    ],
    "maxResultsPerQuery": 5,
    "includeTypes": [
        "channel",
        "group",
        "message"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("khadinakbar/telegram-keyword-search-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "queries": ["bitcoin"],
    "maxResultsPerQuery": 5,
    "includeTypes": [
        "channel",
        "group",
        "message",
    ],
}

# Run the Actor and wait for it to finish
run = client.actor("khadinakbar/telegram-keyword-search-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "queries": [
    "bitcoin"
  ],
  "maxResultsPerQuery": 5,
  "includeTypes": [
    "channel",
    "group",
    "message"
  ]
}' |
apify call khadinakbar/telegram-keyword-search-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,khadinakbar/telegram-keyword-search-scraper"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/I44UEwoe5XdhhFcjz/builds/ezRsml4bsV5eO9Ht4/openapi.json
