Authentication & Security
Chartly provides multiple authentication methods to fit different use cases, from quick trials to production deployments with advanced security requirements.
Authentication Methods Overview
Method | Use Case | Security | Setup |
---|---|---|---|
Trial API Key | Testing, demos | Basic | No signup |
API Keys | Production apps | Standard | Account required via dashboard |
Signed URLs | Public sharing | High | HMAC signing |
Note: Production API keys are managed through the Chartly Dashboard. Account creation, login, and API key management are handled via the web interface.
Trial API Keys
Perfect for testing and evaluation without any signup requirements.
Getting a Trial Key
curl -X POST "https://api.chartly.dev/signup/anon"
Response:
{
"api_key": "trial_abc123def456xyz789"
}
Trial Limitations
- ✅ 100 total chart renders
- ✅ 30-day expiration
- ✅ All chart types and formats
- ❌ No account management
- ❌ No usage analytics
- ❌ No API key regeneration
Using Trial Keys
curl "https://api.chartly.dev/v1/chart" \
-H "X-Api-Key: trial_abc123def456xyz789" \
-H "Content-Type: application/json" \
-d '{"chart": {...}, "width": 400, "height": 300}'
Storage and Management
Trial keys are automatically stored in your browser's localStorage
when generated through the web interface:
// Checking for existing trial key
const existingKey = localStorage.getItem('trial_api_key');
const keyCreated = localStorage.getItem('trial_api_key_created');
// Keys expire after 30 days
const isExpired = (Date.now() - parseInt(keyCreated)) > (30 * 24 * 60 * 60 * 1000);
Production API Keys
Full-featured API keys for production applications with account management and analytics.
Getting Production Keys
Production API keys are managed through the Chartly Dashboard:
- Create Account - Sign up at chartly.dev
- Access Dashboard - Log in to your account
- Generate Keys - Create API keys for different environments
- Monitor Usage - Track analytics and billing
API Key Best Practices
-
Separate Keys by Environment
- Use different API keys for development, staging, and production
- Label keys clearly for easy identification
-
Rotate Keys Regularly
- Generate new keys periodically via the dashboard
- Delete unused or compromised keys immediately
-
Secure Storage
- Store keys in environment variables, not in code
- Use secret management services in production
-
Monitor Usage
- Check your dashboard for usage analytics
- Set up alerts for unusual activity
Environment Variable Setup
# .env file
CHARTLY_API_KEY=live_your_production_key_here
# Development environment
CHARTLY_DEV_API_KEY=live_your_development_key_here
// Node.js usage
const apiKey = process.env.CHARTLY_API_KEY;
const response = await fetch('https://api.chartly.dev/v1/chart', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Api-Key': apiKey
},
body: JSON.stringify({
chart: chartConfig,
width: 600,
height: 400
})
});
Signed URLs
Secure, time-limited URLs for public sharing without exposing your API keys. Perfect for email reports, public dashboards, and sharing charts with external parties.
How Signed URLs Work
- Generate a URL with chart parameters
- Add expiration timestamp
- Create HMAC-SHA256 signature using your secret key
- Append signature to URL
Generating Signed URLs
const crypto = require('crypto');
function createSignedChartUrl(chartConfig, options = {}) {
const {
width = 600,
height = 400,
format = 'png',
backgroundColor = 'white',
expirationMinutes = 60,
secretKey = process.env.CHARTLY_SECRET_KEY
} = options;
// Create URL parameters
const params = new URLSearchParams({
chart: JSON.stringify(chartConfig),
width: width.toString(),
height: height.toString(),
format,
backgroundColor
});
// Add expiration timestamp (Unix timestamp)
const exp = Math.floor(Date.now() / 1000) + (expirationMinutes * 60);
params.append('exp', exp.toString());
// Create signature
const message = params.toString();
const signature = crypto
.createHmac('sha256', secretKey)
.update(message)
.digest('hex');
params.append('sig', signature);
return `https://api.chartly.dev/v1/chart?${params.toString()}`;
}
// Usage
const chartConfig = {
type: 'bar',
data: {
labels: ['Q1', 'Q2', 'Q3', 'Q4'],
datasets: [{
label: 'Revenue',
data: [100, 120, 115, 134]
}]
},
options: { responsive: false }
};
const signedUrl = createSignedChartUrl(chartConfig, {
width: 800,
height: 400,
expirationMinutes: 120 // 2 hours
});
Using Signed URLs
The generated URL can be used directly in any context:
<!-- In HTML emails -->
<img src="https://api.chartly.dev/v1/chart?chart=...&exp=1642262400&sig=abc123..."
alt="Quarterly Revenue Chart"
width="800"
height="400">
<!-- In Slack/Discord -->

