PROGRAMMABLE RSS FEEDS · API-FIRST

Anything in.
Anything out.
You control
the middle.

Emit pipes anything on the web into a clean RSS feed — and any RSS feed into an email newsletter, a Slack channel, a webhook. Describe an interest in plain English and get a filtered feed; merge and summarize the feeds you already read; deliver them anywhere. Every stage is real RSS — pay per use, no tiers, no lock-in by construction.

Get an API key → Read the docs $10 min top-up · no card to start
  Atom 1.0 + JSON Feed 1.1 DKIM + DMARC OpenAPI 3.1 pay per use
~/blog · zsh ⌘ + K
  new post triggers 
$1.60 per 1,000 emails
$0 per-subscriber fees
$10 minimum top-up · no card to start
3 calls to wire up the pipe
— go do this

Stop checking. Start subscribing.

Everything below is a real pipeline you can build in the next five minutes — one API call or a couple of clicks in the dashboard. Each one ends in an RSS feed URL, an email, a Slack message, or a webhook. Your pick.

Turn your blog's RSS into a newsletter

The classic. Point emit at your RSS feed and every new post emails your subscribers from your own domain — per-post or as a daily/weekly digest. $1.60 per 1,000 emails, nothing per subscriber.

your rss.xml → email from you@yourdomain
#

Your competitor's changelog, in your Slack

Their /changelog has no RSS feed? emit makes one from the page itself, then pushes every new entry into the channel where your team will actually see it. Page → feed + a Slack broadcast.

their /changelog → rss → #competitive-intel
"

Follow a topic, not forty tabs

Type "vintage synth restorations and new Eurorack releases" and a feed finds the sources, reads everything they publish, and emits an RSS feed of only the items that match — each with a score and a reason.

"eurorack releases" → one filtered rss feed

Tame your overflowing RSS reader

Feed emit the 30 subscriptions you can't keep up with. Get back one merged RSS feed — duplicates collapsed, everything off-topic filtered out by a plain-English prompt, each item optionally pre-summarized.

30 feeds → filter + dedupe → 1 feed you finish

Watch pages that never had a feed

A city permits page, a government notices board, a jobs page, a band's tour dates. emit diffs the page and emits new entries as RSS — read them, or get them as an email alert the day they appear.

any listing page → rss → email alert
{ }

Wire the web into your own systems

Every new matching item can also arrive as a signed, retried webhook — straight into your CRM, n8n, Zapier, or that cron job you've been meaning to write. Delivery log included, HMAC verified.

new item → signed webhook → your code
— ingest → transform → deliver

A bus, not a box.

Every stage's output is a real, subscribable RSS feed (Atom 1.0 + JSON Feed 1.1) — so you can tap in or out anywhere, with any reader or tool that has spoken RSS for the last twenty years. Enter with feeds you already have, a page with no feed, or just a sentence describing what you want to follow. Exit to email, Slack, Discord, webhooks — or just read the feed.

→]

Ingest: anything → RSS feed

Paste feeds you already read. Point at a page with no feed and we diff it into one. Or describe an interest and the feed discovers the sources for you.

feed in · page → feed · prompt → feed

Transform: RSS feed → better RSS feed

An LLM relevance filter tuned by a plain-language prompt, cross-source dedupe, and per-item summaries. Every filtered-out item stays auditable — you can always see why you didn't see something.

filter · merge · dedupe · summarize
[→

Deliver: RSS feed → anywhere

The newsletter engine you came for, plus Slack and Discord sinks, signed webhooks with retries and a delivery log — and always the feed itself. Never gated: your data exits as easily as it entered.

newsletter · slack · discord · webhook · rss out
— what's in the box

A sending pipe. Not a CMS.

Emit does the four boring things between "you published a post" and "someone opened the email," and nothing else.

$

No subscriber tax

10,000 readers you email twice a month shouldn't cost the same as a daily blast. You're billed per email sent — full stop.

1 credit · 1 email · prepaid
@

Your domain, real deliverability

Send from a verified domain over Resend or Amazon SES with DKIM, bounce & complaint handling, list-unsubscribe headers, and one-click opt-out baked in.

posts@yourdomain.dev · SPF · DMARC
{ }

Everything is an API

A typed OpenAPI 3.1 spec, bearer keys, idempotent writes, and signed webhooks for key events. No dashboard you're forced through — wire it in and forget it.

compact REST API · < 200 LOC client
— three calls to wire it up

Three calls to a live 200 OK.

Full reference →
01 Create an account POST /v1/accounts
# returns an emit_live_… key, shown once
curl -X POST api.rssemit.com/v1/accounts \
  -d '{"name":"My Blog",
       "email":"me@blog.dev"}'
