479 lines
9.5 KiB
Markdown
479 lines
9.5 KiB
Markdown
---
|
|
name: policyengine-api
|
|
description: PolicyEngine API - Flask REST service powering policyengine.org and programmatic access
|
|
---
|
|
|
|
# PolicyEngine API
|
|
|
|
The PolicyEngine API is a Flask-based REST service that provides tax and benefit calculations for the web app and programmatic users.
|
|
|
|
## For Users 👥
|
|
|
|
### What is the API?
|
|
|
|
When you use policyengine.org, the API processes your calculations on our servers.
|
|
|
|
**API base:** https://api.policyengine.org
|
|
|
|
**What it does:**
|
|
- Runs tax and benefit calculations
|
|
- Stores and retrieves policy reforms
|
|
- Computes population-wide impacts
|
|
- Serves parameter and variable metadata
|
|
|
|
### Public Access
|
|
|
|
The API is publicly accessible with rate limits:
|
|
- **Unauthenticated:** 100 requests/minute
|
|
- **Authenticated:** 1,000 requests/minute
|
|
|
|
**Try it:**
|
|
```bash
|
|
curl https://api.policyengine.org/us/policy/2
|
|
```
|
|
|
|
### API Documentation
|
|
|
|
**OpenAPI spec:** https://api.policyengine.org/docs
|
|
|
|
**Interactive docs:** Swagger UI at API docs endpoint
|
|
|
|
## For Analysts 📊
|
|
|
|
### Using the API
|
|
|
|
**Option 1: Python client (recommended)**
|
|
```python
|
|
# Use the policyengine package
|
|
# See policyengine-python-client-skill
|
|
```
|
|
|
|
**Option 2: Direct API calls**
|
|
```python
|
|
import requests
|
|
|
|
# Calculate household impact
|
|
response = requests.post(
|
|
"https://api.policyengine.org/us/calculate",
|
|
json={
|
|
"household": household_situation,
|
|
"policy_id": None # or reform_id
|
|
}
|
|
)
|
|
result = response.json()
|
|
```
|
|
|
|
### Key Endpoints
|
|
|
|
**Household calculations:**
|
|
```
|
|
POST /us/calculate
|
|
POST /uk/calculate
|
|
```
|
|
|
|
**Policy management:**
|
|
```
|
|
GET /us/policy/{policy_id}
|
|
POST /us/policy
|
|
```
|
|
|
|
**Economy impacts:**
|
|
```
|
|
GET /us/economy/{policy_id}/over/{baseline_policy_id}
|
|
```
|
|
|
|
**Metadata:**
|
|
```
|
|
GET /us/parameters
|
|
GET /us/variables
|
|
GET /us/parameter/{parameter_name}
|
|
GET /us/variable/{variable_name}
|
|
```
|
|
|
|
### Rate Limits and Performance
|
|
|
|
**Rate limits:**
|
|
- 100 req/min (unauthenticated)
|
|
- 1,000 req/min (authenticated - contact team)
|
|
|
|
**Response times:**
|
|
- Household calculation: ~200-500ms
|
|
- Population impact: ~5-30 seconds
|
|
- Cached results: <100ms
|
|
|
|
**Optimization:**
|
|
- Use the same policy_id for multiple requests (caching)
|
|
- Batch calculations when possible
|
|
- Use webhooks for long-running jobs (population impacts)
|
|
|
|
## For Contributors 💻
|
|
|
|
### Repository
|
|
|
|
**Location:** PolicyEngine/policyengine-api
|
|
|
|
**Clone:**
|
|
```bash
|
|
git clone https://github.com/PolicyEngine/policyengine-api
|
|
cd policyengine-api
|
|
```
|
|
|
|
### Current Architecture
|
|
|
|
**To see current structure:**
|
|
```bash
|
|
tree policyengine_api/
|
|
|
|
# Key directories:
|
|
ls policyengine_api/
|
|
# - endpoints/ - HTTP endpoint handlers
|
|
# - routes/ - Route registration
|
|
# - services/ - Business logic
|
|
# - compute_api/ - Calculation services
|
|
# - economy_api/ - Economy impact calculations
|
|
# - utils/ - Helpers (caching, validation)
|
|
# - data/ - Static data
|
|
```
|
|
|
|
### Current Implementation Patterns
|
|
|
|
**Reference endpoint (read this first):**
|
|
```bash
|
|
cat policyengine_api/endpoints/economy.py
|
|
```
|
|
|
|
**This demonstrates:**
|
|
- Standard endpoint structure
|
|
- Request validation
|
|
- Caching pattern
|
|
- Error handling
|
|
- Response formatting
|
|
|
|
**To find other endpoints:**
|
|
```bash
|
|
ls policyengine_api/endpoints/
|
|
# - household.py
|
|
# - policy.py
|
|
# - economy.py
|
|
# - metadata.py
|
|
# - etc.
|
|
```
|
|
|
|
### Standard Endpoint Pattern (Stable)
|
|
|
|
```python
|
|
from flask import Blueprint, request, jsonify
|
|
from policyengine_api.utils import cache
|
|
|
|
blueprint = Blueprint("my_endpoint", __name__)
|
|
|
|
@blueprint.route("/us/calculate", methods=["POST"])
|
|
def calculate():
|
|
"""Standard pattern: validate, cache-check, compute, cache, return."""
|
|
try:
|
|
# 1. Get and validate input
|
|
data = request.json
|
|
if not data:
|
|
return jsonify({"error": "No data provided"}), 400
|
|
|
|
# 2. Generate cache key
|
|
cache_key = f"calc_{hash(str(data))}"
|
|
|
|
# 3. Check cache
|
|
cached = cache.get(cache_key)
|
|
if cached:
|
|
return jsonify(cached)
|
|
|
|
# 4. Compute
|
|
result = perform_calculation(data)
|
|
|
|
# 5. Cache result
|
|
cache.set(cache_key, result, expire=3600)
|
|
|
|
# 6. Return
|
|
return jsonify(result)
|
|
|
|
except Exception as e:
|
|
return jsonify({"error": str(e), "status": "error"}), 500
|
|
```
|
|
|
|
**Current implementation details:**
|
|
```bash
|
|
# See actual endpoint for current pattern
|
|
cat policyengine_api/endpoints/household.py
|
|
```
|
|
|
|
### Caching Strategy
|
|
|
|
**To see current caching implementation:**
|
|
```bash
|
|
# Redis configuration
|
|
cat policyengine_api/utils/cache.py
|
|
|
|
# Find cache usage
|
|
grep -r "cache\." policyengine_api/endpoints/
|
|
```
|
|
|
|
**Pattern:**
|
|
- Redis for caching
|
|
- Cache keys based on inputs
|
|
- TTL varies by endpoint (1 hour to 1 day)
|
|
- Clear cache on parameter changes
|
|
|
|
### Background Jobs
|
|
|
|
For long-running calculations (population impacts):
|
|
|
|
**To see current implementation:**
|
|
```bash
|
|
# RQ (Redis Queue) usage
|
|
grep -r "@job" policyengine_api/
|
|
|
|
# Job patterns
|
|
cat policyengine_api/economy_api/
|
|
```
|
|
|
|
**Pattern:**
|
|
- Use RQ for jobs > 5 seconds
|
|
- Return job_id immediately
|
|
- Poll for completion
|
|
- Cache results
|
|
|
|
### Country Integration
|
|
|
|
**How API loads country packages:**
|
|
```bash
|
|
cat policyengine_api/country.py
|
|
```
|
|
|
|
**Pattern:**
|
|
- Dynamically imports country packages
|
|
- Routes by country code (/us/, /uk/)
|
|
- Manages multiple model versions
|
|
|
|
### Service Layer
|
|
|
|
**Business logic separated from endpoints:**
|
|
```bash
|
|
ls policyengine_api/services/
|
|
```
|
|
|
|
**Pattern:**
|
|
```python
|
|
# endpoints/household.py
|
|
from policyengine_api.services import household_service
|
|
|
|
@app.route("/us/calculate", methods=["POST"])
|
|
def calculate():
|
|
result = household_service.calculate(data)
|
|
return jsonify(result)
|
|
|
|
# services/household_service.py
|
|
def calculate(data):
|
|
# Business logic here
|
|
simulation = create_simulation(data)
|
|
return simulation.calculate(...)
|
|
```
|
|
|
|
### Testing
|
|
|
|
**To see current test patterns:**
|
|
```bash
|
|
ls tests/
|
|
cat tests/test_household.py
|
|
```
|
|
|
|
**Run tests:**
|
|
```bash
|
|
make test
|
|
|
|
# Specific test
|
|
pytest tests/test_economy.py -v
|
|
|
|
# With coverage
|
|
make test-coverage
|
|
```
|
|
|
|
### Development Server
|
|
|
|
**Start locally:**
|
|
```bash
|
|
make debug
|
|
```
|
|
|
|
**Test endpoint:**
|
|
```bash
|
|
curl http://localhost:5000/us/policy/2
|
|
```
|
|
|
|
### Deployment
|
|
|
|
**To see deployment configuration:**
|
|
```bash
|
|
# Google Cloud Platform
|
|
cat app.yaml # App Engine config
|
|
cat cloudbuild.yaml # Cloud Build config
|
|
|
|
# Environment variables
|
|
cat .env.example
|
|
```
|
|
|
|
**Current deployment:**
|
|
- Google App Engine
|
|
- Cloud SQL (PostgreSQL)
|
|
- Redis (caching)
|
|
- Cloud Build (CI/CD)
|
|
|
|
### API Versions
|
|
|
|
**To see versioning strategy:**
|
|
```bash
|
|
grep -r "version" policyengine_api/
|
|
```
|
|
|
|
**Current approach:**
|
|
- API version in URLs (may add /v1/ prefix)
|
|
- Country package versions independent
|
|
- Breaking changes rare (backwards compatible)
|
|
|
|
## Architecture Diagrams
|
|
|
|
### Request Flow
|
|
|
|
```
|
|
User/App → API Gateway → Flask App → Country Package → Core Engine
|
|
↓
|
|
Redis Cache
|
|
↓
|
|
Background Job (if needed)
|
|
↓
|
|
PostgreSQL (storage)
|
|
```
|
|
|
|
### Dependencies
|
|
|
|
```
|
|
policyengine-core
|
|
↓
|
|
policyengine-us, policyengine-uk, etc.
|
|
↓
|
|
policyengine-api (you are here)
|
|
↓
|
|
policyengine-app (consumes API)
|
|
```
|
|
|
|
**To understand dependencies:**
|
|
- See `policyengine-core-skill` for engine patterns
|
|
- See `policyengine-us-skill` for country model usage
|
|
- See `policyengine-app-skill` for how app calls API
|
|
|
|
## Common Development Tasks
|
|
|
|
### Task 1: Add New Endpoint
|
|
|
|
1. **Study reference implementation:**
|
|
```bash
|
|
cat policyengine_api/endpoints/economy.py
|
|
```
|
|
|
|
2. **Create new endpoint file:**
|
|
```python
|
|
# policyengine_api/endpoints/my_endpoint.py
|
|
# Follow the pattern from economy.py
|
|
```
|
|
|
|
3. **Register route:**
|
|
```bash
|
|
# See route registration
|
|
cat policyengine_api/routes/__init__.py
|
|
```
|
|
|
|
4. **Add tests:**
|
|
```bash
|
|
# Follow test pattern
|
|
cat tests/test_economy.py
|
|
```
|
|
|
|
### Task 2: Modify Caching Behavior
|
|
|
|
**See current caching:**
|
|
```bash
|
|
cat policyengine_api/utils/cache.py
|
|
```
|
|
|
|
**Common changes:**
|
|
- Adjust TTL (time to live)
|
|
- Change cache key generation
|
|
- Add cache invalidation
|
|
|
|
### Task 3: Update Country Package Version
|
|
|
|
**To see how versions are managed:**
|
|
```bash
|
|
# Requirements
|
|
cat requirements.txt | grep policyengine-
|
|
|
|
# Update and deploy
|
|
# See deployment docs in README
|
|
```
|
|
|
|
## Security and Best Practices
|
|
|
|
### Input Validation
|
|
|
|
**Always validate:**
|
|
- Country code (us, uk, ca)
|
|
- Policy ID format
|
|
- Household structure
|
|
- Parameter values
|
|
|
|
**See validation examples:**
|
|
```bash
|
|
grep -r "validate" policyengine_api/endpoints/
|
|
```
|
|
|
|
### Error Handling
|
|
|
|
**Standard error response:**
|
|
```python
|
|
return jsonify({
|
|
"error": "Error message",
|
|
"details": additional_context,
|
|
"status": "error"
|
|
}), status_code
|
|
```
|
|
|
|
**See error patterns:**
|
|
```bash
|
|
grep -A 5 "jsonify.*error" policyengine_api/endpoints/
|
|
```
|
|
|
|
### Logging
|
|
|
|
**To see logging configuration:**
|
|
```bash
|
|
cat policyengine_api/gcp_logging.py
|
|
```
|
|
|
|
**Pattern:**
|
|
- Google Cloud Logging
|
|
- Log all errors
|
|
- Log slow queries (>1s)
|
|
- Don't log sensitive data
|
|
|
|
## Related Skills
|
|
|
|
- **policyengine-python-client-skill** - Using the API
|
|
- **policyengine-core-skill** - Understanding the engine
|
|
- **policyengine-us-skill** - Country model integration
|
|
- **policyengine-app-skill** - How app consumes API
|
|
- **policyengine-standards-skill** - Code quality
|
|
- **policyengine-writing-skill** - API documentation style
|
|
|
|
## Resources
|
|
|
|
**Repository:** https://github.com/PolicyEngine/policyengine-api
|
|
**Live API:** https://api.policyengine.org
|
|
**Documentation:** https://api.policyengine.org/docs
|
|
**Status:** https://status.policyengine.org
|