Validar e formatar números de telefone no Brasil é um dos desafios mais comuns em cadastros, checkouts e formulários de autenticação via SMS/WhatsApp. Embora pareça uma tarefa simples, o sistema telefônico brasileiro possui regras rígidas estabelecidas pela Anatel, envolvendo DDDs geográficos, o nono dígito obrigatório para celulares e a diferenciação entre linhas móveis e fixas.
Neste guia prático, vamos entender a anatomia dos números de telefone no Brasil, como validar DDDs reais, aplicar máscaras dinâmicas e implementar expressões regulares (Regex) robustas em JavaScript/TypeScript e Python.
1. A Anatomia do Telefone no Brasil
Um número de telefone brasileiro completo no padrão internacional (E.164) é composto por três partes:
- DDI (Código do País):
+55para o Brasil. - DDD (Código de Área Nacional): 2 dígitos (de
11a99, com lacunas que não existem). - Número do Assinante:
- Telefonia Móvel (Celular): 9 dígitos (ex:
9XXXX-XXXX). O primeiro dígito é sempre o número 9, e o segundo dígito varia entre 6, 7, 8 ou 9. - Telefonia Fixa: 8 dígitos (ex:
[2-5]XXX-XXXX). O primeiro dígito começa entre 2 e 5.
- Telefonia Móvel (Celular): 9 dígitos (ex:
2. Lista de DDDs Válidos no Brasil
Nem todas as combinações de dois dígitos são DDDs válidos. Existem exatamente 67 DDDs ativos no Brasil divididos por regiões:
| Região | Estados | DDDs Válidos |
|---|---|---|
| Sudeste | SP, RJ, MG, ES | 11 a 19 (SP), 21, 22, 24 (RJ), 27, 28 (ES), 31 a 35, 37, 38 (MG) |
| Sul | PR, SC, RS | 41 a 46 (PR), 47 a 49 (SC), 51, 53 a 55 (RS) |
| Centro-Oeste | DF, GO, TO, MT, MS | 61 (DF/GO), 62, 64 (GO), 63 (TO), 65, 66 (MT), 67 (MS) |
| Nordeste | BA, SE, PE, AL, PB, RN, CE, PI, MA | 71, 73 a 75, 77 (BA), 79 (SE), 81, 87 (PE), 82 (AL), 83 (PB), 84 (RN), 85, 88 (CE), 86, 89 (PI), 98, 99 (MA) |
| Norte | PA, AM, RR, AP, AC, RO | 91, 93, 94 (PA), 92, 97 (AM), 95 (RR), 96 (AP), 68 (AC), 69 (RO) |
3. Expressão Regular (Regex) para Validação Completa
Abaixo está o padrão Regex rigoroso que aceita números com ou sem pontuação (parênteses, espaços e hifens), aceitando tanto telefones fixos (8 dígitos) quanto celulares com o 9º dígito obrigatório:
// Aceita formatos: (11) 99999-9999, 11999999999, (11) 3333-3333, 1133333333
const PHONE_BR_REGEX = /^(?:(?:+|00)?55s?)?(?:(?([1-9][0-9]))?s?)?(?:((?:9d|[2-5])d{3})-?(d{4}))$/;
4. Implementação em TypeScript / JavaScript
A função abaixo limpa caracteres não numéricos, valida a existência do DDD e formata o número com a máscara correta:
const VALID_DDDS = new Set([
11, 12, 13, 14, 15, 16, 17, 18, 19,
21, 22, 24, 27, 28,
31, 32, 33, 34, 35, 37, 38,
41, 42, 43, 44, 45, 46, 47, 48, 49,
51, 53, 54, 55,
61, 62, 63, 64, 65, 66, 67, 68, 69,
71, 73, 74, 75, 77, 79,
81, 82, 83, 84, 85, 86, 87, 88, 89,
91, 92, 93, 94, 95, 96, 97, 98, 99
]);
export function validateAndFormatPhone(phone: string): { isValid: boolean; formatted: string; type?: 'mobile' | 'landline' } {
// Remove tudo que não for dígito
let clean = phone.replace(/\D/g, '');
// Remove DDI 55 inicial se existir
if (clean.length > 11 && clean.startsWith('55')) {
clean = clean.substring(2);
}
// Celular (11 dígitos) ou Fixo (10 dígitos)
if (clean.length !== 10 && clean.length !== 11) {
return { isValid: false, formatted: phone };
}
const ddd = parseInt(clean.substring(0, 2), 10);
if (!VALID_DDDS.has(ddd)) {
return { isValid: false, formatted: phone };
}
const numberPart = clean.substring(2);
// Validação de Celular: 9 dígitos iniciando com 9 e segundo dígito [6-9]
if (clean.length === 11) {
if (numberPart[0] !== '9' || !['6', '7', '8', '9'].includes(numberPart[1])) {
return { isValid: false, formatted: phone };
}
const formatted = `(${ddd}) ${numberPart.substring(0, 5)}-${numberPart.substring(5)}`;
return { isValid: true, formatted, type: 'mobile' };
}
// Validação de Fixo: 8 dígitos iniciando com [2-5]
if (!['2', '3', '4', '5'].includes(numberPart[0])) {
return { isValid: false, formatted: phone };
}
const formatted = `(${ddd}) ${numberPart.substring(0, 4)}-${numberPart.substring(4)}`;
return { isValid: true, formatted, type: 'landline' };
}
5. Implementação em Python
Para pipelines de backend ou rotinas de limpeza de dados em Python:
import re
VALID_DDDS = {
11, 12, 13, 14, 15, 16, 17, 18, 19,
21, 22, 24, 27, 28,
31, 32, 33, 34, 35, 37, 38,
41, 42, 43, 44, 45, 46, 47, 48, 49,
51, 53, 54, 55,
61, 62, 63, 64, 65, 66, 67, 68, 69,
71, 73, 74, 75, 77, 79,
81, 82, 83, 84, 85, 86, 87, 88, 89,
91, 92, 93, 94, 95, 96, 97, 98, 99
}
def validate_brazilian_phone(phone_str: str) -> dict:
digits = re.sub(r'\D', '', phone_str)
if len(digits) > 11 and digits.startswith('55'):
digits = digits[2:]
if len(digits) not in (10, 11):
return {"valid": False, "error": "Tamanho de dígitos inválido"}
ddd = int(digits[:2])
if ddd not in VALID_DDDS:
return {"valid": False, "error": f"DDD {ddd} inexistente no Brasil"}
number = digits[2:]
if len(digits) == 11:
if number[0] != '9' or number[1] not in '6789':
return {"valid": False, "error": "Celular deve iniciar com 9 seguido de [6-9]"}
formatted = f"({ddd}) {number[:5]}-{number[5:]}"
return {"valid": True, "formatted": formatted, "type": "mobile", "e164": f"+55{digits}"}
# Telefone fixo (10 dígitos)
if number[0] not in '2345':
return {"valid": False, "error": "Telefone fixo deve iniciar com [2-5]"}
formatted = f"({ddd}) {number[:4]}-{number[4:]}"
return {"valid": True, "formatted": formatted, "type": "landline", "e164": f"+55{digits}"}
6. Gerando Números Válidos para Testes em Staging
Se você precisa de números de celular ou fixo matematicamente válidos por estado para popular seus testes automatizados no Cypress, Playwright ou Selenium sem usar números de pessoas reais, utilize o nosso Gerador de Telefone Online no DevThru.
🛠️ Experimente na prática
Use nossas ferramentas online gratuitas — sem cadastro, direto no navegador.
