How to Send a Daily News Digest to Telegram
One scheduled request for the last 24 hours of coverage, formatted and sent to a Telegram chat — complete script and cron line
Written by Jacob Partington
July 6, 2026
How to send a daily news digest to Telegram
A daily news digest in Telegram is one scheduled API call: request the last 24 hours of articles that match your topic with published_at.start=NOW-24HOURS, format the results as one message, and post it to your chat through the Telegram Bot API. The full Node.js script is below — no dependencies — plus the cron line that runs it every morning. On a day with no coverage it sends nothing and, because empty search results are free, costs nothing.
Step 1: create the bot and find your chat id
Message @BotFather in Telegram, send /newbot, and copy the bot token it returns (the string like 123456:ABC-...). Then open a chat with your new bot, send it any message, and fetch https://api.telegram.org/bot<TOKEN>/getUpdates in a browser — the response contains your chat.id. For a channel or group digest, add the bot there as an admin and use that chat’s id instead.
Step 2: shape the digest query
The digest is a normal search against /v1/news/everything, so tune it there first. This example collects yesterday’s coverage of a company, strongest sources first, trimmed to the fields the message needs:
curl "https://api.apitube.io/v1/news/everything?organization.name=Tesla&language.code=en&published_at.start=NOW-24HOURS&sort.by=source.rank.opr&per_page=10&fl=id,title,href,source.domain&api_key=YOUR_API_KEY"
sort.by=source.rank.opr puts articles from high-authority outlets at the top, which reads better in a short digest than raw recency — the score is explained in what OPR is. Swap the filter for whatever your digest is about: a category.id, a topic.id, an entity.id or a plain title keyword all work the same way, as listed in the official News API parameters reference.
Step 3: the complete digest script
Save as digest.mjs and run with node digest.mjs (Node 20+). It fetches the day’s articles, formats an HTML message, and sends it — skipping the send entirely on a quiet day:
const API_KEY = process.env.APITUBE_API_KEY;
const BOT_TOKEN = process.env.TELEGRAM_BOT_TOKEN;
const CHAT_ID = process.env.TELEGRAM_CHAT_ID;
const params = new URLSearchParams({
"organization.name": "Tesla",
"language.code": "en",
"published_at.start": "NOW-24HOURS",
"sort.by": "source.rank.opr",
"per_page": "10",
"fl": "id,title,href,source.domain"
});
const res = await fetch(`https://api.apitube.io/v1/news/everything?${params}`, {
headers: { "X-API-Key": API_KEY }
});
if (!res.ok) {
console.error(`search failed: HTTP ${res.status}`, await res.text());
process.exit(1);
}
const data = await res.json();
const articles = data.results ?? [];
if (articles.length === 0) {
console.log("no articles in the last 24h — nothing to send");
process.exit(0);
}
const esc = s => s.replace(/[&<>]/g, c => ({ "&": "&", "<": "<", ">": ">" })[c]);
const lines = articles.map(a => `• <a href="${a.href}">${esc(a.title)}</a> — ${a.source.domain}`);
const text = `<b>Daily news digest</b>\n\n${lines.join("\n")}`;
const send = await fetch(`https://api.telegram.org/bot${BOT_TOKEN}/sendMessage`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
chat_id: CHAT_ID,
text,
parse_mode: "HTML",
disable_web_page_preview: true
})
});
if (!send.ok) console.error("telegram send failed:", await send.text());
Two details worth keeping: titles are HTML-escaped before they enter the message (a headline containing < or & would otherwise break Telegram’s HTML parsing), and disable_web_page_preview stops Telegram from unfurling the first link into a giant card.
Step 4: schedule it
Run the script once a day with cron — here, every morning at 09:00:
0 9 * * * cd /opt/digest && APITUBE_API_KEY=... TELEGRAM_BOT_TOKEN=... TELEGRAM_CHAT_ID=... node digest.mjs
Because the query uses a relative NOW-24HOURS window anchored at run time, the schedule and the window stay aligned automatically. If you later change the cadence — say, twice a day — shrink the window to match (NOW-12HOURS) so editions do not repeat each other; the relative syntax is covered in how to filter by date range.
Common Questions
- What does a daily digest cost?
- Can one script send several digests?
- Why not stream instead of polling once a day?
What does a daily digest cost?
One search request per run, and only when it returns articles — an empty result set is free, and the script exits before contacting Telegram. Ten items per day fits comfortably inside every plan, including Free at 10 requests per minute.
Can one script send several digests?
Yes — loop over an array of {filters, chatId} pairs and run the fetch-format-send steps per entry. Each topic is its own charged request, so five digests cost at most five requests a day. Keep distinct topics in distinct Telegram chats rather than one merged message; readers mute what they cannot filter.
Why not stream instead of polling once a day?
A digest is periodic by definition — the reader wants one message at 09
, not thirty pings across the day. Streaming pays off when seconds matter; for everything scheduled, a single request is simpler and cheaper. The trade-off is laid out in polling vs streaming.