Cache Your Requests
If you're requesting the same data frequently, cache the results locally. This reduces unnecessary API calls and helps you stay within your hourly rate limit.
Batch Requests Carefully
The VerseDB API supports standard CRUD operations per resource. When adding or updating multiple items:
For collections:
POST /api/collections/{id}/items # Add single item
DELETE /api/collections/{id}/items/{item_id} # Remove single item
For user lists:
POST /api/lists/{id}/items # Add single item
DELETE /api/lists/{id}/items/{item_id} # Remove single item
If you need to add multiple items, batch your requests with a small delay between them (100-200ms) to avoid rate limiting.
Monitor Rate Limit Headers
Every API response includes rate limit headers:
X-RateLimit-Limit: 300
X-RateLimit-Remaining: 247
X-RateLimit-Reset: 1712869200
Track these in your code:
const checkRateLimit = (response) => {
const remaining = response.headers.get('X-RateLimit-Remaining');
const reset = response.headers.get('X-RateLimit-Reset');
if (remaining < 50) {
console.warn(`Only ${remaining} requests remaining`);
}
if (remaining < 10) {
const resetDate = new Date(reset * 1000);
console.warn(`Rate limit low! Resets at ${resetDate}`);
}
};
Handle 429 Responses Gracefully
When you exceed your rate limit, you'll receive a 429 Too Many Requests response. Stop sending requests and wait for the reset time.
Implement exponential backoff:
async function apiRequestWithRetry(url, options = {}, maxRetries = 3) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
const response = await fetch(url, options);
if (response.status === 429) {
const retryAfter = parseInt(response.headers.get('Retry-After') || '60');
console.log(`Rate limited. Waiting ${retryAfter}s before retry ${attempt + 1}`);
await sleep(retryAfter * 1000);
continue;
}
return response;
}
throw new Error('Max retries exceeded');
}
Always Authenticate
All VerseDB API endpoints require authentication. Include your Bearer token in every request, even for read-only endpoints.
fetch('https://versedb.com/api/user/collections', {
headers: {
'Authorization': 'Bearer YOUR_TOKEN_HERE',
'Content-Type': 'application/json'
}
})
Leverage Search Endpoints Efficiently
Search endpoints have stricter rate limits (60/hour free, 120/hour PRO). Use them wisely:
- Implement debouncing for user input (wait 300ms after typing stops)
- Cache search results for common queries
- Use specific filters when available to reduce result size
- Limit search frequency in your UI
Use Nested Routes for Related Data (PRO)
PRO users can access nested routes to efficiently fetch related data:
GET /api/series/{id}/issues # Get issues for a series
GET /api/series/{id}/creators # Get creators for a series
GET /api/issues/{id}/variants # Get variants for an issue
GET /api/teams/{id}/characters # Get team members
Protect Your Token
- Store tokens in environment variables
- Use
.envfiles (add to.gitignore) - Regenerate tokens if exposed
- Use different tokens for different applications
- Never commit tokens to Git
- Never log tokens in application logs
- Never share tokens in forums/Discord
If your token is compromised:
- Go to versedb.com/user/api-tokens
- Delete the compromised token immediately
- Generate a new token
- Update your application with the new token
Handle Pagination Correctly
List endpoints return paginated results:
{
"data": [...],
"meta": {
"current_page": 1,
"per_page": 20,
"total": 500,
"last_page": 25
},
"links": {
"first": "/api/series?page=1",
"next": "/api/series?page=2",
"prev": null,
"last": "/api/series?page=25"
}
}
Iterate through pages:
async function fetchAllPages(baseUrl, token) {
let allData = [];
let nextUrl = baseUrl;
while (nextUrl) {
const response = await fetch(nextUrl, {
headers: { 'Authorization': `Bearer ${token}` }
});
const json = await response.json();
allData = allData.concat(json.data);
nextUrl = json.links.next ? `https://versedb.com${json.links.next}` : null;
// Respect rate limits between pages
await sleep(200);
}
return allData;
}
Test Connectivity First
Before implementing your integration, test your authentication:
const testConnection = async (token) => {
const response = await fetch('https://versedb.com/api/user', {
headers: { 'Authorization': `Bearer ${token}` }
});
if (response.ok) {
console.log('API connection successful');
console.log('Rate limit:', response.headers.get('X-RateLimit-Limit'));
} else {
console.error('Authentication failed:', response.status);
}
};
- Cache public data (publishers, series, characters)
- Monitor rate limit headers
- Implement exponential backoff for 429 errors
- Authenticate all requests with Bearer token
- Protect your API tokens
- Test authentication first
- Hammer the API with rapid requests
- Ignore 429 responses
- Share or expose tokens
- Skip pagination for large datasets
- Cache personal data too aggressively