152

I need for testing purposes to populate few hundred email boxes with various messages, and was going to use smtplib for that. But among other things I need to be able to send messages not only TO specific mailboxes, but CC and BCC them as well. It does not look like smtplib supports CC-ing and BCC-ing while sending emails.

Looking for suggestions how to do CC or BCC sending messages from the python script.

(And — no, I'm not creating a script to spam anyone outside of my testing environment.)

user63503
  • 6,243
  • 14
  • 41
  • 44

8 Answers8

201

Email headers don't matter to the smtp server. Just add CC and BCC recipients to toaddrs when sending emails. For CC, add them to the CC header.

toaddr = 'buffy@sunnydale.k12.ca.us'
cc = ['alexander@sunydale.k12.ca.us','willow@sunnydale.k12.ca.us']
bcc = ['chairman@slayerscouncil.uk']
fromaddr = 'giles@sunnydale.k12.ca.us'
message_subject = "disturbance in sector 7"
message_text = "Three are dead in an attack in the sewers below sector 7."
message = "From: %s\r\n" % fromaddr
        + "To: %s\r\n" % toaddr
        + "CC: %s\r\n" % ",".join(cc)
        + "Subject: %s\r\n" % message_subject
        + "\r\n" 
        + message_text
toaddrs = [toaddr] + cc + bcc
server = smtplib.SMTP('smtp.sunnydale.k12.ca.us')
server.set_debuglevel(1)
server.sendmail(fromaddr, toaddrs, message)
server.quit()
My Car
  • 4,198
  • 5
  • 17
  • 50
ABentSpoon
  • 5,007
  • 1
  • 26
  • 23
  • smtplib.SMTP does not send lists as to addresses. At least not on python 2.7.2 – LostMohican Apr 05 '12 at 08:06
  • In this case, the BCC header mentioned in the RFC 2822 does not make any sense. – Chenxiong Qi Apr 21 '12 at 09:34
  • 1
    @ABentSpoon a colon is missing after 'Subject'. – user891260 Jun 25 '12 at 08:04
  • 3
    Don't add the bcc header. See this: https://mail.python.org/pipermail/email-sig/2004-September/000151.html And this: """Notice that the second argument to sendmail(), the recipients, is passed as a list. You can include any number of addresses in the list to have the message delivered to each of them in turn. Since the envelope information is separate from the message headers, you can even BCC someone by including them in the method argument but not in the message header.""" from http://pymotw.com/2/smtplib/ – Leonardo Andrade Apr 14 '15 at 10:45
  • 2
    This will never BCC or CC - it will always be To – ProsperousHeart Mar 25 '19 at 20:38
  • @ProsperousHeart: Yes it should. At least on py3. The SMTP server does not care about TO, CC, BCC - it only sends TO everyone. The header specify how the email READER should interpret who is TO and CC. In the case of BCC one send TO (as in SMTP's TO) but do not include it in header (message) for TO and CC recipients. One can optionally use a separate e-mail header for the BCC where one include the BCC recipient alone or a list of BCC recipients. But that is only for the e-mails sent TO (as in SMTP's TO) the BCC's. – user3342816 Sep 26 '19 at 02:36
  • @LeonardoAndrade: Yes and no. One should of course not include BCC in the header for the e-mail sent to the TO and CC recipients. But one could add BCC in a separate header for the BCC recipient. I'm not sure if `smtplib` supports this in any other way then for you to send the e-mail twice. Once for the TO / CC recipients and once for each of the BCC recipients - optionally all BCC recipients in one go - though if doing that I would communicate that clearly to sender before doing so. – user3342816 Sep 26 '19 at 02:43
73

Key thing is to add the recipients as a list of email ids in your sendmail call.

import smtplib
from email.mime.multipart import MIMEMultipart

me = "user63503@gmail.com"
to = "someone@gmail.com"
cc = "anotherperson@gmail.com,someone@yahoo.com"
bcc = "bccperson1@gmail.com,bccperson2@yahoo.com"

rcpt = cc.split(",") + bcc.split(",") + [to]
msg = MIMEMultipart('alternative')
msg['Subject'] = "my subject"
msg['To'] = to
msg['Cc'] = cc
msg.attach(my_msg_body)
server = smtplib.SMTP("localhost") # or your smtp server
server.sendmail(me, rcpt, msg.as_string())
server.quit()
helios
  • 799
  • 5
  • 5
  • 8
    Leave the `msg['BCC']` off -- it reveals your hidden sender and has no impact on whether the message is sent to them or not (the arguments to `sendmail` do that). – Erica Kane Oct 10 '17 at 14:31
  • 2
    I Agree with Erica and Julio. Intent of Bcc is to hide receiver to other receivers. If its in the message, that defeats the purpose. – Timothy C. Quinn May 03 '19 at 15:51
62

As of Python 3.2, released Nov 2011, the smtplib has a new function send_message instead of just sendmail, which makes dealing with To/CC/BCC easier. Pulling from the Python official email examples, with some slight modifications, we get:

# Import smtplib for the actual sending function
import smtplib

# Import the email modules we'll need
from email.message import EmailMessage

# Open the plain text file whose name is in textfile for reading.
with open(textfile) as fp:
    # Create a text/plain message
    msg = EmailMessage()
    msg.set_content(fp.read())

# me == the sender's email address
# you == the recipient's email address
# them == the cc's email address
# they == the bcc's email address
msg['Subject'] = 'The contents of %s' % textfile
msg['From'] = me
msg['To'] = you
msg['Cc'] = them
msg['Bcc'] = they


# Send the message via our own SMTP server.
s = smtplib.SMTP('localhost')
s.send_message(msg)
s.quit()

Using the headers work fine, because send_message respects BCC as outlined in the documentation:

send_message does not transmit any Bcc or Resent-Bcc headers that may appear in msg


With sendmail it was common to add the CC headers to the message, doing something such as:

msg['Bcc'] = blind.email@adrress.com

Or

msg = "From: from.email@address.com" +
      "To: to.email@adress.com" +
      "BCC: hidden.email@address.com" +
      "Subject: You've got mail!" +
      "This is the message body"

The problem is, the sendmail function treats all those headers the same, meaning they'll get sent (visibly) to all To: and BCC: users, defeating the purposes of BCC. The solution, as shown in many of the other answers here, was to not include BCC in the headers, and instead only in the list of emails passed to sendmail.

The caveat is that send_message requires a Message object, meaning you'll need to import a class from email.message instead of merely passing strings into sendmail.

  • 2
    Just verified that send_message(msg) handles the cc's added to the EmailMessage - msg['Cc'] If you're using Python 3 this is the way to go. – bmiller Jul 08 '19 at 18:40
  • @bmiller Yup, quite handy. I wrote up this answer because I was looking into it and found the simpler way in the docs and other refs, and it worked well for me, and I thought it deserved a place here since many people will look here 1st. –  Jul 08 '19 at 20:46
  • not only I find this solution more elegant but it works perfectly. It also solves an issue I had with the answer by @ABentSpoon , which is that for gmail adresses in CC, the emails were not going in the inbox but int he "All Mail" instead. – Guillaume Jun 03 '20 at 12:11
  • Note that when using email.message.EmailMessage, smtplib.send_message() will *not* deliver to any recipient addresses (to, cc, bcc) that are not listed in the to_addr parameter of the function if to_addr is provided when calling send_message() – The Coprolal Nov 26 '21 at 13:10
23

Don't add the bcc header.

See this: http://mail.python.org/pipermail/email-sig/2004-September/000151.html

And this: """Notice that the second argument to sendmail(), the recipients, is passed as a list. You can include any number of addresses in the list to have the message delivered to each of them in turn. Since the envelope information is separate from the message headers, you can even BCC someone by including them in the method argument but not in the message header.""" from http://pymotw.com/2/smtplib

toaddr = 'buffy@sunnydale.k12.ca.us'
cc = ['alexander@sunydale.k12.ca.us','willow@sunnydale.k12.ca.us']
bcc = ['chairman@slayerscouncil.uk']
fromaddr = 'giles@sunnydale.k12.ca.us'
message_subject = "disturbance in sector 7"
message_text = "Three are dead in an attack in the sewers below sector 7."
message = "From: %s\r\n" % fromaddr
    + "To: %s\r\n" % toaddr
    + "CC: %s\r\n" % ",".join(cc)
    # don't add this, otherwise "to and cc" receivers will know who are the bcc receivers
    # + "BCC: %s\r\n" % ",".join(bcc)
    + "Subject: %s\r\n" % message_subject
    + "\r\n" 
    + message_text
toaddrs = [toaddr] + cc + bcc
server = smtplib.SMTP('smtp.sunnydale.k12.ca.us')
server.set_debuglevel(1)
server.sendmail(fromaddr, toaddrs, message)
server.quit()
Leonardo Andrade
  • 2,508
  • 2
  • 18
  • 24
21

The distinction between TO, CC and BCC occurs only in the text headers. At the SMTP level, everybody is a recipient.

TO - There is a TO: header with this recipient's address

CC - There is a CC: header with this recipient's address

BCC - This recipient isn't mentioned in the headers at all, but is still a recipient.

If you have

TO: abc@company.com
CC: xyz@company.com
BCC: boss@company.com

You have three recipients. The headers in the email body will include only the TO: and CC:

Jim Garrison
  • 85,615
  • 20
  • 155
  • 190
20

You can try MIMEText

msg = MIMEText('text')
msg['to'] = 
msg['cc'] = 

then send msg.as_string()

https://docs.python.org/3.6/library/email.examples.html

ProsperousHeart
  • 188
  • 1
  • 14
foosion
  • 7,619
  • 25
  • 65
  • 102
10

None of the above things worked for me as I had multiple recipients both in 'to' and 'cc'. So I tried like below:

recipients = ['abc@gmail.com', 'xyz@gmail.com']
cc_recipients = ['lmn@gmail.com', 'pqr@gmail.com']
MESSAGE['To'] = ", ".join(recipients)
MESSAGE['Cc'] = ", ".join(cc_recipients)

and extend the 'recipients' with 'cc_recipients' and send mail in trivial way

recipients.extend(cc_recipients)
server.sendmail(FROM,recipients,MESSAGE.as_string())
Chan Gaikwad
  • 171
  • 3
  • 14
  • I will give you points but for me on outlook 2016 I had to join with "; " instead of comma. – Brent May 10 '23 at 02:08
9

It did not worked for me until i created:

#created cc string
cc = ""someone@domain.com;
#added cc to header
msg['Cc'] = cc

and than added cc in recipient [list] like:

s.sendmail(me, [you,cc], msg.as_string())
marko_b123
  • 300
  • 2
  • 14