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

    Desenvolvedor configurando servidor HTTPS e certificado TLS com Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    HTTPSServer: crie servidor HTTPS local no Python

    Aprenda HTTPSServer no Python para servir HTTPS localmente, configurar certificados, usar threads e entender limites de segurança.

    Ler mais

    Tempo de leitura: 5 minutos
    08/10/2026
    Arquivos protegidos representando extração segura de TAR com Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    tarfile extraction_filter: extraia TAR com segurança

    Aprenda tarfile extraction_filter no Python para extrair arquivos TAR com validação, segurança e controle de caminhos.

    Ler mais

    Tempo de leitura: 6 minutos
    08/10/2026
    Código Python exibindo avisos controlados com catch_warnings
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    catch_warnings: capture warnings em testes Python

    Aprenda catch_warnings no Python para capturar, testar e controlar avisos com filtros específicos e segurança.

    Ler mais

    Tempo de leitura: 5 minutos
    07/10/2026
    Código Python representando referências persistentes do pickle
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    pickle persistent_id: serialize referências externas

    Aprenda pickle persistent_id no Python para serializar referências externas com IDs estáveis, validação, segurança e compatibilidade.

    Ler mais

    Tempo de leitura: 5 minutos
    07/10/2026
    Código binário representando buffers e memória no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    memoryview.count: conte valores sem copiar buffers

    Aprenda memoryview.count no Python para contar bytes e valores em buffers sem cópias, com formatos, limites e boas práticas.

    Ler mais

    Tempo de leitura: 6 minutos
    06/10/2026
    Código Python com anotações de tipos
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    annotationlib: evite imports circulares em anotações

    Aprenda annotationlib no Python para ler anotações adiadas, evitar imports circulares e criar ferramentas de introspecção seguras.

    Ler mais

    Tempo de leitura: 6 minutos
    06/10/2026