02 Connect a feed POST /v1/feeds
# polled every 5m · conditional GET
curl -X POST api.rssemit.com/v1/feeds \
  -H "Authorization: Bearer $KEY" \
  -d '{"url":"…/rss.xml",
       "from_email":"posts@blog.dev"}'
03 Top up & go POST /v1/billing/topup
# $20 ≈ 12,500 emails. autopilot.
curl -X POST api.rssemit.com/v1/billing/topup \
  -H "Authorization: Bearer $KEY" \
  -d '{"amount_usd":20}'
# one-time: verify your email + sending
# domain (DNS), then new posts mail
# themselves
FOR YOU + YOUR AGENT

Built so your coding agent can wire it up alone.

We ship a typed OpenAPI spec, a one-shot MCP server, and a Claude Code skill that handle domain verification, DNS records, subscriber import, and the first broadcast — in a single conversation.

Claude Code skill Codex CLI Cursor MCP server
~/blog · claude code
> add a newsletter to my blog using emit
Reading CLAUDE.md, found feed at /rss.xml.
Verifying domain blog.dev
emit.create_account → emit_live_••• issued
emit.verify_domain → 3 DNS records added to vercel.app
emit.connect_feed → posts@blog.dev · polling 5m
emit.import_subscribers → 1,284 from buttondown.csv
Done. Your next post will email 1,284 subscribers. Credits for 3.9k emails on file.
>
— pick your stack

One endpoint. Whatever language.

No SDKs to chase across versions. Bearer auth, JSON in, JSON out.

POST /v1/broadcasts copy
# Send a one-off broadcast to all confirmed subscribers
curl -X POST https://api.rssemit.com/v1/broadcasts \
  -H "Authorization: Bearer $EMIT_KEY" \
  -H "Content-Type: application/json" \
  -d '{
        "subject":   "Shipping Emit v1.4",
        "body_html": "<h1>We shipped.</h1>",
        "feed_id":   "8Hk2q9f1-…"
      }'

# → { "id": "8Hk2q9f1…", "status": "queued", "recipient_count": 4218 }
// node 20 · zero deps
const r = await fetch("https://api.rssemit.com/v1/broadcasts", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.EMIT_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    subject:   "Shipping Emit v1.4",
    body_html: "<h1>We shipped.</h1>",
    feed_id:   "8Hk2q9f1-…",
  }),
});

const { id, recipient_count } = await r.json();
console.log(`queued ${recipient_count} to ${id}`);
# python 3.11 · stdlib only
import os, json, urllib.request as u

req = u.Request(
    "https://api.rssemit.com/v1/broadcasts",
    method="POST",
    headers={
        "Authorization": f"Bearer {os.environ['EMIT_KEY']}",
        "Content-Type": "application/json",
    },
    data=json.dumps({
        "subject":   "Shipping Emit v1.4",
        "body_html": "<h1>We shipped.</h1>",
        "feed_id":   "8Hk2q9f1-…",
    }).encode(),
)

res = json.loads(u.urlopen(req).read())
print(f"queued {res['recipient_count']} to {res['id']}")
// go 1.22
body, _ := json.Marshal(map[string]any{
    "subject":   "Shipping Emit v1.4",
    "body_html": "<h1>We shipped.</h1>",
    "feed_id":   "8Hk2q9f1-…",
})

req, _ := http.NewRequest("POST",
    "https://api.rssemit.com/v1/broadcasts",
    bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer " + os.Getenv("EMIT_KEY"))
req.Header.Set("Content-Type", "application/json")

res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
# ruby 3.3
require "net/http"; require "json"

uri = URI("https://api.rssemit.com/v1/broadcasts")
req = Net::HTTP::Post.new(uri, {
  "Authorization" => "Bearer #{ENV['EMIT_KEY']}",
  "Content-Type"  => "application/json",
})
req.body = {
  subject:   "Shipping Emit v1.4",
  body_html: "<h1>We shipped.</h1>",
  feed_id:   "8Hk2q9f1-…",
}.to_json

res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }
puts JSON.parse(res.body)["id"]
— pricing

