Skip to main content

Best Practices for Using the VerseDB API

A few habits make the difference between an API integration that runs smoothly and one that hits rate limits, leaks tokens, or breaks silently.

Cache Data That Rarely Changes#

Catalog data doesn't change often. Cache it locally and cut your API calls dramatically.

Good to cache Don't cache
Publisher, creator, character, story arc data (hours to days) User collection, pull list, reading progress
Series and issue metadata (hours) Anything time-sensitive

Invalidate cache when you know data changed (you just mutated it) or on a reasonable schedule.

Watch Rate Limit Headers#

Every response includes X-RateLimit-Remaining. When it drops below ~10% of your limit, slow down.

function checkRateLimit(response) {
  const remaining = parseInt(response.headers.get('X-RateLimit-Remaining'));
  if (remaining < 30) console.warn(`Only ${remaining} requests remaining`);
}

Handle 429 Gracefully#

When you hit the limit, the server returns 429 with a Retry-After header. Honor it — don't retry immediately.

async function apiRequest(url, options = {}, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    const res = await fetch(url, options);
    if (res.status !== 429) return res;
    const retryAfter = parseInt(res.headers.get('Retry-After') || '60');
    await new Promise(r => setTimeout(r, retryAfter * 1000));
  }
  throw new Error('Max retries exceeded');
}

Instead of looping over items to fetch relationships one by one, use the nested endpoints (PRO):

GET /api/series/{id}/issues
GET /api/series/{id}/creators
GET /api/issues/{id}/variants
GET /api/teams/{id}/characters

One request per relationship beats dozens of single-item lookups.

Protect Your Token#

Warning
  • Store tokens in environment variables (add .env to .gitignore)
  • Give each integration its own token — revoke individually if compromised
  • Never paste tokens in Discord, forums, or client-side JavaScript
  • Rotate tokens on a schedule (e.g., every 90 days)

If a token is exposed: delete it at versedb.com/user/api-tokens, generate a new one, update your app.

Paginate Correctly#

List endpoints return up to 50 items per page (default 20). Walk links.next until it's null.

async function fetchAll(url, token) {
  let all = [];
  let next = url;
  while (next) {
    const res = await fetch(next, { headers: { Authorization: `Bearer ${token}` } });
    const json = await res.json();
    all = all.concat(json.data);
    next = json.links?.next ? `https://versedb.com${json.links.next}` : null;
    await new Promise(r => setTimeout(r, 200));  // spacing
  }
  return all;
}

Don't send a search request on every keystroke. Wait 300ms after the user stops typing, and cache common queries locally.

Test Authentication First#

Before building out an integration, verify your token with a single request to GET /api/user. If that returns your profile, your auth is working.