Python SDK
Complete guide to using the HOLE Foundation API Python SDK.
Installation
With pip
$ pip install hole-foundation-api
With uv (Recommended for HOLE Foundation)
$ uv pip install hole-foundation-api
From Source
$ git clone https://github.com/The-HOLE-Foundation/hole-api.git $ cd hole-api/sdks/python $ uv pip install -e .
Quick Start
1 from hole_foundation_api import HoleFoundationClient 2 import os 3 4 client = HoleFoundationClient( 5 environment="https://api.theholefoundation.org", 6 token=os.environ["AUTH0_TOKEN"] 7 ) 8 9 # Make your first API call 10 health = client.health.check() 11 print(f"API Status: {health.status}")
Client Configuration
Basic Configuration
1 from hole_foundation_api import HoleFoundationClient 2 3 client = HoleFoundationClient( 4 environment="https://api.theholefoundation.org", 5 token="your-jwt-token" 6 )
Advanced Configuration
1 import httpx 2 from hole_foundation_api import HoleFoundationClient 3 4 # Custom HTTP client 5 custom_client = httpx.Client( 6 timeout=30.0, 7 limits=httpx.Limits(max_connections=100) 8 ) 9 10 client = HoleFoundationClient( 11 environment="https://api.theholefoundation.org", 12 token=os.environ["AUTH0_TOKEN"], 13 14 # Custom timeout (default: 60 seconds) 15 timeout=30, 16 17 # Additional headers 18 headers={ 19 "X-Client-Version": "1.0.0", 20 "X-Request-ID": lambda: str(uuid.uuid4()) 21 }, 22 23 # Custom HTTP client 24 httpx_client=custom_client 25 )
Async Client
1 from hole_foundation_api import AsyncHoleFoundationClient 2 3 async def main(): 4 client = AsyncHoleFoundationClient( 5 environment="https://api.theholefoundation.org", 6 token=os.environ["AUTH0_TOKEN"] 7 ) 8 9 health = await client.health.check() 10 print(f"API Status: {health.status}") 11 12 # Run with asyncio 13 import asyncio 14 asyncio.run(main())
Dynamic Token (Recommended)
For automatic token refresh:
1 from hole_foundation_api import HoleFoundationClient 2 import requests 3 import time 4 import os 5 6 class TokenManager: 7 def __init__(self): 8 self.token = None 9 self.expires_at = 0 10 11 def get_token(self) -> str: 12 # Return cached token if still valid 13 if self.token and time.time() < self.expires_at: 14 return self.token 15 16 # Fetch new token from Auth0 17 response = requests.post( 18 f"https://{os.environ['AUTH0_DOMAIN']}/oauth/token", 19 json={ 20 "grant_type": "client_credentials", 21 "client_id": os.environ["AUTH0_M2M_CLIENT_ID"], 22 "client_secret": os.environ["AUTH0_M2M_CLIENT_SECRET"], 23 "audience": "https://api.theholefoundation.org" 24 } 25 ) 26 27 data = response.json() 28 self.token = data["access_token"] 29 self.expires_at = time.time() + data["expires_in"] - 60 # Refresh 1 min early 30 31 return self.token 32 33 token_manager = TokenManager() 34 35 client = HoleFoundationClient( 36 environment="https://api.theholefoundation.org", 37 token=lambda: token_manager.get_token() 38 )
API Methods
Health
1 # Check API health 2 health = client.health.check() 3 print(health.status) # "healthy" 4 print(health.database) # "connected" 5 print(health.version) # "2.0.0"
Vector Search
1 # Search legal documents 2 results = client.vector_search.search( 3 query="FOIA exemptions for national security", 4 limit=10, 5 include_context=True, 6 filters={ 7 "jurisdiction": "federal", 8 "document_type": "statute" 9 } 10 ) 11 12 # Access results 13 for result in results.results: 14 print(result.title) 15 print(result.score) 16 print(result.excerpt) 17 18 # Get specific document 19 document = client.vector_search.get_document("doc-123") 20 print(document.content)
Transparency
1 # List jurisdictions 2 jurisdictions = client.transparency.list_jurisdictions( 3 limit=50, 4 type="state" # 'federal', 'state', or 'all' 5 ) 6 7 # Search rights of access 8 rights = client.transparency.search_rights( 9 query="public records", 10 jurisdiction="california", 11 limit=20 12 ) 13 14 # Search exemptions 15 exemptions = client.transparency.search_exemptions( 16 query="privacy", 17 jurisdiction="federal", 18 category="personal_information" 19 ) 20 21 # Get statute text 22 statute = client.transparency.get_statute("5-USC-552") 23 print(statute.full_text)
FOIA Dashboard
1 # Create FOIA request (requires authentication) 2 request = client.foia_dashboard.create_request( 3 jurisdiction="federal", 4 agency="Department of Justice", 5 subject="Freedom of Information Request", 6 description="Request for records regarding...", 7 requester_name="John Doe", 8 requester_email="john@example.com", 9 delivery_method="email" 10 ) 11 12 # List your requests 13 my_requests = client.foia_dashboard.list_requests( 14 status="pending", 15 limit=10 16 ) 17 18 # Get request details 19 request_details = client.foia_dashboard.get_request(request.id) 20 21 # Update request 22 updated = client.foia_dashboard.update_request( 23 request.id, 24 status="submitted", 25 notes="Request submitted to agency" 26 )
Chat
1 # Non-streaming chat 2 response = client.chat.send_message( 3 message="What is the Freedom of Information Act?", 4 stream=False 5 ) 6 print(response.message) 7 8 # Streaming chat 9 for chunk in client.chat.send_message( 10 message="Explain FOIA exemption (b)(6)", 11 stream=True 12 ): 13 print(chunk, end="", flush=True)
Error Handling
1 from hole_foundation_api.errors import TheholetruthApiError 2 3 try: 4 results = client.vector_search.search(query="test") 5 except TheholetruthApiError as error: 6 print(f"API Error: {error.status_code}") 7 print(f"Message: {error.message}") 8 print(f"Body: {error.body}") 9 except Exception as error: 10 print(f"Unknown error: {error}")
Type Hints
The SDK includes full type hints:
1 from hole_foundation_api import HoleFoundationClient 2 from hole_foundation_api.types import ( 3 ChatResponse, 4 SearchRequest, 5 SearchResponse, 6 Jurisdiction, 7 RightOfAccess, 8 Exemption, 9 FoiaRequest 10 ) 11 12 # Use types for better IDE support 13 def search_documents(query: str) -> SearchResponse: 14 client = HoleFoundationClient( 15 environment="https://api.thoholefoundation.org", 16 token=os.environ["AUTH0_TOKEN"] 17 ) 18 19 return client.vector_search.search(query=query, limit=10)
Framework Integration
Flask
1 from flask import Flask, request, jsonify 2 from hole_foundation_api import HoleFoundationClient 3 4 app = Flask(__name__) 5 6 client = HoleFoundationClient( 7 environment=os.environ["HOLE_API_URL"], 8 token=os.environ["AUTH0_M2M_TOKEN"] 9 ) 10 11 @app.route('/api/search', methods=['POST']) 12 def search(): 13 data = request.json 14 results = client.vector_search.search(query=data['query']) 15 return jsonify(results) 16 17 if __name__ == '__main__': 18 app.run()
FastAPI
1 from fastapi import FastAPI 2 from hole_foundation_api import HoleFoundationClient 3 from pydantic import BaseModel 4 5 app = FastAPI() 6 7 client = HoleFoundationClient( 8 environment=os.environ["HOLE_API_URL"], 9 token=os.environ["AUTH0_M2M_TOKEN"] 10 ) 11 12 class SearchRequest(BaseModel): 13 query: str 14 limit: int = 10 15 16 @app.post("/api/search") 17 async def search(request: SearchRequest): 18 results = client.vector_search.search( 19 query=request.query, 20 limit=request.limit 21 ) 22 return results
Django
1 # views.py 2 from django.http import JsonResponse 3 from hole_foundation_api import HoleFoundationClient 4 import os 5 6 client = HoleFoundationClient( 7 environment=os.environ["HOLE_API_URL"], 8 token=os.environ["AUTH0_M2M_TOKEN"] 9 ) 10 11 def search_view(request): 12 query = request.POST.get('query') 13 results = client.vector_search.search(query=query) 14 return JsonResponse(results)
Testing
1 # test_api.py 2 import pytest 3 from hole_foundation_api import HoleFoundationClient 4 import os 5 6 @pytest.fixture 7 def client(): 8 return HoleFoundationClient( 9 environment=os.environ["HOLE_API_URL"], 10 token=os.environ["AUTH0_TOKEN"] 11 ) 12 13 def test_health_check(client): 14 health = client.health.check() 15 assert health.status == "healthy" 16 17 def test_search(client): 18 results = client.vector_search.search(query="FOIA", limit=5) 19 assert len(results.results) > 0 20 21 def test_list_jurisdictions(client): 22 jurisdictions = client.transparency.list_jurisdictions() 23 assert len(jurisdictions) == 52
Run tests with uv:
$ uv run pytest
Best Practices
1. Use Context Manager (Async)
1 from hole_foundation_api import AsyncHoleFoundationClient 2 3 async with AsyncHoleFoundationClient( 4 environment="https://api.theholefoundation.org", 5 token=os.environ["AUTH0_TOKEN"] 6 ) as client: 7 results = await client.vector_search.search(query="FOIA")
2. Connection Pooling
1 import httpx 2 3 # Reuse HTTP client for better performance 4 http_client = httpx.Client( 5 limits=httpx.Limits( 6 max_connections=100, 7 max_keepalive_connections=20 8 ) 9 ) 10 11 client = HoleFoundationClient( 12 environment="https://api.theholefoundation.org", 13 token=os.environ["AUTH0_TOKEN"], 14 httpx_client=http_client 15 )
3. Logging
1 import logging 2 3 logging.basicConfig(level=logging.DEBUG) 4 logger = logging.getLogger("hole_foundation_api") 5 6 # SDK will log all requests/responses
Package Information
- Name:
hole-foundation-api - Version: Auto-generated from OpenAPI
- License: See package metadata
- Repository: https://github.com/The-HOLE-Foundation/hole-api
Support
- 📧 Email: api@theholefoundation.org
- 💬 Discord: Join our community
- 🐛 Issues: GitHub Issues
- 📚 Full Docs: API Documentation