One balance. Pay for work, not seats.

Prepaid credits, $10 minimum top-up, one meter for everything. Making a feed is free — filtered feeds, turning pages into RSS, polling sources, serving your Atom/JSON feeds, Slack/Discord pushes, and webhooks all cost nothing. You pay for exactly three kinds of work, when they happen:

Pay-as-you-go
$1.60/ 1,000 emails
Top up & ship →
20,000 emails/mo $32.00 / month
send an email · 1 credit ($0.0016) LLM-filter an item · ⅕ credit ($0.00032) summarize an item · 1 credit ($0.0016) re-run source discovery · 25 credits ($0.04) everything else · 0

Worked example: a feed filtering ~40 new items a day ≈ 240 credits ≈ $0.38 / month. Add per-item summaries and it's ≈ $2.30.

No subscriber tiers No monthly minimum Feed-out never gated Credits never expire
$0.0016 / email · compare vs Mailchimp, Substack & more →
— what gets sent

A readable email, not a marketing artifact.

Default template is plaintext-first, HTML-friendly, dark-mode safe, and respects your post's typography. Override it per feed with your own raw HTML via the feed's template_html.

preview posts@blog.dev → reader@hey.com
html · 41 kb
blog.dev · issue #042 · may 26

How conditional GET keeps polling cheap

We poll 14,000 RSS feeds every five minutes. Here's the ETag trick that drops 92% of those requests to a 304 — and why it matters for your bill.

The naive answer is "cache the feed body and diff it." That works until you're hitting 14k publishers per cycle, your egress bill stops making sense, and half of those publishers are running WordPress instances that hate you personally.

Continue reading →
you're receiving this because you subscribed at blog.dev · unsubscribe
source template.html
override per-feed
<!-- raw HTML, rendered with Jinja. Set
     per feed via template_html. Variables:
     title, body, url, unsubscribe_url -->

<h1>{{ title }}</h1>

<div>{{ body }}</div>

<a href="{{ url }}">
  Continue reading →
</a>

<hr>
<p>
  <a href="{{ unsubscribe_url }}">
    Unsubscribe
  </a>
</p>
— faq

Questions you'd ask before paying.

If yours isn't here, will@heltonlabs.com — a human reads it.

Why credits and not a subscription?
Because monthly plans punish you for having a small list, and tiers punish you for having a successful one. Emit charges $0.0016 per email actually delivered. If you publish twice in January and ten times in February, your bill reflects exactly that — no minimum, no rollover games. Credits don't expire.
Do you handle bounces, complaints, and unsubscribes?
Yes. Hard bounces and spam complaints suppress the address automatically. One-click unsubscribe (List-Unsubscribe + List-Unsubscribe-Post) is on every send. Suppressed addresses are queryable via GET /v1/subscribers?status=bounced (or complained) and exportable through GET /v1/subscribers/export.
How is the RSS feed polled?
Every five minutes by default, with conditional GET (If-None-Match + If-Modified-Since). You can switch to on_publish mode and POST to /v1/feeds/{id}/ping from a webhook for instant delivery.
Can my coding agent set everything up?
That's the design goal. Emit ships a single-file OpenAPI 3.1 spec, an MCP server, and a Claude Code skill, so add a newsletter to my blog using emit in Claude Code or Codex can do DNS records, domain verification, subscriber import, and the first broadcast without you leaving the terminal.
What domains do you send from?
Yours. You verify a domain (we generate the DKIM, SPF, and DMARC records), and every send goes from: posts@yourdomain.dev. No <@rssemit.com> ever appears in your subscribers' inboxes.
Is there a free tier?
No. We don't want a free-tier theater, abuse problem, or the perverse incentive to make paid users worse. Your first $10 of credit gets you ~6,250 emails and never expires — pay only for what you send.
Where does my data live?
Postgres in us-east-1. We never sell subscriber data — there's nothing in it for us, and it would tank the only thing we're selling.

Built for people who'd rather curl than click.

The dashboard exists. We just hope you never need it.