Send emails with Ruby
Send email from Ruby or Rails with the official mailblastr gem — zero runtime dependencies, plain-hash params, and typed errors.
The official `mailblastr` gem is the fastest way to send email from Ruby. It has zero runtime dependencies — only net/http, json and openssl from the standard library — so it drops into a Rails app, a Sinatra service, or a bare script without pulling anything else in.
One thing to know up front: the gem is configured at the module level (Mailblastr.api_key = ...) rather than by constructing a client object, and every resource is a module method — Mailblastr::Emails.send, Mailblastr::Contacts.create, and so on. Note the capitalisation: the constant is Mailblastr, with a lowercase b.
Prerequisites
- A verified domain for your
fromaddress. (guide) - An API key (
mb_...), read fromENV["MAILBLASTR_API_KEY"]or Rails credentials. (Authentication) - Ruby 2.7 or newer — that is the floor declared by the gemspec (
required_ruby_version).
Install
gem install mailblastrOr add it to your Gemfile and run bundle install:
gem "mailblastr", "~> 5.0"Configure
Set the key once at boot. Mailblastr.api_key = ... on its own is enough; the configure block is the same assignment with room for the optional settings.
require "mailblastr"
Mailblastr.configure do |config|
config.api_key = ENV.fetch("MAILBLASTR_API_KEY")
# Optional:
# config.base_url = "https://www.mailblastr.com/api" # override the API host
# config.timeout = 10 # seconds per attempt (default 30; 0 disables)
# config.max_retries = 3 # 429/503 retry budget (default 2)
endMailblastr::Emails.send works from anywhere once the key is set. Call it before setting the key and the gem raises a Mailblastr::Error whose name is missing_api_key — it fails locally rather than spending a request, which is also why that one error has a nil #status_code.Send an email
Params are a plain hash with snake_case keys, passed through as JSON. delivered@mailblastr.dev is MailBlastr's mailbox simulator — the send is accepted, produces a real email object and a delivery event, and never reaches a provider, so it is safe to send to while you wire things up. Do not point a send at example.com: those recipients are suppressed and the call comes back 422 validation_error having sent nothing.
require "mailblastr"
Mailblastr.api_key = ENV.fetch("MAILBLASTR_API_KEY")
begin
sent = Mailblastr::Emails.send({
from: "Acme <hello@yourdomain.com>",
to: ["delivered@mailblastr.dev"],
subject: "Hello from Ruby",
html: "<p>Sent with the official Ruby gem 💎</p>"
})
puts "Sent email #{sent['id']}"
rescue Mailblastr::Error => e
warn "MailBlastr #{e.status_code} #{e.name}: #{e.message}"
endHandling the response
A successful call returns the parsed JSON body — a Hash with string keys, so the id is sent["id"] and not sent[:id]. Keep it to retrieve the email later or to correlate webhook events.
{
"id": "49a3999c-0ce1-4ea6-ab68-afcd6dc2e794"
}Any non-2xx answer raises Mailblastr::Error, which carries the API's { statusCode, name, message } envelope as #status_code, #name and #message. Branch on #name together with #status_code, never on #message — message text is scrubbed server-side and is not a stable contract. Plan and quota rejections additionally populate #limit and reputation gates populate #reputation; both are nil on an ordinary error, and #body is the whole parsed body so a field newer than your gem version is still reachable.
begin
Mailblastr::Emails.send(params)
rescue Mailblastr::Error => e
case e.name
when "validation_error"
# 422 — a bad field (and 403 on a raw request with no User-Agent).
when "daily_quota_exceeded", "monthly_quota_exceeded", "plan_limit_reached"
if (cap = e.limit)
warn "#{cap['kind']} cap hit: #{cap['used']}/#{cap['limit']}"
end
when "missing_api_key", "invalid_api_key", "restricted_api_key"
# 401/403 — the key is absent, wrong, or lacks the scope for this route.
else
raise
end
endFrom a Rails controller or job
With the initializer above in place there is nothing else to wire up. Send from a background job so a slow API call never blocks the request.
class WelcomeEmailJob < ApplicationJob
queue_as :mailers
def perform(user)
sent = Mailblastr::Emails.send(
{
from: "Acme <hello@yourdomain.com>",
to: [user.email],
subject: "Welcome to Acme",
html: "<p>Hi #{CGI.escapeHTML(user.first_name.to_s)}, thanks for signing up.</p>"
},
{ idempotency_key: "welcome-#{user.id}" }
)
user.update!(welcome_email_id: sent["id"])
end
endThe second argument is an options hash. idempotency_key is honoured by Mailblastr::Emails.send and Mailblastr::Batch.send only — a retry with the same key replays the original response instead of sending twice. It must be 1–255 characters (Mailblastr::Client::IDEMPOTENCY_KEY_MAX_LENGTH); the server answers anything else with 400 invalid_idempotency_key.
User-Agent on every request (mailblastr-ruby/5.1.0), which the API requires on every route — a request without one is rejected with 403 validation_error before it is even authenticated. It also retries 429 and 503 automatically, honouring Retry-After, up to Mailblastr.max_retries times (default 2). Nothing else is retried — not a timeout, not a network error, not any other status — so a send is never silently duplicated.mb_ API key server-side — in Rails credentials or an environment variable, never in client-side code or version control. Anyone with the key can send email as you.Next steps
- Send up to 100 messages in one request with
Mailblastr::Batch.send([...]). Batch items rejectattachmentsandscheduled_at— send those one at a time. - See the full Send Email API reference for every body field (
cc,bcc,reply_to,attachments,scheduled_at). - Explore the SDKs reference for the other resources the gem exposes — contacts, segments, campaigns, templates, automations, webhooks and events.
- Verify inbound webhooks with
Mailblastr::Webhooks.verify(raw_body, headers, signing_secret)— passrequest.raw_post, never re-serialized JSON. See Webhooks. - Prefer no gem at all? Send with Ruby over raw HTTP uses only
net/http.