Best Practices

Production Optimization Guide

Advanced patterns, performance optimization, and reliability best practices for Predax integration.

Caching Strategy

Caching IP checks reduces API calls, improves response time, and saves on quota usage.

Recommended Cache TTL:

  • IP Risk Scores: 24 hours (risk profiles change slowly)
  • Geolocation: 7 days (rarely changes)
  • ASN/Network: 30 days (very stable)
  • High-Risk IPs: 6 hours (more dynamic)

Example: Redis Caching (Python)

import redis
import json
from datetime import timedelta

redis_client = redis.Redis(host='localhost', port=6379, db=0)

def check_ip_with_cache(ip_address):
    cache_key = f"ip_check:{ip_address}"

    # Try cache first
    cached = redis_client.get(cache_key)
    if cached:
        return json.loads(cached)

    # Cache miss - call API
    result = requests.post(
        'https://predax.io/api/v1/check/ip',
        headers={'X-API-Key': API_KEY},
        json={'ip': ip_address},
        timeout=2.0
    ).json()

    # Cache for 24 hours
    redis_client.setex(
        cache_key,
        timedelta(hours=24),
        json.dumps(result)
    )

    return result

Example: In-Memory Cache (Node.js)

const NodeCache = require('node-cache');
const cache = new NodeCache({ stdTTL: 86400 }); // 24 hours

async function checkIpWithCache(ipAddress) {
  const cacheKey = `ip_check:${ipAddress}`;

  // Try cache first
  const cached = cache.get(cacheKey);
  if (cached) {
    return cached;
  }

  // Cache miss - call API
  const response = await fetch('https://predax.io/api/v1/check/ip', {
    method: 'POST',
    headers: {
      'X-API-Key': API_KEY,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ ip: ipAddress }),
    signal: AbortSignal.timeout(2000)
  });

  const result = await response.json();

  // Cache result
  cache.set(cacheKey, result);

  return result;
}

Robust Error Handling

Handle API errors gracefully to ensure your application remains reliable even when issues occur.

Fail-Safe Principle:

When the API is unavailable or quota is exceeded, fail open (allow the request) rather than blocking all users. False negatives are better than false positives.

Example: Complete Error Handling (Python)

import requests
import logging
from requests.exceptions import Timeout, RequestException

logger = logging.getLogger(__name__)

def check_ip_safe(ip_address, user_id=None):
    """
    Check IP with comprehensive error handling.
    Returns (result_dict, is_cached_or_default)
    """
    try:
        response = requests.post(
            'https://predax.io/api/v1/check/ip',
            headers={
                'X-API-Key': API_KEY,
                'Content-Type': 'application/json'
            },
            json={'ip': ip_address, 'user_id': user_id},
            timeout=2.0
        )

        # Log request ID for debugging
        request_id = response.headers.get('X-Request-ID')
        logger.info(f"IP check for {ip_address}, request_id={request_id}")

        if response.status_code == 200:
            return response.json(), False

        elif response.status_code == 429:
            # Rate limited - wait and retry once
            logger.warning(f"Rate limited for {ip_address}, retrying...")
            time.sleep(1)
            return check_ip_safe(ip_address, user_id)

        elif response.status_code == 402:
            # Quota exceeded - fail open
            logger.error(f"Quota exceeded, failing open for {ip_address}")
            return get_default_result(ip_address), True

        elif response.status_code == 401:
            # Invalid API key - critical error
            logger.critical("Invalid API key!")
            return get_default_result(ip_address), True

        else:
            # Other error - fail open
            logger.error(f"API error {response.status_code} for {ip_address}")
            return get_default_result(ip_address), True

    except Timeout:
        logger.warning(f"Timeout checking {ip_address}, failing open")
        return get_default_result(ip_address), True

    except RequestException as e:
        logger.error(f"Request exception for {ip_address}: {e}")
        return get_default_result(ip_address), True

def get_default_result(ip_address):
    """Return safe default when API unavailable"""
    return {
        'ip': ip_address,
        'classification': {
            'risk_score': 0,
            'risk_level': 'unknown',
            'is_proxy': False,
            'is_vpn': False,
            'is_tor': False,
            'is_datacenter': False
        },
        'meta': {
            'request_id': 'fallback',
            'note': 'API unavailable - using defaults'
        }
    }

Retry Logic with Exponential Backoff

Implement smart retries for transient failures (429, 5xx) without overwhelming the API.

Example: Exponential Backoff (Python)

import time
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

def create_session_with_retries():
    """Create session with automatic retry logic"""
    session = requests.Session()

    # Retry on 429 (rate limit) and 5xx (server errors)
    retry_strategy = Retry(
        total=3,
        status_forcelist=[429, 500, 502, 503, 504],
        backoff_factor=1,  # 1s, 2s, 4s
        allowed_methods=["POST"]
    )

    adapter = HTTPAdapter(max_retries=retry_strategy)
    session.mount("https://", adapter)
    session.mount("http://", adapter)

    return session

# Usage
session = create_session_with_retries()
response = session.post(
    'https://predax.io/api/v1/check/ip',
    headers={'X-API-Key': API_KEY},
    json={'ip': '8.8.8.8'},
    timeout=2.0
)

Example: Manual Retry (Node.js)

