Best Practices when sending a million emails

This is the thing with my app. My app charge someone $15 a month. ok. this person could have 10000 followers. if this person decide to send 10000 users newsletters 4 times a month I’m going to be broke no ?

I need to be smart. Although I’m new to this field I have to be careful with spending my life savings you know

I’m going to go with the first answer I got from
@nathan.[quote=“nathantreid, post:6, topic:26035”]
How likely is it that all your clients will be sending emails at the same time? If it’s at all likely, then make sure you do some tests sending out 100,000+ emails at once and see how your site responds; hopefully you will find that by using Meteor.defer or Meteor.setTimeout the app performs just fine.
[/quote]

and then If I get stuck with server issue then I will have a strong understanding of the tech I’m playing with.

Then the pricing of your SaaS is way off. $15/mo and you’ll let someone send 40,000 emails per month? On Postmark, that one user in one month will cost you $40. On SendGrid, if you’re on the Essentials $20/mo plan, they will have used up 40% of your allotted emails. So the obvious answer is, your $15/mo fee is way way too low!

You obviously need to have tiered pricing. Look at MailChimp’s plans, for example. No way could someone send 40,000 emails on MailChimp’s platform for $15/mo!

If you’re building a serious app, I’d sit down and crunch the numbers. A lot. Or talk with someone savvy with SaaS pricing. I did, and it was a huge help to me.

Good luck!

1 Like

We are having a tough time working through this right now as well!

What we’ve learned so far is that you can’t set saas pricing based on competitors, and also that pretty much every saas that offers ‘unlimited’ anything really doesn’t when you look at the details/hit their hidden limits.

Yeah !! thanks a lot for the info. Yeah I need to find a way to manage everything. $15 was just a rough number. But your opinion is super valuable.

Thanks

1 Like

Pricing is one of the toughest things, hands down. There’s also a lot of psychology involved.

2 Likes

Consider Mailchimp/mandrill or others. They spend Millions on developers and security you can never afford. try to build a lean startup. Cap the number of Emails with pricing. If your customer earns money with sending 40k Emails you with your service can get a fair cut. Especially B2B. Build your platform first as simple as possible. See if customers like it. Adapt. Dont build mailchimp yourself over 2 years and then nobody wants it. Thats what APIs are for. Building a cheap MVP. Later adapt.

1 Like

Honestly, you probably shouldn’t be building this out yourself. Bulk email platforms, for the most part, are commodity products and have prices hovering around the bottom of what is sustainable. If your application idea doesn’t work with these prices, you may need to rethink your idea/profit/etc…?

BUT, thinking about how to build this is a fun exercise. When building out system that directly communicate with people, always plan for failure and how you will respond. Failures will happen. This is a guarantee.

Let’s call each email you want to send out a Campaign. Each Campaign might have some metadata (a template, who its from, etc…), and a list of receivers. A naive way (on a worker process, ignoring timeouts, etc…) of delivering a Campaign might look something like…

let campaign = Campaigns.findOne(...);
campaign.receivers.map((receiver) => {
    Email.send({
        to: receiver,
        ...
    });
});

But what if Email.send throws an exception? That will interrupt your map and prevent all subsequent sends from happening.

Let’s say we’ve fixed the problem (if it was ours to fix), and now if we want to retry sending the campaign. Unfortunately, we have no idea who’s already been sent the email. Our retry will resend the email to lots of users. Not good!

You need some way of handling errors as they happen, and tracking who’s been successfully sent the campaign.

function deliverCampaign(campaignId) {

    let campaign = Campaigns.findOne(campaignId);
    let receivers = _.without(campaign.receivers, campaign.delivered);
    let delivered = [];

    receivers.map((receiver) => {
        try {
            Email.send({
                to: receiver,
                ...
            });
            delivered.push(receiver);
        } catch (e) {
            console.error(`Problem sending ${campaignId} to ${receiver}`, e);
        }
    });

    Campaigns.update(campaignId, {
        $push: {
            delivered
        }
    });

}

But what if problems happen elsewhere? What if the process is killed before we can update delivered on Campaigns? In theory we should update delivered after every email sent, but now we’re looking at millions of sequential Email.send calls followed by Campaigns.update. Not good.

A better architecture could be to use some kind of job queue. Each delivery could be a job pushed onto the queue with data about the Campaign being sent, the receiver, and information about how the job should be handled (retries, backoff, etc…). Each job handles sending one email to one receiver, and handles individual failures on its own. Whether or not a receiver was sent the email can either be inferred from the job’s completion status, or can be stored back in the Campaign.

What’s really nice is that we could potentially scale this solution to use many worker processes (across many servers) in parallel. Each process pulls a job off the queue and sends an email.

But again, there will be unforseen problems with this solution too. Will those problems result in double-sending emails and spamming users? You’d better hope not.

I’m rambling… In a very round-about way, I’m just trying to say that there are many things to consider when building out these kinds of solutions. I would happily pay $XX/mo for the peace of mind of handling it off to a SaaS solution like Mailchimp, etc…

Oh man you just showed the code that I was going to implement. Now that you’ve mentioned it. Shitt !!!

Do I have to go back to drawing board and figure out what to charge my customers ?

I really need to figure out something soon.

Thank you for the answer

I’m all about building for the future, but unless you have customers lined up willing to pay and needing to send 40k emials, this seems like analysis paralysis to me.

Focus on your tentpoles and worry about scaling when the time comes. There’s a difference between skating to where the puck is heading and building a stadium there.

1 Like

This is the best thing I’ve read all day.

1 Like

PS: This is why I’m using SendGrid as the email API for my SaaS. It lets you create nice-looking templates, and then on the Meteor side you just use an API key, send some parameters over, and boom, SendGrid will fill in the blanks and fire off said HTML email. I wanna spend my time on coding and marketing/advertising, not pulling out my hair over antiquated HTML email format :slight_smile:

2 Likes

This is my final verdict to start off with

I will be sending emails using Amazon SES, https://github.com/dwyl/sendemail/issues/41 this package

https://www.discovermeteor.com/blog/wrapping-npm-packages/

SES seems like a very good option. Haven’t shopped around that much, but can’t imagine anyone being cheaper here.

Sendy ( https://sendy.co/ ) is a pretty awesome product to send out bulk emails for very low costs. There’s a one off cost of about $60 and then it’ll be something like $1 per 10,000 emails. It has some pretty advanced features too. It is a bit of a pain to use, but will save you thousands of dollars over a product such as MailGun.

Also, you can use Sendy for multiple clients, so this might be exactly the product you’re trying to build.

With regards to the Discover Meteor article you posted, that’s outdated. Since Meteor 1.3 that’s not how you would work with npm packages in Meteor. See here instead: https://guide.meteor.com/using-npm-packages.html#using-npm