imaplib no Python: leia e-mails com IMAP

Publicado em: 22/08/2026
Tempo de leitura: 5 minutos
A close-up of a laptop on a table, displaying a book on test-driven software with Python, set in a comfortable environment.

O módulo imaplib implementa um cliente IMAP na biblioteca padrão do Python. Ele permite listar caixas, pesquisar mensagens, buscar headers e corpos, copiar, mover por operações compatíveis, alterar flags e acompanhar novas mensagens. É útil para automações de suporte, processamento de anexos, arquivamento e integrações corporativas.

IMAP trabalha diretamente com uma caixa real. Uma chamada errada pode marcar mensagens como lidas, adicionar \Deleted ou removê-las com EXPUNGE. Comece com seleção somente leitura, use UIDs e teste em uma conta isolada.

Conexão segura com IMAP4_SSL

import imaplib
import ssl

contexto = ssl.create_default_context()

with imaplib.IMAP4_SSL(
    "imap.example.com",
    port=993,
    ssl_context=contexto,
    timeout=15,
) as cliente:
    cliente.login("usuario@example.com", "senha")
    print(cliente.noop())

A documentação informa que o contexto SSL padrão interno pode criptografar sem verificar certificado e hostname. Passe explicitamente ssl.create_default_context(). Não use IMAP simples na porta 143 com senha antes de STARTTLS.

Para certificados corporativos e versões mínimas, consulte ssl no Python.

Credenciais e autenticação

Não fixe senhas no código. Use variáveis de ambiente, gerenciador de segredos ou OAuth quando o provedor exigir. O método authenticate() permite mecanismos SASL suportados pelo servidor.

Alguns provedores desativam login por senha. Confirme capabilities e documentação do serviço. Nunca imprima tokens, senha ou respostas completas de autenticação.

Verificando capabilities

print(cliente.capabilities)

Capabilities indicam recursos como IMAP4REV1, IDLE, UIDPLUS, MOVE ou mecanismos de autenticação. Não presuma suporte universal. Implemente fallback ou recuse a operação quando o recurso for obrigatório.

Listando caixas

tipo, linhas = cliente.list()
if tipo != "OK":
    raise RuntimeError("não foi possível listar caixas")

for linha in linhas or []:
    print(linha.decode("utf-8", errors="replace"))

Os nomes podem usar codificação IMAP específica e separadores diferentes. Evite extrair campos por split() ingênuo. Bibliotecas de alto nível podem ser melhores quando você precisa de compatibilidade ampla com nomes internacionais.

Selecionando INBOX em modo somente leitura

tipo, dados = cliente.select("INBOX", readonly=True)
if tipo != "OK":
    raise RuntimeError("falha ao abrir INBOX")

quantidade = int(dados[0])
print("mensagens:", quantidade)

readonly=True reduz o risco de alterações. Para marcar, mover ou excluir, abra conscientemente com escrita e revalide a caixa.

Use UIDs, não números de sequência

Números de mensagem mudam quando a caixa muda, especialmente após EXPUNGE. UIDs são mais estáveis dentro da caixa.

tipo, dados = cliente.uid("search", None, "ALL")
if tipo != "OK":
    raise RuntimeError("busca falhou")

uids = dados[0].split()
print(uids[-10:])

UIDs não são identificadores globais eternos. Uma caixa recriada pode ter outro UIDVALIDITY. Sistemas de sincronização devem armazenar mailbox, UIDVALIDITY e UID.

Buscas IMAP

tipo, dados = cliente.uid(
    "search",
    None,
    "UNSEEN",
    "SINCE",
    "01-Jul-2026",
)

Critérios são interpretados pelo servidor. Datas usam formato IMAP e não incluem hora. Para assuntos ou remetentes, cuide de charset e quoting. Não monte critérios diretamente a partir de entrada não confiável sem validação.

Lendo headers sem marcar como lida

