A production-ready, scalable OCR (Optical Character Recognition) SaaS API powered by Chandra.
- 🚀 High-Accuracy OCR - State-of-the-art OCR with layout preservation
- 📄 Multi-Format Support - Process images (PNG, JPG, TIFF) and PDFs
- 🌍 40+ Languages - Multilingual document processing
- 📝 Multiple Output Formats - Markdown, HTML, JSON
- 🖼️ Image Extraction - Extract and caption images from documents
- ✍️ Handwriting Recognition - Process handwritten text
- 📊 Form & Table Detection - Reconstruct forms with checkboxes and tables
- 🔒 API Key Authentication - Secure access control
- ⚡ Rate Limiting - Configurable rate limits per API key
- 📈 Usage Tracking - Track usage for billing purposes
- 🐳 Docker Ready - Containerized for easy deployment
- ♾️ Stateless & Scalable - Horizontal scaling support
- Python 3.11+
- Docker & Docker Compose (optional, for containerized deployment)
- Clone the repository
git clone <repository-url>
cd test-ocr- Create virtual environment
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate- Install dependencies
pip install -r requirements.txt- Configure environment
cp .env.example .env
# Edit .env with your configuration- Run the API
python -m uvicorn app.main:app --reloadThe API will be available at http://localhost:8000
- Build and run with Docker Compose
docker-compose up -d- Check health
curl http://localhost:8000/healthOnce the API is running, access the interactive documentation:
- Swagger UI: http://localhost:8000/docs
- ReDoc: http://localhost:8000/redoc
All endpoints (except /health) require an API key in the X-API-Key header:
curl -H "X-API-Key: your_api_key_here" http://localhost:8000/api/v1/usageEndpoint: POST /api/v1/ocr
Process a document and get results immediately.
Example:
curl -X POST "http://localhost:8000/api/v1/ocr" \
-H "X-API-Key: demo_key_123" \
-F "file=@document.pdf" \
-F "output_format=markdown" \
-F "include_images=true"Parameters:
file(required): Document file (image or PDF)output_format:markdown,html, orjson(default:markdown)page_range: Page range for PDFs (e.g.,"1-5,7,9-12")include_images: Extract images (default:true)max_output_tokens: Max tokens per pagelanguage: Language hint (e.g.,"en","de","fr")
Response:
{
"success": true,
"format": "markdown",
"content": "# Document Title\n\nDocument content...",
"page_count": 5,
"processing_time_seconds": 12.34,
"images": ["base64_encoded_image_1", "base64_encoded_image_2"]
}Endpoint: POST /api/v1/ocr/async
Submit a document for asynchronous processing (recommended for large files).
Example:
curl -X POST "http://localhost:8000/api/v1/ocr/async" \
-H "X-API-Key: demo_key_123" \
-F "file=@large_document.pdf" \
-F "output_format=markdown"Response:
{
"job_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "pending",
"message": "Job submitted for processing",
"created_at": "2024-01-20T10:30:00Z"
}Endpoint: GET /api/v1/ocr/status/{job_id}
Example:
curl "http://localhost:8000/api/v1/ocr/status/550e8400-e29b-41d4-a716-446655440000" \
-H "X-API-Key: demo_key_123"Endpoint: GET /api/v1/usage
Example:
curl "http://localhost:8000/api/v1/usage" \
-H "X-API-Key: demo_key_123"Response:
{
"api_key": "demo****123",
"requests_today": 42,
"pages_processed_today": 156,
"rate_limit": 100,
"remaining_requests": 97
}Endpoint: GET /health
No authentication required.
curl http://localhost:8000/healthConfigure the API using environment variables in .env:
| Variable | Description | Default |
|---|---|---|
API_KEYS |
Comma-separated list of valid API keys | demo_key_123 |
RATE_LIMIT_FREE |
Rate limit for free tier (req/min) | 10 |
RATE_LIMIT_PREMIUM |
Rate limit for premium tier (req/min) | 100 |
OCR_METHOD |
OCR method: hf or vllm |
hf |
MAX_FILE_SIZE_MB |
Maximum file size in MB | 10 |
MAX_OUTPUT_TOKENS |
Max tokens per page | 2048 |
TEMP_DIR |
Temporary directory for OCR jobs | /tmp/ocr_jobs |
API keys containing "premium" in their name receive premium rate limits:
- Free tier: 10 requests/minute
- Premium tier: 100 requests/minute
Example:
API_KEYS=free_customer_abc,premium_customer_xyz
- Set production environment variables
export API_KEYS="prod_key_1,prod_key_2,premium_customer_xyz"
export RATE_LIMIT_PREMIUM=100
export MAX_FILE_SIZE_MB=20- Deploy with Docker Compose
docker-compose up -d- Monitor logs
docker-compose logs -f ocr-apiFor Kubernetes deployment, create:
- ConfigMap for environment variables
- Secret for API keys
- Deployment for the API service
- Service for load balancing
- HorizontalPodAutoscaler for auto-scaling
Example deployment:
apiVersion: apps/v1
kind: Deployment
metadata:
name: ocr-api
spec:
replicas: 3
selector:
matchLabels:
app: ocr-api
template:
metadata:
labels:
app: ocr-api
spec:
containers:
- name: ocr-api
image: your-registry/ocr-api:latest
ports:
- containerPort: 8000
env:
- name: API_KEYS
valueFrom:
secretKeyRef:
name: ocr-secrets
key: api-keys
resources:
requests:
memory: "2Gi"
cpu: "1000m"
limits:
memory: "4Gi"
cpu: "2000m"The API is stateless and can be horizontally scaled:
- Load Balancer: Use Nginx, Traefik, or cloud load balancers
- Multiple Instances: Deploy multiple API containers
- Redis for Rate Limiting: For distributed rate limiting across instances
# docker-compose.yml with Redis
services:
ocr-api:
# ... existing config ...
environment:
- REDIS_URL=redis://redis:6379/0
depends_on:
- redis
redis:
image: redis:7-alpine
volumes:
- redis_data:/dataFor better performance per instance:
- Increase CPU: More cores for parallel processing
- Increase Memory: Required for large documents (4-8GB recommended)
- GPU Support: Use vLLM method with GPU for faster inference
For production workloads, implement async processing with:
- Task Queue: Celery, RQ, or cloud-based queues (AWS SQS, Google Pub/Sub)
- Background Workers: Separate worker processes for OCR
- Job Storage: Redis or database for job state
- Result Storage: S3, MinIO, or cloud storage for processed documents
Example architecture:
[Client] → [API] → [Queue] → [Workers] → [Storage]
↓ ↓
[Job Status DB] [Result Cache]
Approximate processing times (with HuggingFace method, CPU):
| Document Type | Size | Processing Time |
|---|---|---|
| Single image | 1 page | 2-5 seconds |
| Simple PDF | 5 pages | 10-25 seconds |
| Complex PDF | 10 pages | 20-50 seconds |
| Large PDF | 50 pages | 2-5 minutes |
Tips for better performance:
- Use
page_rangeto process specific pages - Set
include_images=falseif images aren't needed - Use vLLM method with GPU for faster inference
- Implement caching for frequently processed documents
-
API Key Management
- Use strong, random API keys
- Store keys securely (environment variables, secrets management)
- Rotate keys regularly
- Never commit keys to version control
-
Rate Limiting
- Configure appropriate rate limits per tier
- Monitor for abuse
- Implement IP-based rate limiting for additional protection
-
File Validation
- Configure
MAX_FILE_SIZE_MBappropriately - Validate file types before processing
- Scan uploads for malware in production
- Configure
-
Network Security
- Use HTTPS in production (reverse proxy with SSL/TLS)
- Configure CORS appropriately
- Use firewall rules to restrict access
-
Container Security
- Run container as non-root user (already configured)
- Keep base images updated
- Scan images for vulnerabilities
The API includes a health check endpoint:
curl http://localhost:8000/healthConfigure monitoring tools to ping this endpoint regularly.
Logs are written to stdout/stderr. Configure log aggregation:
- Docker: Use Docker logging drivers
- Kubernetes: Use Fluentd, Logstash, or cloud logging
- Production: Send logs to Elasticsearch, CloudWatch, or Datadog
Implement metrics collection for:
- Request count per API key
- Processing time per document
- Error rates
- Queue depth (for async processing)
- Resource utilization (CPU, memory)
Use tools like Prometheus, Grafana, or cloud monitoring services.
test-ocr/
├── app/
│ ├── __init__.py
│ ├── main.py # FastAPI application
│ ├── config.py # Configuration management
│ ├── models.py # Pydantic models
│ ├── auth.py # Authentication
│ ├── rate_limit.py # Rate limiting
│ └── ocr_service.py # Chandra OCR wrapper
├── requirements.txt
├── Dockerfile
├── docker-compose.yml
├── .env.example
├── .gitignore
└── README.md
# Install dev dependencies
pip install pytest pytest-asyncio httpx
# Run tests
pytest- Fork the repository
- Create a feature branch
- Make your changes
- Add tests
- Submit a pull request
Issue: "Invalid API key"
- Solution: Ensure
X-API-Keyheader is set and key is inAPI_KEYSenvironment variable
Issue: "Rate limit exceeded"
- Solution: Wait for the rate limit window to reset (1 minute) or upgrade to premium tier
Issue: "File size exceeds maximum"
- Solution: Increase
MAX_FILE_SIZE_MBor split document into smaller parts
Issue: "OCR processing timed out"
- Solution: Use
/api/v1/ocr/asyncfor large documents or increase timeout
Issue: Docker container won't start
- Solution: Check logs with
docker-compose logs ocr-api
This project uses:
- Application Code: Apache 2.0 License
- Chandra Model Weights: Modified OpenRAIL-M License (free for research and startups < $2M revenue)
See Chandra license for details.
For issues, questions, or feature requests:
- Open an issue on GitHub
- Contact: support@example.com
- Implement full async processing with Celery
- Add Redis-based distributed rate limiting
- Implement usage analytics dashboard
- Add webhook notifications for async jobs
- Support for batch processing
- Add result caching
- Implement pay-as-you-go billing integration
- Add support for more output formats
- Implement document preprocessing options
- Add metrics and monitoring endpoints
Built with ❤️ for scalable OCR processing