RSS → INBOX · API-FIRST

Your feed,
in their inbox.
Billed by the email,
not the head.

Emit watches your RSS feed and emails new posts to your subscribers from your own domain. You pay for emails actually sent — no subscriber tiers, no monthly minimum, no free-tier theater.

Get an API key → Read the docs $10 min top-up · no card on file
  Resend + SES DKIM + DMARC OpenAPI 3.1 1 credit = 1 email
~/blog · zsh ⌘ + K
  new post triggers 
$0.80 per 1,000 emails
$0 per-subscriber fees
$10 minimum top-up · no card
3 calls from zero to delivery
— 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.

14 endpoints · < 200 LOC client
— live in three calls

From zero to 200 OK in 60 seconds.

Full reference →
01 Create an account POST /v1/accounts
# returns an sk_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":"posts@blog.dev"}'
03 Top up & go POST /v1/billing/topup
# $20 ≈ 25,000 emails. autopilot.
curl -X POST api.rssemit.com/v1/billing \
  -H "Authorization: Bearer $KEY" \
  -d '{"amount_usd":20}'
# → 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 → sk_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",
        "from":    "posts@blog.dev",
        "html":    "<h1>We shipped.</h1>",
        "audience": "all_confirmed"
      }'

# → { "id": "bcst_8Hk…", "status": "queued", "recipients": 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",
    from:     "posts@blog.dev",
    html:     "<h1>We shipped.</h1>",
    audience: "all_confirmed",
  }),
});

const { id, recipients } = await r.json();
console.log(`queued ${recipients} 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",
        "from":     "posts@blog.dev",
        "html":     "<h1>We shipped.</h1>",
        "audience": "all_confirmed",
    }).encode(),
)

res = json.loads(u.urlopen(req).read())
print(f"queued {res['recipients']} to {res['id']}")
// go 1.22
body, _ := json.Marshal(map[string]any{
    "subject":  "Shipping Emit v1.4",
    "from":     "posts@blog.dev",
    "html":     "<h1>We shipped.</h1>",
    "audience": "all_confirmed",
})

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",
  from:     "posts@blog.dev",
  html:     "<h1>We shipped.</h1>",
  audience: "all_confirmed",
}.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 number. $0.80 per 1,000 emails.

Prepaid credits. $10 minimum top-up. One credit, one email — including bounces, but only on confirmed delivery attempts. That's the whole pricing page.

Pay-as-you-go
$0.80/ 1,000 emails
Top up & ship →
20,000 emails/mo $16.00 / month
No subscriber tiers No monthly minimum No overage surprise Credits never expire
$0.0008 / email · all-in
— 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.mjml
override per-feed
<!-- emit interpolates {{ }} from the
     RSS <item> — title, link, html,
     pubDate, and any <custom:*> tags -->

<mj-section>
  <mj-column>
    <mj-text font-size="11"
             color="#6b6b70">
      {{ feed.title }} · issue #{{ issue }}
    </mj-text>

    <mj-text font-size="30"
             font-weight="600">
      {{ title }}
    </mj-text>

    <mj-raw>{{ html_excerpt }}</mj-raw>

    <mj-button href="{{ link }}">
      Continue reading →
    </mj-button>
  </mj-column>
</mj-section>
— faq

Questions you'd ask before paying.

If yours isn't here, team@rssemit.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.0008 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 ~12,500 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.