tipo, dados = cliente.uid(
    "fetch",
    uid,
    "(BODY.PEEK[HEADER.FIELDS (FROM TO SUBJECT DATE MESSAGE-ID)])",
)

BODY.PEEK busca dados sem adicionar \Seen em servidores compatíveis. BODY[] pode marcar a mensagem como lida. Teste o comportamento do provedor.

Interpretando o retorno de fetch

Uma resposta FETCH pode conter dados adicionais e respostas não solicitadas. A documentação alerta para não depender apenas de data[0][1]. Inspecione tuplas e bytes.

def literais_fetch(dados):
    for item in dados or []:
        if isinstance(item, tuple) and len(item) == 2:
            cabecalho, literal = item
            if isinstance(literal, bytes):
                yield cabecalho, literal

Limite o tamanho solicitado. Para mensagens grandes, busque apenas partes necessárias ou use partial() quando o servidor suportar.

Parseando uma mensagem

from email import policy
from email.parser import BytesParser

mensagem = BytesParser(policy=policy.default).parsebytes(raw_email)

print(mensagem.get("Subject"))
print(mensagem.get("From"))

Headers e corpos são dados não confiáveis. Não renderize HTML de e-mail sem sanitização. Nomes de anexos não devem virar caminhos locais diretamente.

Extraindo texto com limites

def partes_texto(mensagem, limite=1_000_000):
    total = 0
    for parte in mensagem.walk():
        if parte.get_content_maintype() == "multipart":
            continue
        if parte.get_content_disposition() == "attachment":
            continue
        conteudo = parte.get_payload(decode=True) or b""
        total += len(conteudo)
        if total > limite:
            raise ValueError("mensagem grande demais")
        yield parte.get_content_type(), conteudo

Decodifique usando charset declarado com fallback seguro. O guia de codecs no Python ajuda a tratar erros de encoding.

Salvando anexos com segurança

Gere um nome interno, aplique limite de tamanho, valide tipo real e armazene fora de diretórios executáveis. O header de filename pode conter path traversal, caracteres de controle ou nomes repetidos.

Use tempfile no Python durante validação e hashlib no Python para integridade e deduplicação.

Flags

Flags comuns incluem \Seen, \Answered, \Flagged, \Deleted e \Draft. Use UID STORE e operações silenciosas quando não precisar da resposta completa.

cliente.uid(
    "store",
    uid,
    "+FLAGS.SILENT",
    r"(\Seen)",
)

Antes de alterar, confirme que o UID pertence à mensagem esperada. Em automações, registre a decisão de domínio, não o conteúdo sensível.

Exclusão em duas etapas

Normalmente, excluir significa adicionar \Deleted e depois executar EXPUNGE. EXPUNGE pode remover todas as mensagens marcadas na caixa, inclusive por outro cliente.

cliente.uid("store", uid, "+FLAGS.SILENT", r"(\Deleted)")
# não chame expunge automaticamente sem política explícita

Quando disponível, extensões UIDPLUS ou MOVE oferecem operações mais previsíveis. Para encerrar sem expurgar, unselect() libera a caixa. close() pode remover mensagens marcadas em uma caixa gravável.

Copiar e mover

copy() copia mensagens. Um movimento legado costuma ser COPY, marcação \Deleted e EXPUNGE, o que exige cuidado. Se o servidor anuncia MOVE, use a extensão via uid("MOVE", ...) e teste o resultado.

IDLE no Python 3.14

idle() cria um context manager iterável que recebe notificações como EXISTS. Defina duração para evitar timeouts do servidor.

with cliente.idle(duration=29 * 60) as idler:
    for tipo, dados in idler:
        if tipo == "EXISTS":
            print("a caixa mudou", dados)

Notificação não substitui busca. Ao receber evento, execute uma sincronização por UID. Reconecte com backoff quando a conexão cair.

Timeout total e reconexão

O parâmetro timeout cobre a abertura da conexão, mas tarefas longas precisam de deadline total e política de reconexão. IMAP4.abort normalmente exige fechar e criar outra instância.

