Piloterr

Best Practices

Get the most out of the Piloterr API with these recommendations for reliability, efficiency, and security.

Follow these guidelines to build integrations that are reliable, cost-efficient, and easy to maintain.

Credit efficiency

Check your balance before large batches

Before triggering a high-volume operation, verify you have enough credits. The Usage endpoint is free and returns your current balance in real time.

curl "https://api.piloterr.com/v2/usage" \
  -H "x-api-key: YOUR_API_KEY"

Enable Auto Top-Up to automatically refill your balance when it drops below a threshold, so your workflows keep running without interruption.

Avoid redundant calls

Cache results on your side whenever the underlying data is unlikely to change between requests. Re-fetching identical inputs wastes credits.

const cache = new Map<string, unknown>()

async function fetchCached(key: string, fetcher: () => Promise<unknown>) {
  if (cache.has(key)) return cache.get(key)
  const result = await fetcher()
  cache.set(key, result)
  return result
}

Only call what you need

Each endpoint has a defined credit cost listed on the Credits page. Prefer lighter endpoints when a full response is not required.


Authentication

Store keys in environment variables

Never hardcode your API key in source code. Use environment variables and load them at runtime.

const apiKey = process.env.PILOTERR_API_KEY
if (!apiKey) throw new Error("API key is not set")
import os
api_key = os.environ["PILOTERR_API_KEY"]
$apiKey = getenv('PILOTERR_API_KEY');
apiKey := os.Getenv("PILOTERR_API_KEY")

Use separate keys per environment

Create distinct API keys for development, staging, and production in your dashboard. This lets you revoke or rotate a compromised key without affecting other environments.

EnvironmentRecommended category
Local devdevelopment
CI / stagingdevelopment
Productionproduction

Rotate keys periodically

Treat API keys like passwords. Set an expiry date on sensitive keys and rotate them on a regular schedule from the API Keys section of your account settings.

Never expose your API key in client-side code, browser requests, or public repositories. Always proxy calls through your own backend.


Error handling

Always check the HTTP status code

Do not assume a request succeeded. Every response should be checked against its HTTP status code before processing the body. See the Error Handling guide for a full breakdown.

Retry on transient errors

Server errors (500) and network timeouts are transient. Implement exponential backoff to retry automatically:

async function fetchWithRetry(url: string, options: RequestInit, maxRetries = 3) {
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    try {
      const res = await fetch(url, options)
      if (res.status === 500 && attempt < maxRetries) {
        await new Promise(r => setTimeout(r, 500 * 2 ** attempt))
        continue
      }
      return res
    } catch {
      if (attempt === maxRetries) throw new Error("Max retries reached")
      await new Promise(r => setTimeout(r, 500 * 2 ** attempt))
    }
  }
}

Never retry on 402 without topping up

A 402 Payment Required response means your balance is empty. Retrying the same call immediately will not help. Add credits from your dashboard or enable Auto Top-Up.


Rate limits

Spread requests over time

If you need to send a large number of calls, distribute them across time rather than firing them all at once. A simple delay between iterations prevents hitting rate limits.

const DELAY_MS = 100 // adjust based on your plan

for (const item of items) {
  await processItem(item)
  await new Promise(r => setTimeout(r, DELAY_MS))
}

Monitor the 401 Rate Limit Exceeded response

When rate-limited, the API returns 401 with status Rate Limit Exceeded. Back off and wait before retrying. Consider upgrading your plan if you consistently hit limits.


Security

Never make API calls from the browser

Your API key would be visible to anyone who inspects the network tab. Always route calls through your own server.

Browser → Your backend → Piloterr API

Validate and sanitize inputs

If your application accepts user-supplied parameters that are passed to the API (URLs, search queries, etc.), sanitize them to prevent injection or unexpected behavior.


Monitoring

Track credit consumption

Query the Usage endpoint on a schedule (e.g. daily) and alert when consumption exceeds a threshold. This prevents surprises at the end of a billing period.

MetricWhy it matters
remainingWarns you before credits run out
subscription.percent_usedTracks your plan utilization
credits.remainingMonitors bonus credit balance

Log request context

Store the HTTP status code and a timestamp for every API call. This makes it easy to audit credit consumption and debug failures after the fact.

async function apiCall(endpoint: string, params: Record<string, string>) {
  const url = new URL(`https://api.piloterr.com${endpoint}`)
  Object.entries(params).forEach(([k, v]) => url.searchParams.set(k, v))

  const res = await fetch(url.toString(), {
    headers: { "x-api-key": process.env.API_KEY! },
  })

  console.log(JSON.stringify({
    endpoint,
    status: res.status,
    billed: res.status === 200 || res.status === 201,
    timestamp: new Date().toISOString(),
  }))

  return res
}

Checklist

Use this checklist before going to production:

  • API key stored in an environment variable, not in source code
  • Separate keys for development and production
  • HTTP status codes checked on every response
  • Retry logic with exponential backoff for 500 errors
  • No retries on 402 without adding credits first
  • Rate limit handling in place
  • Credit balance monitored and alerts configured
  • Auto Top-Up enabled (or a manual top-up process defined)
  • All API calls proxied through your backend

See also: Error Handling for a complete list of status codes and Glossary for term definitions.

On this page