Production-ready API for generating comprehensive style guides from websites using AI-powered analysis.
Production: https://your-app.railway.app
Development: http://localhost:3002
All requests must include a valid API key using one of these methods:
Method 1: Bearer Token (Recommended)
Authorization: Bearer sk-your-api-key-hereMethod 2: API Key Header
X-API-Key: sk-your-api-key-here- Must start with
sk- - Minimum 20 characters
- Example:
sk-stylesstealer-prod-key-87654321
Contact the administrator to obtain an API key.
Generate a comprehensive style guide from a website URL.
Endpoint: POST /api/generate
Headers:
Content-Type: application/json
Authorization: Bearer sk-your-api-key-hereRequest Body:
{
"url": "https://example.com",
"webhook_url": "https://your-webhook.com/endpoint" // optional
}Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
| url | string | Yes | Website URL to analyze |
| webhook_url | string | No | Webhook URL to post results to |
Response (Success):
{
"success": true,
"markdown": "# Style Guide: Example Website\n\n...",
"generationTime": 90657,
"pagesAnalyzed": 3
}Response Headers:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1234567890Example Request (cURL):
curl -X POST https://your-app.railway.app/api/generate \
-H "Content-Type: application/json" \
-H "Authorization: Bearer sk-your-api-key-here" \
-d '{
"url": "https://stripe.com",
"webhook_url": "https://webhook.site/your-unique-url"
}'Example Request (JavaScript):
const response = await fetch('https://your-app.railway.app/api/generate', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer sk-your-api-key-here'
},
body: JSON.stringify({
url: 'https://stripe.com',
webhook_url: 'https://webhook.site/your-unique-url'
})
});
const data = await response.json();
console.log(data.markdown);Example Request (n8n Workflow):
{
"method": "POST",
"url": "https://your-app.railway.app/api/generate",
"headers": {
"Authorization": "Bearer sk-your-api-key-here"
},
"body": {
"url": "{{$json.website_url}}",
"webhook_url": "{{$json.webhook_url}}"
}
}Check if the service is running.
Endpoint: GET /api/health
No authentication required
Response:
{
"status": "ok",
"timestamp": "2024-01-15T10:30:00.000Z",
"service": "style-stealer",
"version": "1.0.0"
}When a webhook_url is provided, the API will POST the results to that URL after generation completes.
Webhook Request:
POST {webhook_url}
Content-Type: application/json
User-Agent: StyleStealer/1.0Webhook Payload:
{
"url": "https://example.com",
"markdown": "# Style Guide: Example Website\n\n...",
"generationTime": 90657,
"pagesAnalyzed": 3,
"timestamp": "2024-01-15T10:30:00.000Z"
}Your webhook endpoint should return a 2xx status code to indicate success.
All requests require authentication. Rate limits apply per API key and globally.
| Type | Limit | Window |
|---|---|---|
| Per API Key | 100 requests | 1 hour |
| Global (DDoS protection) | 50 requests | 1 minute |
Note: No unauthenticated access is allowed. All requests must include a valid API key.
Every response includes rate limit information:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1234567890X-RateLimit-Limit: Maximum requests allowedX-RateLimit-Remaining: Requests remaining in current windowX-RateLimit-Reset: Unix timestamp when the limit resets
Response (429):
{
"success": false,
"error": "Rate limit exceeded for your API key. Please try again later."
}Headers:
Retry-After: 3600401 Unauthorized:
{
"success": false,
"error": "Unauthorized. Valid API key required."
}Cause: Missing or invalid API key
400 Bad Request:
{
"success": false,
"error": "Invalid request: Invalid URL format"
}Causes:
- Invalid URL format
- Invalid webhook URL format
- Missing required fields
503 Service Unavailable:
{
"success": false,
"error": "Service temporarily unavailable. Please try again later."
}Cause: Global rate limit exceeded (DDoS protection)
504 Gateway Timeout:
{
"success": false,
"error": "Request timeout - the website took too long to respond",
"generationTime": 120000
}Cause: Target website didn't respond in time
404 Not Found:
{
"success": false,
"error": "Website not found or refused connection",
"generationTime": 5000
}Cause: Target website is unreachable
Use the special test URL to get instant dummy data:
{
"url": "https://website.com/test"
}Response: Instant dummy style guide (< 1 second, no cost)
- Single page: ~55 seconds
- Multi-page (3 pages): ~90 seconds
- Test URL: < 1 second
- Page Discovery (~2s): Finds 2 additional relevant pages
- Concurrent Scraping (~5-10s): Scrapes 3 pages simultaneously
- CSS/HTML Parsing (~2-3s per page): Extracts design tokens
- AI Image Analysis (~15-20s per page): Analyzes 6 images per page
- Style Guide Generation (~10-15s per page): Generates markdown
- Report Combination (~10-15s): Synthesizes all reports
- Approximately $0.05 - $0.15 per generation
- Depends on number of images and pages analyzed
Method: POST
URL: https://your-app.railway.app/api/generate
Headers:
{
"Authorization": "Bearer sk-your-api-key-here"
}Body:
{
"url": "{{$json.website_url}}",
"webhook_url": "{{$json.callback_url}}"
}Set up a webhook to receive the results:
Webhook URL: https://your-n8n.com/webhook/style-guide-result
The webhook will receive:
{
"url": "https://example.com",
"markdown": "# Style Guide...",
"generationTime": 90657,
"pagesAnalyzed": 3,
"timestamp": "2024-01-15T10:30:00.000Z"
}Trigger
→ Set website URL
→ HTTP Request (POST /api/generate)
→ [Optional] Wait for webhook
→ Process markdown result
→ Save to database/send email/etc.
- No unauthenticated access allowed
- All requests must include a valid API key
- Invalid or missing API keys return 401 Unauthorized
- Protects against unauthorized usage and abuse
- URL length: Maximum 2048 characters
- Webhook URL length: Maximum 2048 characters
- Request body size: Maximum 10KB (prevents payload spam)
- URL format validation (Zod schema)
- Webhook URL format validation
- JSON parsing validation
- Sanitized error messages (no sensitive data exposed)
- API key-based: 100 requests per hour per key
- Global (DDoS protection): 50 requests per minute across all users
- Automatic 5-minute block after limit exceeded
- Rate limit headers in all responses
- Note: No IP-based limiting (authentication required instead)
- Environment variables only
- Never logged or exposed in responses
- Supports multiple keys for rotation
- Minimum 20 characters, must start with
sk-
- All production endpoints must use HTTPS
- API keys transmitted securely
- Webhook URLs must use HTTPS in production
- Railway account
- GitHub repository
1. Connect Repository:
railway link2. Set Environment Variables:
railway variables set ANTHROPIC_API_KEY=sk-ant-api03-your-key-here
railway variables set API_KEYS=sk-your-api-key-1,sk-your-api-key-2
railway variables set RATE_LIMIT_PER_HOUR=10
railway variables set RATE_LIMIT_API_KEY=100
railway variables set RATE_LIMIT_GLOBAL=503. Deploy:
railway up4. Get URL:
railway domain| Variable | Required | Default | Description |
|---|---|---|---|
| ANTHROPIC_API_KEY | Yes | - | Anthropic API key for AI |
| API_KEYS | Yes | - | Comma-separated API keys |
| RATE_LIMIT_PER_HOUR | No | 10 | Requests/hour per IP |
| RATE_LIMIT_API_KEY | No | 100 | Requests/hour per API key |
| RATE_LIMIT_GLOBAL | No | 50 | Global requests/minute |
See railway.json for build and deployment settings.
Health Check: /api/health
Since generation takes 60-120 seconds, use webhooks for async processing:
// Don't wait for response
fetch('/api/generate', {
method: 'POST',
body: JSON.stringify({
url: 'https://example.com',
webhook_url: 'https://your-webhook.com/callback'
})
});
// Handle result in webhook endpointconst response = await fetch('/api/generate', {
method: 'POST',
headers: { 'Authorization': 'Bearer sk-your-key' },
body: JSON.stringify({ url: 'https://example.com' })
});
if (response.status === 429) {
const retryAfter = response.headers.get('Retry-After');
console.log(`Rate limited. Retry after ${retryAfter} seconds`);
// Implement exponential backoff
}const remaining = response.headers.get('X-RateLimit-Remaining');
if (remaining < 10) {
console.warn('Approaching rate limit!');
}// Development/testing
const testUrl = 'https://website.com/test';
// Production
const realUrl = 'https://example.com';"Unauthorized" error:
- Check API key format (must start with
sk-) - Verify API key is in environment variables
- Check Authorization header format
"Rate limit exceeded":
- Wait for the time specified in
Retry-Afterheader - Consider upgrading rate limits
- Use test URL for development
"Request timeout":
- Target website is slow or unresponsive
- Try again later
- Some complex sites may exceed timeout limits
"Website not found":
- Verify URL is correct and publicly accessible
- Check if website has anti-bot protection
- Ensure website uses HTTPS
- Initial release
- Multi-page analysis
- AI-powered image descriptions
- Webhook support
- Railway deployment ready
- Comprehensive security and rate limiting