Best Practices for Using the VerseDB API

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
Tip
**Avoid Rate Limits**

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

Warning
**Never Hammer the API**

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

Note
**Authentication Required**

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:

Tip
**Search Best Practices**
  • 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

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
Note
These relationship endpoints return 402 for free users.

Protect Your Token

Warning
**Security Checklist**
  • Store tokens in environment variables
  • Use .env files (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:

  1. Go to versedb.com/user/api-tokens
  2. Delete the compromised token immediately
  3. Generate a new token
  4. 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);
  }
};

Quick Summary
  • 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