validators
validators is a collection of small Python functions for checking one value at a time without defining a form or schema. It covers email addresses, URLs, domains, IP addresses, UUIDs, hashes, payment-card formats, country and currency codes, cron strings, slugs, lengths, numeric ranges, and more. A successful check returns True; a failed check returns a false-valued ValidationError object that records the function and arguments, or can be configured to raise.
A good grab bag for small, explicit format checks, especially when a schema library would be ceremony. Do not confuse syntactic acceptance with real-world validity, and normalize its unusual True-or-ValidationError result at your application boundary.
Use it if
- You need a readable one-line check for common string and identifier formats without bringing in a schema system
- You want failure objects that retain which validator and arguments failed while still working in boolean conditions
- You need configurable URL, hostname, domain, or IP checks, including private-address and TLD restrictions
- You are writing a few boundary checks in an existing Python application that already owns normalization and error messages
- You need request, model, or configuration validation with nested structures, coercion, field paths, and collected errors: this package explicitly avoids schemas
- You need proof that an email inbox, domain, card, or account exists: these functions check syntax and known formats, not DNS, deliverability, ownership, or authorization
- Your code assumes validators return plain booleans: failures are ValidationError instances with false truthiness, so identity checks against False and JSON serialization do not behave like a bool
- You need stable public validation policy across services: options such as consider_tld, strict_query, private, simple_host, and RFC flags materially change acceptance, and defaults may not match your product rules
- You want a package declared production-stable: PyPI still classifies 0.35.0 as Beta, and the README currently points documentation to a separately owned site while saying the original versioned docs will return later
Setup reality
`pip install validators` is enough for the main validators. Version 0.35.0 requires Python 3.9 or newer and has no mandatory runtime dependency; Ethereum address validation is the exception and is exposed through the `crypto-eth-addresses` extra, which installs `eth-hash[pycryptodome]`. The first surprise is the result type. Valid input returns the literal True, while invalid input returns a `ValidationError` instance whose `__bool__` method is False. `if validators.email(value)` works, but `validators.email(value) is False` does not, and returning the result directly from a JSON API will expose a non-serializable object. Keep the result when you need its `func`, arguments, or reason. Pass `r_ve=True` to any decorated validator to raise on failure, or set `RAISE_VALIDATION_ERROR=True` to change behavior process-wide; the environment switch is convenient but can silently alter third-party code in the same process. These are format checks, not normalizers. Trim input, decide Unicode policy, and store canonical forms yourself. URL and host validation needs explicit product choices about ports, local hosts, private IP addresses, valid TLDs, strict query parsing, and trailing dots. Email validation does not verify DNS or delivery. Card checks do not make handling card data compliant. Finally, Python 3.9 is accepted by the package even though the README calls out that version's end of life, so your application runtime policy may need to be stricter than the package metadata.
Patterns
Check an email addressvalidate-email
import validators
result = validators.email('someone@example.com')
if result:
accept_address()
else:
reject_address(str(result))This checks format only. A false result is a ValidationError object, not the literal False, and no DNS or mailbox check occurs.
Raise a validation exceptionraise-on-invalid
import validators
from validators import ValidationError
try:
validators.email('bogus@@', r_ve=True)
except ValidationError as error:
print(error.func.__name__, error.value)The r_ve keyword is consumed by the decorator and works for every validator wrapped by the package.
Require an HTTP or HTTPS URLvalidate-url
import validators
def http_scheme(scheme: str) -> bool:
return scheme in {'http', 'https'}
valid = validators.url(
'https://example.com/docs?q=python',
validate_scheme=http_scheme,
)The default scheme validator accepts more than web URLs; pass product policy explicitly when only HTTP and HTTPS belong.
Reject URLs containing private IP addressesreject-private-url
import validators
result = validators.url(
'http://192.168.1.10/admin',
private=False,
)
assert not resultThis option applies to embedded IP literals, not DNS resolution. It is not by itself an SSRF defense because a hostname can resolve to a private address later.
Require a domain with a recognized TLDvalidate-domain
import validators
result = validators.domain('example.com', consider_tld=True)
assert result is Trueconsider_tld checks the package's IANA-derived list; leave it off for internal suffixes and test domains that are valid only in your network.
Require a private IPv4 addressvalidate-private-ipv4
import validators
assert validators.ipv4('10.2.3.4', private=True) is True
assert not validators.ipv4('8.8.8.8', private=True)The private option includes local, loopback, and broadcast categories documented by the implementation, so confirm that grouping matches your security rule.
Check a UUID valuevalidate-uuid
from uuid import UUID
import validators
assert validators.uuid('2bc1c94f-0deb-43e9-92a1-4775189ec9f8') is True
assert validators.uuid(UUID('2bc1c94f-0deb-43e9-92a1-4775189ec9f8')) is TrueThe validator accepts UUID objects and UUID-shaped strings; its docstring calls this UUID v4, but the implementation constructs UUID without enforcing a version field.
Check a lowercase URL slugvalidate-slug
import validators
assert validators.slug('release-notes-2026') is True
assert not validators.slug('Release Notes')The accepted grammar is lowercase ASCII letters and digits separated by single hyphens; it does not create or normalize a slug.
Constrain string lengthvalidate-length
import validators
result = validators.length('display name', min_val=3, max_val=40)
assert result is TrueLength uses Python len, so it counts Unicode code points rather than bytes or user-perceived grapheme clusters.
Check a comparable value rangevalidate-range
from datetime import datetime, timezone
import validators
result = validators.between(
datetime.now(timezone.utc),
min_val=datetime(2026, 1, 1, tzinfo=timezone.utc),
)Both bounds are inclusive. Mixing incomparable types or naive and aware datetimes produces a captured ValidationError unless raising mode is enabled.
Check a five-field cron expressionvalidate-cron
import validators
assert validators.cron('0 2 * * 1-5') is True
assert not validators.cron('30-20 * * * *')Format validity does not guarantee that a specific scheduler supports every token or interprets weekdays and time zones the same way.
Create a validator with the same result contractdefine-custom-validator
from validators import validator
@validator
def even(value: int):
return value % 2 == 0
assert even(4) is True
failure = even(5)
assert not failureThe decorator converts false results and common value, type, or Unicode errors into false-valued ValidationError objects.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| pydantic | PyPI | You need typed models, coercion, nested schemas, reusable field rules, and structured error locations |
| email-validator | PyPI | Email addresses are the main concern and you need normalization plus optional DNS deliverability checks |
| rfc3986 | PyPI | You need URI parsing and RFC-focused validation with components you can normalize and inspect |