Fetch CEP information from multiple providers concurrently
Project description
cep-fetch
A Python library for fetching Brazilian postal codes (CEP) from multiple providers concurrently, automatically returning the fastest response.
Features
- Concurrent execution: Queries all providers simultaneously and returns the fastest response
- Automatic fallback: If one provider fails, others continue running
- Task cancellation: Automatically cancels pending requests when first provider responds
- Multiple providers: Supports ViaCEP, BrasilAPI, OpenCEP, and ApiCEP
- Type safe: Full type hints support
- Easy to extend: Simple interface for adding custom providers
- Well tested: Comprehensive test suite with 90%+ coverage
Installation
pip install cep-fetch
Or using uv:
uv add cep-fetch
Quick Start
import asyncio
from cep_fetch import CepFetch
async def main():
cep_fetch = CepFetch()
result = await cep_fetch.search('01001000')
print(f"CEP: {result.cep}")
print(f"State: {result.state}")
print(f"City: {result.city}")
print(f"Neighborhood: {result.neighborhood}")
print(f"Street: {result.street}")
print(f"Provider: {result.service}")
asyncio.run(main())
Output:
CEP: 01001-000
State: SP
City: São Paulo
Neighborhood: Sé
Street: Praça da Sé
Provider: viacep
Usage Examples
Basic Usage
from cep_fetch import CepFetch
async def fetch_address():
cep_fetch = CepFetch()
result = await cep_fetch.search('01001000')
return result
Using Specific Providers
You can specify which providers to use:
from cep_fetch import CepFetch
from cep_fetch.providers import ViaCepProvider, BrasilApiProvider
async def fetch_with_specific_providers():
# Only use ViaCEP and BrasilAPI
cep_fetch = CepFetch(providers=[
ViaCepProvider(),
BrasilApiProvider()
])
result = await cep_fetch.search('01001000')
return result
Error Handling
from cep_fetch import CepFetch
async def fetch_with_error_handling():
cep_fetch = CepFetch()
try:
result = await cep_fetch.search('99999999')
print(f"Found: {result.city}")
except ValueError as e:
print(f"Error: {e}")
# Error: CEP 99999999 não encontrado em nenhum provider. Detalhes: ...
Different CEP Formats
The library automatically handles different CEP formats:
from cep_fetch import CepFetch
async def fetch_different_formats():
cep_fetch = CepFetch()
# All these formats work
result1 = await cep_fetch.search('01001000') # Without dash
result2 = await cep_fetch.search('01001-000') # With dash
result3 = await cep_fetch.search('01.001-000') # With dots and dash
Available Providers
The library includes four providers out of the box:
| Provider | URL | Format |
|---|---|---|
| ViaCEP | https://viacep.com.br | Free, no rate limit |
| BrasilAPI | https://brasilapi.com.br | Free, no rate limit |
| OpenCEP | https://opencep.com | Free, no rate limit |
| ApiCEP | https://apicep.com | Free, no rate limit |
Creating a Custom Provider
You can easily create custom providers by implementing the Provider interface:
from aiohttp import ClientSession
from cep_fetch import Provider, CepResult
class MyCustomProvider(Provider):
"""Custom CEP provider."""
@property
def name(self) -> str:
"""Return provider identifier."""
return 'my_custom_provider'
async def get_cep(self, session: ClientSession, cep: str) -> CepResult:
"""
Fetch CEP from custom API.
Args:
session: aiohttp ClientSession for making requests
cep: The CEP to search (may contain formatting)
Returns:
CepResult with normalized data
Raises:
Exception if CEP not found or request fails
"""
# Clean CEP (remove formatting)
clean_cep = self._clean_cep(cep)
# Make request to your API
url = f'https://api.example.com/cep/{clean_cep}'
async with session.get(url) as response:
response.raise_for_status()
data = await response.json()
# Map response to CepResult
return CepResult(
cep=data['zipcode'],
state=data['state'],
city=data['city'],
neighborhood=data.get('district'),
street=data.get('street'),
service=self.name
)
Using Your Custom Provider
from cep_fetch import CepFetch
from cep_fetch.providers import ViaCepProvider, BrasilApiProvider
async def use_custom_provider():
# Use only your custom provider
cep_fetch = CepFetch(providers=[MyCustomProvider()])
result = await cep_fetch.search('01001000')
# Or combine with existing providers
cep_fetch = CepFetch(providers=[
MyCustomProvider(),
ViaCepProvider(),
BrasilApiProvider()
])
result = await cep_fetch.search('01001000')
How It Works
- Concurrent Execution: All providers are queried simultaneously using asyncio tasks
- First Response Wins: The first provider to successfully respond returns the result
- Automatic Cancellation: All pending requests are automatically cancelled once a response is received
- Error Aggregation: If all providers fail, a detailed error message with all failures is returned
Provider 1 -----> [Response in 150ms] -----> Cancelled
Provider 2 -----> [Response in 80ms] -----> RETURNED (fastest)
Provider 3 -----> [Response in 200ms] -----> Cancelled
Provider 4 -----> [Error] -----> Logged
API Reference
CepFetch
Main class for fetching CEP data.
Constructor
CepFetch(providers: List[Provider] | None = None)
Parameters:
providers(optional): List of Provider instances to use. If not provided, uses all default providers.
Methods
search
async def search(cep: str) -> CepResult
Search for CEP information across all providers concurrently.
Parameters:
cep: Brazilian postal code (with or without formatting)
Returns:
CepResult: Result from the fastest responding provider
Raises:
ValueError: If all providers fail to find the CEP
CepResult
Dataclass containing CEP information.
Attributes:
cep(str): The postal codestate(str): State abbreviation (e.g., 'SP')city(str): City nameneighborhood(str | None): Neighborhood namestreet(str | None): Street nameservice(str): Provider that returned the result
Provider
Abstract base class for implementing custom providers.
Methods to implement:
name(property): Returns provider identifier stringget_cep(session, cep): Async method that fetches and returns CepResult
Helper methods:
_clean_cep(cep): Removes formatting from CEP string
Development
Setup
# Clone repository
git clone https://github.com/tiagovit/cep-fetch.git
cd cep-fetch
# Install dependencies
uv sync --all-groups
# Or with pip
pip install -e ".[dev]"
Running Tests
# Run all tests
pytest
# Run with coverage
pytest --cov=src --cov-report=html
# Run specific test file
pytest tests/unit/test_cep_fetch.py
# Run specific test
pytest tests/unit/test_cep_fetch.py::TestCepFetchSearch::test_search_returns_fastest_provider_result
Code Quality
# Format code
ruff format src/
# Lint code
ruff check src/
# Fix linting issues
ruff check --fix src/
Requirements
- Python >= 3.11
- aiohttp >= 3.13.2
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
License
This project is licensed under the MIT License - see the LICENSE file for details.
Acknowledgments
This project was inspired by cep-promise, a similar library for Node.js.
Changelog
See CHANGELOG.md for a detailed history of changes.
Support
If you encounter any issues or have questions, please open an issue on GitHub.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file cep_fetch-0.1.1.tar.gz.
File metadata
- Download URL: cep_fetch-0.1.1.tar.gz
- Upload date:
- Size: 128.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.8.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
75ed854ba224be032f8bbb30fa62937309b1d0c2718e1ba00f97f66c45d0ce2d
|
|
| MD5 |
24322537c065f249bbc9b99722f7599f
|
|
| BLAKE2b-256 |
4459f8b2e5330079e6edc81888dfb1a638da22d83f152d061526723453c2c93f
|
File details
Details for the file cep_fetch-0.1.1-py3-none-any.whl.
File metadata
- Download URL: cep_fetch-0.1.1-py3-none-any.whl
- Upload date:
- Size: 9.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.8.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
992564c89a718198f73935585f791562825a3444406a955fb14cca11a33edace
|
|
| MD5 |
41afe2b3a1202fe5fd64303ab3b41132
|
|
| BLAKE2b-256 |
a32b6098e62756a03fa165faf76eee7257796808f189fa3c9cfa161a3bf0548e
|