async function checkIpWithRetry(ipAddress, maxRetries = 3) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      const response = await fetch('https://predax.io/api/v1/check/ip', {
        method: 'POST',
        headers: {
          'X-API-Key': API_KEY,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({ ip: ipAddress }),
        signal: AbortSignal.timeout(2000)
      });

      if (response.ok) {
        return await response.json();
      }

      // Retry on rate limit or server errors
      if (response.status === 429 || response.status >= 500) {
        if (attempt < maxRetries) {
          const backoffMs = Math.pow(2, attempt - 1) * 1000; // 1s, 2s, 4s
          console.log(`Retry ${attempt}/${maxRetries} after ${backoffMs}ms`);
          await new Promise(resolve => setTimeout(resolve, backoffMs));
          continue;
        }
      }

      throw new Error(`API error: ${response.status}`);

    } catch (error) {
      if (attempt === maxRetries) {
        throw error;
      }
    }
  }
}

Client-Side Rate Limiting

Prevent hitting rate limits by implementing client-side throttling.

Example: Token Bucket (Python)

import time
import threading

class RateLimiter:
    def __init__(self, max_requests_per_second):
        self.max_requests = max_requests_per_second
        self.tokens = max_requests_per_second
        self.last_update = time.time()
        self.lock = threading.Lock()

    def acquire(self):
        """Wait until a token is available"""
        with self.lock:
            now = time.time()
            elapsed = now - self.last_update

            # Replenish tokens
            self.tokens = min(
                self.max_requests,
                self.tokens + elapsed * self.max_requests
            )
            self.last_update = now

            if self.tokens >= 1:
                self.tokens -= 1
                return True

            # Wait for next token
            wait_time = (1 - self.tokens) / self.max_requests
            time.sleep(wait_time)
            self.tokens = 0
            return True

# Usage
rate_limiter = RateLimiter(max_requests_per_second=10)

def check_ip_rate_limited(ip_address):
    rate_limiter.acquire()
    return check_ip_safe(ip_address)

Batch Processing Optimization

Use the bulk endpoint to check multiple IPs efficiently (max 100 per request).

When to Use Bulk:

  • • Processing historical data
  • • Batch fraud review
  • • Log analysis
  • • Warming up caches

Example: Batch Processing (Python)

def check_ips_in_batches(ip_list, batch_size=100):
    """
    Check multiple IPs using bulk endpoint.
    Automatically chunks into batches of 100.
    """
    results = []

    for i in range(0, len(ip_list), batch_size):
        batch = ip_list[i:i + batch_size]

        try:
            response = requests.post(
                'https://predax.io/api/v1/check/ip/bulk',
                headers={
                    'X-API-Key': API_KEY,
                    'Content-Type': 'application/json'
                },
                json={'ips': batch},
                timeout=5.0
            )

            if response.status_code == 200:
                batch_results = response.json()
                results.extend(batch_results)
            else:
                logger.error(f"Bulk check failed: {response.status_code}")

        except Exception as e:
            logger.error(f"Bulk check exception: {e}")

        # Rate limiting between batches
        time.sleep(0.1)

    return results

# Usage
all_ips = ["1.1.1.1", "8.8.8.8", ...] # Your IP list
results = check_ips_in_batches(all_ips)

Monitoring & Alerting

Track API health, quota usage, and error rates in production.

Key Metrics to Monitor:

API Response Time

Track p50, p95, p99 latencies. Alert if p95 > 2s

Error Rate

Track 4xx and 5xx errors. Alert if > 1%

Quota Usage

Alert when 80% of daily or monthly quota used

Cache Hit Rate

Track cache effectiveness. Aim for > 60%

Example: Prometheus Metrics (Python)

from prometheus_client import Counter, Histogram

# Define metrics
api_requests_total = Counter(
    'predax_api_requests_total',
    'Total Predax API requests',
    ['status_code']
)

api_request_duration = Histogram(
    'predax_api_request_duration_seconds',
    'Predax API request duration'
)

quota_remaining = Gauge(
    'predax_quota_remaining',
    'Remaining quota',
    ['period']  # 'daily' or 'monthly'
)

# Track in API calls
@api_request_duration.time()
def check_ip_monitored(ip_address):
    try:
        response = requests.post(...)

        # Record metrics
        api_requests_total.labels(
            status_code=response.status_code
        ).inc()

        # Track quota
        remaining_today = int(
            response.headers.get('X-Quota-Remaining-Today', 0)
        )
        quota_remaining.labels(period='daily').set(remaining_today)

        return response.json()

    except Exception as e:
        api_requests_total.labels(status_code='error').inc()
        raise

Security Best Practices

Protect your API keys and implement secure integration patterns.

🔒 Never Expose API Keys

Never commit API keys to version control or expose them in client-side code. Use environment variables or secrets managers.

🔒 Use Backend Proxies

Always call Predax from your backend, never directly from browsers or mobile apps. This prevents key exposure and rate limit abuse.

🔒 Rotate Keys Periodically

Generate new API keys every 90 days and update your application. Keep old keys active briefly for zero-downtime rotation.

🔒 Use HTTPS Only

Always use https:// endpoints. Never use http:// in production.

Performance Optimization Tips

⚡ Run Checks Asynchronously

Don't block user requests waiting for IP checks. Use background jobs or async workers.

⚡ Check Only When Necessary

Only check IPs on sensitive actions (login, signup, checkout). Don't check every page view.

⚡ Use Connection Pooling

Reuse HTTP connections with session objects. Don't create new connections for each request.

⚡ Pre-warm Caches

For high-traffic applications, pre-populate your cache with known IPs using bulk checks.

Quick Reference Checklist

Before Production:

  • ✓ Implement caching (24h TTL)
  • ✓ Add timeout (2-3s)
  • ✓ Add retry logic with backoff
  • ✓ Implement fail-open error handling
  • ✓ Log X-Request-ID headers

Monitoring Setup:

  • ✓ Track API response time
  • ✓ Monitor error rates
  • ✓ Alert on 80% quota usage
  • ✓ Track cache hit rate
  • ✓ Monitor risk score distribution