Não repita operações de escrita sem saber se o servidor as executou. Uma queda depois de STORE pode deixar estado incerto.

Tratando respostas

A maioria dos métodos retorna (tipo, dados), onde tipo costuma ser OK, NO ou BAD. Não considere ausência de exceção como sucesso.

tipo, dados = cliente.uid("search", None, "ALL")
if tipo != "OK":
    detalhe = dados[0] if dados else b"sem detalhe"
    raise RuntimeError(f"IMAP retornou {tipo}: {detalhe!r}")

Logs e privacidade

Não ative imaplib.Debug em produção. O protocolo pode revelar comandos, endereços, subjects e identificadores. Registre apenas caixa lógica, operação, duração, quantidade e um ID de correlação.

Testes recomendados

Teste certificado inválido, login rejeitado, caixa ausente, readonly, UIDs, UIDVALIDITY, mensagens multipart, charset inválido, anexos grandes, flags, reconexão, IDLE, resposta FETCH adicional e proteção contra EXPUNGE acidental.

Erros comuns

Os erros frequentes são não validar TLS, usar números de sequência, selecionar a caixa com escrita sem necessidade, buscar BODY[] e marcar e-mails, interpretar apenas o primeiro item de FETCH, salvar filenames de anexos, chamar EXPUNGE automaticamente, registrar conteúdo e repetir operações de escrita após timeout.

Conclusão

imaplib oferece controle detalhado de caixas IMAP, mas exige disciplina. Use IMAP4_SSL com contexto verificado, selecione readonly, prefira UIDs, use BODY.PEEK, limite mensagens e anexos, trate todas as respostas e mantenha exclusões como fluxo explicitamente autorizado.

Consulte a documentação oficial de imaplib e o RFC 9051 do IMAP4rev2. Para automações críticas, combine testes em conta isolada, idempotência, observabilidade e backups.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Conteúdo do artigo

    Artigos relacionados

    A developer typing code on a laptop with a Python book beside in an office.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    ftplib no Python: FTP e FTPS seguros

    Aprenda ftplib no Python para listar, baixar e enviar arquivos por FTP ou FTPS com TLS, timeouts, limites, retomada e

    Ler mais

    Tempo de leitura: 5 minutos
    21/08/2026
    Rack de servidores representando um endpoint criado com xmlrpc.server no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    xmlrpc.server: crie servidores XML-RPC

    Aprenda xmlrpc.server no Python para criar servidores XML-RPC, registrar funções, limitar métodos e caminhos e evitar exposição insegura.

    Ler mais

    Tempo de leitura: 6 minutos
    21/08/2026
    Cabos conectados a servidor representando chamadas remotas com xmlrpc.client no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    xmlrpc.client no Python: chamadas RPC

    Aprenda xmlrpc.client no Python para chamar serviços XML-RPC, tratar Fault e ProtocolError, usar TLS, tipos compatíveis e limites seguros.

    Ler mais

    Tempo de leitura: 4 minutos
    21/08/2026
    Código de erro sobre dados binários representando falhas tratadas com urllib.error no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    urllib.error no Python: trate falhas HTTP

    Aprenda urllib.error no Python para tratar URLError, HTTPError, downloads incompletos, retries seletivos e diagnósticos de rede mais claros.

    Ler mais

    Tempo de leitura: 6 minutos
    21/08/2026
    Teclas com a palavra HTML representando entidades HTML no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    html.entities: converta entidades HTML

    Aprenda html.entities no Python para consultar entidades HTML, converter nomes e code points e evitar confundir decoding com sanitização.

    Ler mais

    Tempo de leitura: 7 minutos
    21/08/2026
    Pasta com arquivos representando tipos MIME identificados com mimetypes no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    mimetypes: detecte tipos MIME de arquivos

    Aprenda mimetypes no Python para identificar tipos de arquivos, validar uploads e gerar headers HTTP com mais segurança.

    Ler mais

    Tempo de leitura: 7 minutos
    20/08/2026