|
# API Authentication & Usage Guide
|
|
|
|
## π Setting Up API Key in Hugging Face Spaces
|
|
|
|
### Step 1: Generate Secure API Key
|
|
```bash
|
|
# Generate random API key (example methods)
|
|
node -e "console.log('sk-' + require('crypto').randomBytes(32).toString('hex'))"
|
|
# Or use Python
|
|
python -c "import secrets; print('sk-' + secrets.token_hex(32))"
|
|
```
|
|
|
|
### Step 2: Configure in HF Spaces
|
|
1. Navigate to your Space: `https://huggingface.co/spaces/your-username/space-name`
|
|
2. Click **"Settings"** tab
|
|
3. Click **"Variables"** section
|
|
4. Add this environment variable:
|
|
|
|
| Variable | Value | Description |
|
|
|----------|-------|-------------|
|
|
| `API_KEY` | `sk-your-secure-key-here` | Single API key for authentication |
|
|
|
|
### Step 3: Test Authentication
|
|
```bash
|
|
# Test without API key (should fail if authentication enabled)
|
|
curl -X POST https://your-space.hf.space/screenshot \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"url": "https://example.com"}'
|
|
|
|
# Test with API key (should work)
|
|
curl -X POST https://your-space.hf.space/screenshot \
|
|
-H "Authorization: Bearer your-api-key-here" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"url": "https://example.com"}'
|
|
```
|
|
|
|
## π Understanding Server Status Responses
|
|
|
|
### Normal Operation
|
|
```json
|
|
{
|
|
"status": "running",
|
|
"system": { "cpuUsage": "25%" },
|
|
"queue": { "activeRequests": 1, "queuedRequests": 0 }
|
|
}
|
|
```
|
|
|
|
### Server Busy (CPU > 95%)
|
|
```json
|
|
{
|
|
"status": "busy",
|
|
"error": "Server is currently overloaded",
|
|
"cpuUsage": "96%",
|
|
"queueLength": 3
|
|
}
|
|
```
|
|
|
|
### Queue Full Response
|
|
```json
|
|
{
|
|
"status": "busy",
|
|
"error": "Request queue timeout",
|
|
"queueLength": 10
|
|
}
|
|
```
|
|
|
|
## π Implementing Retry Logic
|
|
|
|
### JavaScript Retry Pattern
|
|
```javascript
|
|
async function screenshotWithRetry(url, options = {}, maxRetries = 3) {
|
|
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
try {
|
|
const response = await fetch('/screenshot', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'Authorization': 'Bearer your-api-key'
|
|
},
|
|
body: JSON.stringify({ url, ...options })
|
|
});
|
|
|
|
if (response.ok) {
|
|
return await response.blob();
|
|
}
|
|
|
|
if (response.status === 503) {
|
|
const error = await response.json();
|
|
if (error.status === 'busy') {
|
|
console.log(`Attempt ${attempt}: Server busy, retrying...`);
|
|
await new Promise(resolve => setTimeout(resolve, 2000 * attempt));
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// Other errors, don't retry
|
|
throw new Error(`API Error: ${response.status}`);
|
|
|
|
} catch (error) {
|
|
if (attempt === maxRetries) throw error;
|
|
await new Promise(resolve => setTimeout(resolve, 1000 * attempt));
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### Python Retry Pattern
|
|
```python
|
|
import time
|
|
import requests
|
|
from typing import Optional
|
|
|
|
def screenshot_with_retry(url: str, api_key: str, max_retries: int = 3) -> Optional[bytes]:
|
|
for attempt in range(1, max_retries + 1):
|
|
try:
|
|
response = requests.post(
|
|
'https://your-space.hf.space/screenshot',
|
|
headers={'Authorization': f'Bearer {api_key}', 'Content-Type': 'application/json'},
|
|
json={'url': url},
|
|
timeout=30
|
|
)
|
|
|
|
if response.status_code == 200:
|
|
return response.content
|
|
|
|
if response.status_code == 503:
|
|
error_data = response.json()
|
|
if error_data.get('status') == 'busy':
|
|
print(f"Attempt {attempt}: Server busy (CPU: {error_data.get('cpuUsage', 'N/A')})")
|
|
time.sleep(2 * attempt)
|
|
continue
|
|
|
|
response.raise_for_status()
|
|
|
|
except requests.RequestException as e:
|
|
if attempt == max_retries:
|
|
raise e
|
|
time.sleep(attempt)
|
|
|
|
return None
|
|
```
|
|
|
|
## π¨ Rate Limiting & Best Practices
|
|
|
|
### Rate Limit Headers
|
|
The API returns rate limiting information:
|
|
```
|
|
X-RateLimit-Limit: 100
|
|
X-RateLimit-Remaining: 95
|
|
X-RateLimit-Reset: 1640995200
|
|
```
|
|
|
|
### Best Practices
|
|
1. **Implement exponential backoff** for retries
|
|
2. **Cache results** when possible
|
|
3. **Use appropriate image quality** (70-80% usually sufficient)
|
|
4. **Monitor your usage** via `/status` endpoint
|
|
5. **Handle queue timeouts** gracefully
|
|
|
|
### Production Usage Example
|
|
```javascript
|
|
class ScreenshotAPI {
|
|
constructor(apiKey, baseUrl) {
|
|
this.apiKey = apiKey;
|
|
this.baseUrl = baseUrl;
|
|
this.requestCount = 0;
|
|
this.resetTime = 0;
|
|
}
|
|
|
|
async screenshot(url, options = {}) {
|
|
// Check rate limit
|
|
if (this.requestCount >= 95 && Date.now() < this.resetTime) {
|
|
throw new Error('Rate limit approached, waiting...');
|
|
}
|
|
|
|
const response = await this.makeRequest('/screenshot', {
|
|
method: 'POST',
|
|
body: JSON.stringify({ url, ...options })
|
|
});
|
|
|
|
// Update rate limit tracking
|
|
this.requestCount = parseInt(response.headers.get('X-RateLimit-Remaining') || '0');
|
|
this.resetTime = parseInt(response.headers.get('X-RateLimit-Reset') || '0') * 1000;
|
|
|
|
return response;
|
|
}
|
|
|
|
async makeRequest(endpoint, options = {}) {
|
|
return fetch(this.baseUrl + endpoint, {
|
|
...options,
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'Authorization': `Bearer ${this.apiKey}`,
|
|
...options.headers
|
|
}
|
|
});
|
|
}
|
|
}
|
|
```
|
|
|
|
## π§ Troubleshooting
|
|
|
|
### Common Issues
|
|
|
|
| Error | Cause | Solution |
|
|
|-------|-------|----------|
|
|
| `401 Unauthorized` | Missing API key | Add `Authorization: Bearer` header |
|
|
| `403 Forbidden` | Invalid API key | Check key spelling/validity |
|
|
| `503 Service Unavailable` | Server overloaded | Implement retry with delay |
|
|
| `429 Too Many Requests` | Rate limit exceeded | Wait for reset time |
|
|
|
|
### Performance Optimization
|
|
- Use smaller dimensions for faster processing
|
|
- Lower quality settings for non-critical uses
|
|
- Batch requests with appropriate delays
|
|
- Monitor CPU usage via `/status` |