Hermes Agent on a Schedule: A Daily Lead Finder That Posts to Telegram
Hermes Agent is one of the fastest-rising AI searches in India this year. Most posts about it show the chat side: talk to it on Telegram, let it write skills, let it remember you. This one shows the boring side, which is where I got the most value: a scheduled job that runs every morning at 9:00 IST, finds people asking for SEO help, drafts a reply, and sends me a Telegram digest I can act on from my phone.
It has been running daily since 15 October 2026. In its first week it collected 276 posts, threw away 245 as not a real buyer, and drafted replies for 31. I approve every message by hand.
Last checked: 22 October 2026, on Hermes Agent v0.21.4.
What Hermes Agent is (in one paragraph)
Hermes Agent is an open-source (MIT) agent from Nous Research. You install it on your laptop or a VPS, and one gateway process connects it to Telegram, Discord, Slack, WhatsApp and the CLI. It has memory, skills, plugins and a built-in cron scheduler that delivers results to any of those chats. If you've read my OpenClaw self-hosting guide, it's the same category of tool.
The pipeline
09:00 IST Hermes cron (no-agent mode)
│
├─ Reddit search (12 subs + 7 queries) ┐
├─ Hacker News (Algolia API, last 3 days) │ ~300 posts/day
├─ X live search (3 queries) │
└─ LinkedIn contract / part-time SEO jobs ┘
│
▼ regex filter: SEO words AND buying words, NOT "for hire" / "my agency"
SQLite (leads.db, url = unique key, so nothing is seen twice)
│
▼ PageSpeed Insights audit if the post links a site (free, no key)
Groq gpt-oss-120b: score 1–10, fit yes/no, draft first reply
│
▼ stdout
Telegram digest: top 6 leads + /send /edit /skip /email
All of it is one Python file using only the standard library: about 330 lines, with no requests and no LangChain.
Step 1: a no-agent cron job
This is the feature that makes Hermes useful for automation. A normal cron job wakes the agent up, gives it a prompt and lets the LLM decide what to do. A no-agent job just runs a script and sends its stdout to the chat word for word. No model and no tokens are involved.
# script lives in ~/.hermes/scripts/
hermes cron create "0 9 * * *" \
--no-agent \
--script seo_leads.py \
--deliver telegram \
--name seo-leads-daily
The rules that matter, from the Hermes cron docs:
"kind": "late". This morning's ran 54 minutes late, and that's fine.Step 2: collect from four sources
Each source is a Python generator that yields the same dict shape: url, source, author, title, body.
def hackernews():
since = int(time.time()) - 3 * 86400
url = "https://hn.algolia.com/api/v1/search_by_date?" + urllib.parse.urlencode(
{"query": "SEO", "tags": "(story,comment)",
"numericFilters": f"created_at_i>{since}", "hitsPerPage": 100})
for h in json.loads(get(url))["hits"]:
yield {"url": f"https://news.ycombinator.com/item?id={h['objectID']}",
"source": "hackernews", "author": h.get("author") or "",
"title": h.get("title") or h.get("story_title") or "",
"body": strip_html(h.get("comment_text") or h.get("story_text") or "")}
Every source is wrapped so that one failure can't sink the run. Errors go into a list that is printed at the bottom of the digest:
⚠️ reddit 'need SEO help': exitCode: 69
That warning is real. Reddit search through the browser failed on 20 and 22 October, and the digest told me so the same morning.
Step 3: a cheap filter before any LLM
Most posts that mention SEO come from sellers, not buyers. Three regexes remove most of them for free:
INTENT = re.compile(r"\b(hiring|hire|looking for|need(ed)?|seeking|recommend|help with|budget|paid|freelancer|agency)\b", re.I)
SEO = re.compile(r"\b(seo|search console|google rank|organic traffic|core web vitals|indexing|indexed|serp)\b", re.I)
NOT_BUYER = re.compile(r"\bfor hire\b|\bi am an seo\b|\bmy seo agency\b|\boffering\b", re.I)
A post must match SEO and buying intent, and must not look like an agency ad. Reddit posts on user profiles are dropped too (that's almost always self-promotion). Whatever survives goes into SQLite with insert or ignore on the URL, so a post is processed once, ever.
Step 4: Groq qualifies and drafts
New leads go to Groq's openai/gpt-oss-120b in batches of 4. The system prompt is not hard-coded. It's sections 1–5 of a PLAYBOOK.md file, read at runtime. To change how replies sound, I edit the markdown, and the next morning's drafts follow it.
The rules that made drafts worth sending:
{"id", "fit", "score", "why", "draft"}.Two things I learned the hard way:
1. reasoning_effort: "low" made things up. It invented broken sitemaps and past clients. "medium" stopped that.
2. The free tier has a tokens-per-minute cap. With the playbook, one call is about 4k tokens, so the script sleeps 35 seconds between batches and retries 429s. If a batch fails, those leads stay new and are retried tomorrow.
If you don't have a Groq key, any OpenAI-compatible endpoint works. I compare the free options in Free LLM APIs in 2026.
Step 5: the Telegram digest and slash commands
The script prints the top 6 leads by score, each with the post title, a one-line "why", the PageSpeed audit if there was a site, the draft and the link. Here is a real one from the first week (lead #27, r/smallbusiness, scored 8/10: "Help on website developed in Claude Code"). The model's first draft was:
Since you'll be adding blog posts weekly, setting up a proper XML sitemap and automating its updates will ensure Google crawls new content fast. I can implement a Next.js-compatible sitemap generator... Does that sound useful?
It's generic, it pitches, and it doesn't answer the question. I rewrote it like this:
One thing to check with Claude Code sites: if it built a client-side React/Vite app, Google sees a nearly empty page until JavaScript runs, and new blog posts can take weeks to get indexed. Quick test: right-click → View Page Source (not Inspect). If your blog text isn't in there, that's the issue, and prerendering fixes it. Happy to look at the URL if you want a second pair of eyes.
Then I pasted both versions into the playbook as a "weak vs strong" example. Every later draft is written with that example in front of the model, and no code changed. The digest ends with the commands:
/send ID · /edit ID what to change · /skip ID · /email ID addr · /leads
Those commands come from a small Hermes plugin: a plugin.yaml and an __init__.py that calls ctx.register_command() for each one. Each handler runs the same script with a subcommand, so no LLM is involved except for /edit:
def register(ctx):
for name, (prefix, usage, desc) in COMMANDS.items():
ctx.register_command(name, handler=_cmd(*prefix, usage=usage), description=desc)
for status in ("skip", "won", "lost", "replied", "call"):
... # each one runs: seo_leads.py mark ID
/send 27 prints the final text and marks the lead contacted. I post it myself. No auto-DMs./edit 27 shorter, mention Next.js rewrites the draft with Groq./email 27 founder@site.com sends it from my own domain through a Cloudflare Worker mail API./replied, /call, /won and /lost turn the same SQLite table into a tiny CRM.Why I switched the agent's LLM off
My first version was a normal Hermes cron job: "find SEO leads and draft replies", with the agent choosing tools. It failed for three reasons:
1. Prompt size. Hermes's agent system prompt (tools, memory, skills) was about 18k tokens. Groq's free tier allows roughly 8k tokens a minute for this model, so every run hit 429 before doing anything.
2. API mismatch. Hermes sent a reasoning_effort value that Groq rejected.
3. Cost and reliability. Free models on aggregator routers expired or rate-limited from one week to the next.
The fix was to let the script make all the decisions and use the LLM only for the one thing it's good at: reading a post and writing a reply. Hermes still does what it's best at: scheduling, catching up missed runs, delivering to Telegram and hosting the commands.
That's my general rule for agents now: if you can write the steps down, write them as code, and call the model only for judgment. I wrote about the same trade-off for my infrastructure agent.
Results after one week
| Count | |
|---|---|
| Posts collected (15–22 Oct) | 276 |
| Auto-skipped (sellers, spam, not a fit) | 245 |
| Drafted for review | 31 |
| Biggest skip source | X (55), r/AISEOInsider (25) |
| Best drafted source | r/jobboardsearch, X, r/SaaS |
| Cost | ₹0 (free Groq tier, free APIs, my own laptop) |
Daily cost is zero because it runs on hardware I already own. On a VPS, it fits next to n8n on a ₹400–700/month server (cost breakdown).
Build your own
The same shape works for any "watch the internet for X" job: job posts, tenders, mentions of your product, competitor launches.
hermes cron create ... --no-agent --script your_script.py --deliver telegramWant a scheduled agent like this for your own leads, alerts or reports? Get in touch.
Related Posts
Built something similar or want to talk through the architecture? Get in touch.