Signed URL Security
- Time-limited: URLs automatically expire
- Tamper-proof: Any modification invalidates the signature
- Secret-based: Requires your secret key to generate
- No API key exposure: Safe for public sharing
Managing Secret Keys
Your secret key should be:
- Stored securely (environment variables)
- Never committed to version control
- Rotated periodically
- Different for each environment
# Environment setup
CHARTLY_SECRET_KEY=your-256-bit-secret-key-here
CHARTLY_SECRET_KEY_DEV=your-development-secret-key-here
Error Handling
Authentication Errors
Error Code | Status | Description | Solution |
---|---|---|---|
unauthorized | 401 | Invalid API key | Check key format and validity |
forbidden | 403 | Insufficient permissions | Check account status |
trial_limit_reached | 429 | Trial usage exhausted | Upgrade account |
rate_limit_exceeded | 429 | Too many requests | Implement rate limiting |
Error Response Format
{
"error": "unauthorized",
"detail": "Invalid or missing API key"
}
Handling Authentication Errors
async function makeChartRequest(chartConfig) {
try {
const response = await fetch('https://api.chartly.dev/v1/chart', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Api-Key': process.env.CHARTLY_API_KEY
},
body: JSON.stringify(chartConfig)
});
if (response.status === 401) {
throw new Error('Invalid API key - please check your credentials');
}
if (response.status === 429) {
const retryAfter = response.headers.get('Retry-After');
throw new Error(`Rate limit exceeded. Retry after ${retryAfter} seconds`);
}
if (!response.ok) {
const error = await response.json();
throw new Error(`Chart generation failed: ${error.detail}`);
}
return await response.blob();
} catch (error) {
console.error('Chart generation error:', error.message);
throw error;
}
}
Security Best Practices
API Key Security
-
Environment Variables
# Never hardcode keys
❌ const apiKey = "live_abc123...";
# Use environment variables
✅ const apiKey = process.env.CHARTLY_API_KEY; -
Secure Transmission
- Always use HTTPS
- Never log API keys
- Don't include keys in URLs (except signed URLs)
-
Access Control
- Use different keys for different environments
- Regularly rotate keys via the dashboard
- Monitor usage for unusual activity
Signed URL Security
-
Short Expiration Times
// For emails: 24-48 hours
expirationMinutes: 24 * 60
// For real-time sharing: 1-2 hours
expirationMinutes: 120 -
Strong Secret Keys
# Generate secure random keys
openssl rand -hex 32 -
Signature Validation
- Always validate expiration
- Verify signature before serving
- Use constant-time comparison
Integration Examples
React Hook for Authentication
import { useState, useEffect } from 'react';
function useChartlyAuth() {
const [apiKey, setApiKey] = useState(null);
const [isAuthenticated, setIsAuthenticated] = useState(false);
const [loading, setLoading] = useState(true);
useEffect(() => {
// Check for stored trial key
const trialKey = localStorage.getItem('trial_api_key');
const keyCreated = localStorage.getItem('trial_api_key_created');
if (trialKey && keyCreated) {
const isExpired = (Date.now() - parseInt(keyCreated)) > (30 * 24 * 60 * 60 * 1000);
if (!isExpired) {
setApiKey(trialKey);
setIsAuthenticated(true);
} else {
localStorage.removeItem('trial_api_key');
localStorage.removeItem('trial_api_key_created');
}
}
setLoading(false);
}, []);
const generateTrialKey = async () => {
try {
const response = await fetch('https://api.chartly.dev/signup/anon', {
method: 'POST'
});
const data = await response.json();
localStorage.setItem('trial_api_key', data.api_key);
localStorage.setItem('trial_api_key_created', Date.now().toString());
setApiKey(data.api_key);
setIsAuthenticated(true);
return data.api_key;
} catch (error) {
console.error('Failed to generate trial key:', error);
throw error;
}
};
return {
apiKey,
isAuthenticated,
loading,
generateTrialKey
};
}
Express.js Middleware
function chartlyAuth(req, res, next) {
const apiKey = req.headers['x-api-key'] || process.env.CHARTLY_API_KEY;
if (!apiKey) {
return res.status(401).json({
error: 'unauthorized',
detail: 'API key required'
});
}
req.chartlyApiKey = apiKey;
next();
}
// Usage
app.post('/generate-chart', chartlyAuth, async (req, res) => {
try {
const response = await fetch('https://api.chartly.dev/v1/chart', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Api-Key': req.chartlyApiKey
},
body: JSON.stringify(req.body)
});
const chartBuffer = await response.buffer();
res.setHeader('Content-Type', 'image/png');
res.send(chartBuffer);
} catch (error) {
res.status(500).json({ error: 'Chart generation failed' });
}
});
Next Steps:
- Learn about Chart Types & Examples
- Explore Integration Guides
- Check Usage & Billing