🤖 A fully featured, easy to use Python wrapper for the Lovdata API
- ✨ Modular Architecture - Organized by API resource groups for clarity
- 🔄 Session & Request Handling - Automatic retries, connection pooling, and timeout management
- 🔐 Authentication & Security - Secure API key handling with X-API-Key header
- 🐍 Pythonic Interface - Context managers, type hints, and intuitive method names
- 🛡️ Error Handling - Comprehensive exception hierarchy for different error types
- 📊 Rate Limiting - Automatic tracking of API rate limits
- 📚 Well Documented - Complete docstrings and usage examples
- 🧪 Tested - Comprehensive test suite with pytest
- 🚀 Easy to Extend - Plugin architecture for adding new endpoints
pip install python-lovdataOr install from source:
git clone https://github.com/DiFronzo/python-lovdata.git
cd python-lovdata
pip install -e .from lovdata import LovdataAPI
# Initialize the API client
with LovdataAPI(api_key="your-api-key") as api:
# Check authentication
user_info = api.services.get_user_info()
print(f"Logged in as: {user_info['login']}")
# Search for documents
results = api.search.search(emne1="straffelov", rows=10)
print(f"Found {len(results)} results")
# Get document metadata
doc = api.content.get_document_meta("NL/lov/2005-05-20-28")
print(f"Document title: {doc.get('title')}")The library supports authentication via API key using the X-API-Key header:
from lovdata import LovdataAPI
# Using context manager (recommended)
with LovdataAPI(api_key="your-api-key") as api:
user_info = api.services.get_user_info()
print(f"User ID: {user_info['userID']}")
print(f"Company ID: {user_info['companyID']}")
# Without context manager
api = LovdataAPI(api_key="your-api-key")
try:
user_info = api.services.get_user_info()
finally:
api.close()Access document lists, metadata, and rendering:
# Get document metadata
doc_meta = api.content.get_document_meta("NL/lov/2005-05-20-28")
# Get document index (table of contents)
index = api.content.get_document_index("NL/lov/1997-02-28-19")
# Get document history
history = api.content.get_document_history("lov/2005-05-20-28")
# List documents in a legal source
docs = api.content.list_base("NL")
# Get change history for a legal source
changes = api.content.get_base_history(
base="NL",
from_date="2024-01-01",
to_date="2024-12-31",
limit=50
)
# Render document as HTML
html = api.content.render_ref_id("lov/2005-05-20-28/§4", format="html")
# Download document in various formats
pdf_content = api.content.download_document(
dok_id="NL/lov/2005-05-20-28",
file_type="pdf"
)
# Check if document exists
exists = api.content.lookup(ref_id="lov/2005-05-20-28")
# List all legal sources
sources = api.content.list_legal_sources()Get complete documents in structured HTML5/XML format:
# List available legal sources
bases = api.structured_rules.list_bases()
# List documents in a legal source
docs = api.structured_rules.list_documents("NL")
# Get a document in structured format
doc = api.structured_rules.get_document("NL", "lov-2005-05-20-28.xml")
# Get document version at specific date
doc_at_date = api.structured_rules.get_document_at_date(
"NL",
"lov-2005-05-20-28.xml",
"2024-01-01"
)
# Get document timeline (all versions)
timeline = api.structured_rules.get_timeline("NL", "lov-2005-05-20-28.xml")Perform full-text search across legal sources:
# Simple search
results = api.search.search(emne1="straffelov", rows=10)
# Advanced search with multiple terms
results = api.search.search(
emne1="straff",
emne2="bot",
base="NL",
from_date="2020-01-01",
to_date="2024-12-31",
rows=50,
offset=0,
sort_field="date",
sort_order="DESC"
)
# Search with text references
results = api.search.search(
teksthenvisning="§ 12",
base="NL"
)Miscellaneous utility functions:
# Ping service to check if it's running
status = api.services.ping()
# Get user information
user_info = api.services.get_user_info()
# Locate references in text (genref)
result = api.services.genref(
text="Se straffeloven § 4 og § 5",
dok_id="NL/lov/2005-05-20-28"
)
print(result['appliedString']) # Text with hyperlinks
print(result['model']['matches']) # Found referencesAccess public data under NLOD 2.0 license:
# List available public data packages
packages = api.public_data.list()
# Download a public data file
data = api.public_data.get("filename.zip")
with open("output.zip", "wb") as f:
f.write(data)Advanced AI-powered search and text generation:
# Strategy search
results = api.ai.strategy_search(
text="What are the penalties for theft?",
strategy="LovSourcedReferenceGraphStrategy",
legal_areas="STRAFF"
)
# Generate AI response
responses = api.ai.generate_response(
query="Explain § 4 of the Penal Code",
section_ids=[12345, 12346],
message_history=[
{"type": "USER", "content": "Previous question"},
{"type": "SYSTEM", "content": "Previous answer"}
]
)The library provides comprehensive exception handling:
from lovdata import (
LovdataAPI,
AuthenticationError,
AuthorizationError,
NotFoundError,
RateLimitError,
ServerError,
NetworkError,
TimeoutError,
ValidationError,
)
try:
with LovdataAPI(api_key="your-api-key") as api:
doc = api.content.get_document_meta("NL/lov/2005-05-20-28")
except AuthenticationError:
print("Invalid API key")
except AuthorizationError:
print("Not authorized to access this resource")
except NotFoundError:
print("Document not found")
except RateLimitError:
print("Rate limit exceeded")
except ValidationError as e:
print(f"Invalid parameters: {e.message}")
except ServerError:
print("Server error occurred")
except NetworkError:
print("Network connection error")
except TimeoutError:
print("Request timed out")The library automatically tracks rate limit information:
api = LovdataAPI(api_key="your-api-key")
# Make some requests
results = api.search.search(emne1="test")
# Check rate limit info
rate_info = api.client.get_rate_limit_info()
print(f"Limit: {rate_info['limit']}")
print(f"Remaining: {rate_info['remaining']}")
print(f"Reset at: {rate_info['reset']}")
api.close()Customize client behavior:
from lovdata import LovdataAPI
api = LovdataAPI(
api_key="your-api-key",
base_url="https://api.lovdata.no", # Custom base URL
timeout=60, # Request timeout in seconds (default: 30)
max_retries=5, # Max retries for failed requests (default: 3)
backoff_factor=1.0, # Backoff factor for retries (default: 0.5)
verify_ssl=True, # SSL verification (default: True)
)For complete API documentation, see the Lovdata API Swagger documentation.
For XML format documentation, see Lovdata XML documentation.
# Clone the repository
git clone https://github.com/DiFronzo/python-lovdata.git
cd python-lovdata
# Install development dependencies
pip install -e ".[dev]"# Run all tests
pytest
# Run with coverage
pytest --cov=lovdata --cov-report=html
# Run specific test file
pytest tests/test_client.py# Format code with black
black lovdata tests
# Lint with ruff
ruff check lovdata tests
# Type check with mypy
mypy lovdataThe Lovdata API uses FRBR-based document identifiers:
-
refID: FRBR "work" - Can refer to multiple expressions of the same work- Example:
lov/2005-05-20-28(any version of the Penal Code)
- Example:
-
dokID: FRBR "expression" - Unique identifier for a specific version- Example:
NL/lov/2005-05-20-28(consolidated Penal Code) - Example:
LTI/lov/2005-05-20-28(originally promulgated version) - Example:
NLE/lov/2005-05-20-28(English version)
- Example:
Common legal source abbreviations:
NL- Norwegian Laws (Norsk Lovtidend)SF- Government Regulations (Sentral forskrift)LF- Local Regulations (Lokal forskrift)DEL- Delegated legislationINS- InstructionsSTV- Stortinget (Parliament) decisions
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
This project is licensed under the MIT License - see the LICENSE file for details.
- Lovdata for providing the API
- Documentation based on the Lovdata API Swagger
For API-related questions, contact Lovdata API tech support at api@lovdata.no
For library issues, please open an issue on GitHub.