DEV Community

Dakota Liu
Dakota Liu

Posted on

I Put My Free LLM Quota on a Cron Job: A 60-Line Daily Digest Bot

Every morning I open three dashboards, two newsletters, and one very long Slack thread. Then I close them all and forget what mattered.

So I built a bot that does the reading for me. It runs on a schedule, pulls a few feeds, and sends me one tight summary. Total cost: zero. Total code: about 60 lines.

Here's the part nobody tells you about free LLM quotas: they're perfect for scheduled jobs. A daily digest uses maybe 5,000 tokens a day. Even a 10-million-token monthly allowance (what MonkeyCode's free tier offered as of August 2026 — check the repo, quotas change) lasts you months of daily digests.

This is not a tutorial about summarization. This is a tutorial about making a free endpoint do useful work while you sleep.

The architecture

Three pieces:

  1. A fetcher — pulls RSS feeds or JSON endpoints
  2. A summarizer — sends the content to an LLM endpoint
  3. A notifier — pushes the result to Telegram, email, or a file

I run it with GitHub Actions on a cron schedule. No server to maintain, no cost, and the logs are right there in the Actions tab.

Step 1: The fetcher

Keep it boring. No framework, just urllib and xml.etree:

import urllib.request
import xml.etree.ElementTree as ET

def fetch_rss(url, limit=5):
    req = urllib.request.Request(url, headers={"User-Agent": "digest-bot/0.1"})
    with urllib.request.urlopen(req, timeout=15) as r:
        root = ET.fromstring(r.read())
    items = []
    for item in root.iter("item")[:limit]:
        title = item.findtext("title", "")
        link = item.findtext("link", "")
        items.append(f"- {title}\n  {link}")
    return "\n".join(items)
Enter fullscreen mode Exit fullscreen mode

RSS is still the most reliable way to get content without API keys. If your source doesn't have RSS, use a JSON endpoint instead — same idea, different parser.

Step 2: The summarizer

The prompt matters more than the model. I spent a week tuning this:

import json, urllib.request

def summarize(content, endpoint, api_key, model):
    prompt = f"""You are a technical news editor. Read these headlines and links.
Write a 3-bullet digest. For each bullet: what happened, why it matters, and one
question a developer should ask. Ignore anything that's pure marketing.

CONTENT:\n{content}"""

    payload = {
        "model": model,
        "messages": [{"role": "user", "content": prompt}],
        "max_tokens": 300,
        "temperature": 0.3,
    }
    req = urllib.request.Request(
        endpoint,
        data=json.dumps(payload).encode(),
        headers={
            "Content-Type": "application/json",
            "Authorization": f"Bearer {api_key}",
        },
        method="POST",
    )
    with urllib.request.urlopen(req, timeout=60) as r:
        body = json.load(r)
    return body["choices"][0]["message"]["content"]
Enter fullscreen mode Exit fullscreen mode

Three prompt lessons from real failures:

  1. "Summarize" is a weak verb. "Write a 3-bullet digest" gives the model a structure to fill.
  2. Add a filter instruction. Without "ignore pure marketing," half your digest is press-release language.
  3. Ask for a question. It turns the digest from a recap into a thinking tool.

Step 3: The notifier

Telegram is the easiest free notification channel. Create a bot, get the chat ID, send a message:

import urllib.parse, urllib.request

def notify_telegram(text, bot_token, chat_id):
    url = f"https://api.telegram.org/bot{bot_token}/sendMessage"
    data = urllib.parse.urlencode({"chat_id": chat_id, "text": text}).encode()
    urllib.request.urlopen(url, data=data, timeout=15)
Enter fullscreen mode Exit fullscreen mode

No SDK needed. It's one HTTP call.

Step 4: The cron

GitHub Actions makes this trivial. Save as .github/workflows/digest.yml:

name: daily-digest

on:
  schedule:
    - cron: "0 7 * * *"  # 7 AM UTC
  workflow_dispatch:  # manual trigger for testing

jobs:
  digest:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: "3.12"
      - run: pip install -r requirements.txt
      - run: python digest.py
        env:
          LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }}
          LLM_API_KEY: ${{ secrets.LLM_API_KEY }}
          LLM_MODEL: ${{ secrets.LLM_MODEL }}
          TELEGRAM_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }}
          TELEGRAM_CHAT_ID: ${{ secrets.TELEGRAM_CHAT_ID }}
Enter fullscreen mode Exit fullscreen mode

Secrets go in the repo settings. Never commit keys.

Where MonkeyCode fits

I needed an endpoint that (a) was OpenAI-compatible so my code stayed portable, (b) had a free tier generous enough for daily runs, and (c) didn't require a credit card to start. MonkeyCode's free server checked all three boxes when I set this up. The 10-million-token monthly allowance meant I could run this bot daily for months without thinking about cost.

Disclosure: This article was prepared as part of MonkeyCode's product outreach.

Is it the best model for summarization? Probably not. Is it good enough for a morning digest? Absolutely. That's the tradeoff free tiers are actually good for: not production workloads, but personal automation that runs once a day and fails gracefully.

What I learned running it for 30 days

Failure #1: Timeouts. The first version had a 30-second timeout. A slow endpoint killed the whole job. Fix: raise it to 60 seconds and add a retry.

Failure #2: No deduplication. The same story appeared in three feeds. The digest had three near-identical bullets. Fix: keep a small JSON file of seen titles, skip duplicates.

Failure #3: Token waste. I was sending full article text when headlines were enough. Cutting content from 2,000 tokens to 400 tokens per source made the digest faster and cheaper.

The honest limitations

This bot is not a production system. It's a personal tool with three failure modes you should know about:

  • It depends on a free tier that can change. The allowance I used in August 2026 might not exist next quarter. The code is portable — that's the point of using an OpenAI-compatible endpoint.
  • It's only as good as its sources. Garbage in, confident-sounding garbage out.
  • It can't catch nuance. A headline summary misses context. It's a filter, not a replacement for reading.

Who should not build this

Skip this approach if you need:

  • Real-time alerts — cron jobs have latency by design
  • Guaranteed delivery — free endpoints have no SLA
  • Compliance-grade data handling — your prompts go to a third party

For everything else, a scheduled LLM job is the cheapest automation you'll ever run.

The takeaway

Free LLM quotas are usually wasted on chat windows. The real value is in unattended jobs: digests, monitors, classifiers, translators. A cron job doesn't care about latency. It doesn't care about rate limits. It just needs enough tokens and a stable API shape.

My digest bot has run for 30 days without a single manual intervention. That's 30 mornings I didn't spend scrolling. If you've got a free endpoint sitting idle, point it at something boring and repetitive — that's where the leverage is.

MonkeyCode provides free models that can run this workflow.

Top comments (0)