FAQ¶
Frequently asked questions about RepeaterBook.
General Questions¶
What is RepeaterBook?¶
RepeaterBook is a Python library that provides programmatic access to the RepeaterBook.com database of amateur radio repeaters worldwide. It allows you to download, query, and analyze repeater data for various amateur radio applications.
Do I need an API key?¶
No! The RepeaterBook API is public and doesn't require authentication or an API key. However, please be respectful of their servers by:
- Using the built-in caching (enabled by default)
- Not making excessive requests
- Respecting the cache TTL
What data is available?¶
RepeaterBook provides comprehensive repeater data including:
- Frequencies (input/output)
- Location (lat/lon)
- Callsign and trustee
- Access tones (CTCSS/DCS)
- Digital mode capabilities (DMR, P25, NXDN, etc.)
- Network affiliations
- Status and access type
- Notes and additional information
Is the data accurate?¶
The data comes directly from RepeaterBook.com, which is community-maintained. Accuracy varies by region and how recently the information was updated. Always verify critical information (especially for emergency communications) through local sources.
Which countries are supported?¶
RepeaterBook covers repeaters worldwide. Major coverage includes:
- North America: USA, Canada, Mexico
- Europe: Most European countries
- Asia: Japan, South Korea, Taiwan, and others
- Oceania: Australia, New Zealand
- South America: Brazil, Argentina, Chile, and others
- Africa: South Africa and others
Use pycountry to find the correct country codes.
Installation Issues¶
ModuleNotFoundError: No module named 'repeaterbook'¶
Make sure you've installed the package:
If using a virtual environment, ensure it's activated:
ImportError with SQLModel or aiohttp¶
These are dependencies that should be installed automatically. Try:
Or install dependencies explicitly:
SSL Certificate Errors¶
On some systems (especially macOS), you may encounter SSL errors. Install certificates:
# macOS
/Applications/Python\ 3.x/Install\ Certificates.command
# Or install certifi
pip install --upgrade certifi
Usage Issues¶
asyncio.run() gives "Event loop is closed" error¶
This happens when running in Jupyter notebooks. Use this pattern instead:
# In Jupyter/IPython
await api.download(query=ExportQuery(countries={brazil}))
# In regular Python scripts
import asyncio
asyncio.run(api.download(query=ExportQuery(countries={brazil})))
Database file is locked¶
This occurs when multiple processes access the same database. Solutions:
- Use different database files for concurrent access
- Close connections properly with context managers
- Use a single RepeaterBook instance per database file
# Good
rb = RepeaterBook(database="repeaters.db")
results1 = rb.query(...)
results2 = rb.query(...)
# Bad (multiple instances to same file)
rb1 = RepeaterBook(database="repeaters.db")
rb2 = RepeaterBook(database="repeaters.db") # May cause lock
Queries return empty results¶
Check:
- Data exists: Have you populated the database?
# Check if database has data
all_repeaters = rb.query()
print(f"Database has {len(all_repeaters)} repeaters")
- Query conditions are correct:
# Use Status enum, not strings
from repeaterbook.models import Status
results = rb.query(Repeater.operational_status == Status.ON_AIR)
- Geographic bounds are reasonable:
Cache not working¶
The cache should work automatically. To debug:
# Check cache directory
api = RepeaterBookAPI()
print(f"Cache dir: {api.cache_dir}")
# Verify cache files exist
import os
if os.path.exists(".repeaterbook_cache"):
files = os.listdir(".repeaterbook_cache")
print(f"Cache files: {files}")
To clear the cache:
Distance calculations seem wrong¶
Verify:
- Units: Default is kilometers
# Use miles instead
from haversine import Unit
radius = Radius(origin=location, distance=50, unit=Unit.MILES)
- Coordinates: Ensure lat/lon are correct
# Check repeater coordinates
for rep in results:
print(f"{rep.callsign}: {rep.latitude}, {rep.longitude}")
- Use filter_radius() after square():
Performance Issues¶
Download is very slow¶
- Check internet connection: API depends on network speed
- Use cache: Subsequent requests use cached data
- Limit scope: Download specific states/regions instead of entire countries
# Instead of entire USA
query = ExportQuery(countries={usa}) # Slow!
# Download specific states
query = ExportQuery(countries={usa}, state_ids={"06"}) # California (FIPS code)
Queries are slow¶
- Use square() before filter_radius():
# Efficient
candidates = rb.query(square(radius)) # Fast: SQL indexed
nearby = filter_radius(candidates, radius) # Slower: but fewer items
# Inefficient
all_reps = rb.query() # Gets everything
nearby = filter_radius(all_reps, radius) # Slow: calculates all distances
- Add query conditions to limit results:
# Add more filters to reduce result set
results = rb.query(
square(radius),
Repeater.operational_status == Status.ON_AIR, # Filters out off-air
band(Bands.M_2) # Only 2m band
)
- Use LIMIT for large result sets:
from sqlmodel import select
statement = select(Repeater).limit(100)
with rb.session as session:
results = session.exec(statement).all()
Database is getting large¶
The SQLite database can grow with many repeaters. To optimize:
# Compact database
import sqlite3
conn = sqlite3.connect('repeaterbook.db')
conn.execute('VACUUM')
conn.close()
Or start fresh:
API Questions¶
What's the difference between export.php and exportROW.php?¶
- export.php: North America (USA, Canada, Mexico)
- exportROW.php: Rest of World (all other countries)
The library automatically selects the correct endpoint based on your query.
How often should I refresh data?¶
Repeater data doesn't change frequently. Recommended refresh intervals:
- Active development: 1 hour (default cache TTL)
- Production apps: 24 hours or longer
- Static analysis: Download once, use indefinitely
from datetime import timedelta
# Set longer cache for production
api = RepeaterBookAPI(max_cache_age=timedelta(hours=24))
Can I download all repeaters worldwide?¶
Yes, but it's a lot of data and takes time. Consider:
import pycountry
# All countries (slow, large)
all_countries = set(pycountry.countries)
# Download in batches...
Better approach: Download by region as needed.
Rate limiting?¶
RepeaterBook doesn't publicly document rate limits, but be respectful:
- ✅ Use caching (enabled by default)
- ✅ Download once, query many times
- ✅ Download specific regions, not everything
- ❌ Don't make rapid-fire requests
- ❌ Don't abuse the API
Data Questions¶
Why are some repeaters missing expected fields?¶
Not all repeaters have complete information. Always check for None:
if rep.dmr_id:
print(f"DMR ID: {rep.dmr_id}")
else:
print("DMR ID not available")
# Or use getattr with default
dmr_id = getattr(rep, 'dmr_id', 'Unknown')
How do I handle unknown/missing coordinates?¶
Some repeaters have imprecise or missing coordinates:
# Filter for precise coordinates
results = rb.query(Repeater.precise == True)
# Check before using
if rep.latitude and rep.longitude:
# Use coordinates
pass
What does "UNKNOWN" status mean?¶
Status.UNKNOWN means the operational status hasn't been verified or reported. It doesn't necessarily mean the repeater is off-air.
For critical applications, prefer:
Why is DMR color code sometimes None?¶
Not all DMR repeaters report their color code. Note that the dmr_color_code field is a string, not an integer. Common defaults:
- Color Code 1: Most common default
- Color Code 2: Also common
When programming radios, try CC1 first if unknown.
How are analog and digital flags set?¶
These are capability flags:
analog_capable=True: Supports FM analogdmr_capable=True: Supports DMRapco_p_25_capable=True: Supports P25- etc.
A repeater can have multiple flags (e.g., dual-mode).
Integration Questions¶
Can I use this with Flask/Django/FastAPI?¶
Yes! See the Examples page for a Flask integration example.
Key considerations:
- Initialize once: Create
RepeaterBookinstance at startup - Async support: FastAPI works great with async/await
- Database per app: Don't share database files across applications
Can I export to CSV/JSON?¶
Yes:
import pandas as pd
import json
results = rb.query(...)
# CSV with pandas
df = pd.DataFrame([r.model_dump() for r in results])
df.to_csv('repeaters.csv', index=False)
# JSON
data = [r.model_dump() for r in results]
with open('repeaters.json', 'w') as f:
json.dump(data, f, indent=2, default=str)
Can I use this in a mobile app?¶
The library is designed for Python. For mobile apps:
- Python backend: Create a REST API using Flask/FastAPI
- Direct integration: Use frameworks like Kivy or BeeWare
- Alternative: Use RepeaterBook API directly in your mobile code
How do I integrate with radio programming software?¶
Most radio programming software accepts CSV imports. See the codeplug example for details.
Development Questions¶
How do I contribute?¶
See CONTRIBUTING.md for detailed guidelines.
Quick start:
- Fork the repository
- Clone your fork
- Create a feature branch
- Make changes
- Run tests:
pytest --cov - Submit a pull request
How do I run tests?¶
# Install dev dependencies
uv sync --all-extras
# Run tests
pytest
# With coverage
pytest --cov --cov-report=html
# Run specific test
pytest tests/test_repeaterbook.py
How do I build documentation?¶
Where do I report bugs?¶
Please report bugs on GitHub Issues.
Include:
- Python version
- Operating system
- Error message and stack trace
- Minimal code to reproduce
Error Handling¶
What exceptions does RepeaterBook raise?¶
RepeaterBook uses a hierarchy of custom exceptions:
| Exception | When Raised |
|---|---|
RepeaterBookError |
Base exception for all library errors |
RepeaterBookAPIError |
API returned an error response (status: "error") |
RepeaterBookValidationError |
Invalid data or response format |
RepeaterBookCacheError |
Cache read/write operations failed |
How do I handle errors properly?¶
from repeaterbook import (
RepeaterBookError,
RepeaterBookAPIError,
RepeaterBookValidationError,
)
try:
repeaters = await api.download(query=query)
except RepeaterBookAPIError as e:
print(f"API error: {e}")
except RepeaterBookValidationError as e:
print(f"Invalid data: {e}")
except RepeaterBookError as e:
print(f"Library error: {e}")
Why did I get a validation error?¶
The Repeater model validates data automatically:
- Latitude must be between -90 and 90
- Longitude must be between -180 and 180
- Frequency must be positive
If you're seeing validation errors, the data from the API may be malformed.
Troubleshooting¶
Enable debug logging¶
from loguru import logger
import sys
logger.add(sys.stdout, level="DEBUG")
# Now see detailed logs
repeaters = await api.download(query=ExportQuery(countries={brazil}))
Check database contents¶
sqlite3 repeaterbook.db
# List tables
.tables
# Show schema
.schema repeater
# Query data
SELECT COUNT(*) FROM repeater;
SELECT * FROM repeater LIMIT 5;
Verify API response¶
import aiohttp
import asyncio
async def test_api():
url = "https://www.repeaterbook.com/api/export.php?country=Brazil"
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
print(f"Status: {response.status}")
data = await response.json()
print(f"Repeaters: {len(data['results'])}")
asyncio.run(test_api())
Still Having Issues?¶
- Check the Examples page for working code
- Review the API Reference for detailed documentation
- Search GitHub Issues
- Ask for help on Discord