Skip to main content

Overview

This guide provides production-ready integration examples for the Iris API across multiple programming languages and frameworks. All examples use the actual endpoint structure and handle common scenarios like rate limiting, errors, and asynchronous processing.
These examples assume the API is running at http://localhost:8080. Replace with your actual API URL for production deployments.

Quick Start Examples

Get started quickly with these minimal examples:
# Basic face comparison
curl -X POST http://localhost:8080/compare \
  -H "Content-Type: application/json" \
  -d '{
    "target_url": "https://example.com/photos/target.jpg",
    "people": [
      {
        "name": "John Doe",
        "image_url": "https://example.com/photos/john.jpg"
      },
      {
        "name": "Jane Smith",
        "image_url": "https://example.com/photos/jane.jpg"
      }
    ]
  }'

# Response:
# {
#   "matches": [
#     {"name": "John Doe", "probability": 87.0},
#     {"name": "Jane Smith", "probability": 42.0}
#   ]
# }

Production-Ready Client Libraries

Robust client implementations with error handling, retries, and rate limiting:
// iris-client.ts
export interface Person {
  name: string;
  image_url: string;
}

export interface MatchResult {
  name: string;
  probability: number;
}

export interface CompareResponse {
  matches: MatchResult[];
}

export interface IrisClientOptions {
  baseUrl: string;
  maxRetries?: number;
  retryDelay?: number;
  timeout?: number;
}

export class IrisClient {
  private baseUrl: string;
  private maxRetries: number;
  private retryDelay: number;
  private timeout: number;

  constructor(options: IrisClientOptions) {
    this.baseUrl = options.baseUrl.replace(/\/$/, ''); // Remove trailing slash
    this.maxRetries = options.maxRetries ?? 3;
    this.retryDelay = options.retryDelay ?? 1000;
    this.timeout = options.timeout ?? 30000;
  }

  /**
   * Compare a target face against a list of known people
   */
  async compare(
    targetUrl: string,
    people: Person[],
    retryCount = 0
  ): Promise<CompareResponse> {
    const controller = new AbortController();
    const timeoutId = setTimeout(() => controller.abort(), this.timeout);

    try {
      const response = await fetch(`${this.baseUrl}/compare`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          target_url: targetUrl,
          people: people
        }),
        signal: controller.signal
      });

      clearTimeout(timeoutId);

      // Handle rate limiting with exponential backoff
      if (response.status === 429) {
        if (retryCount < this.maxRetries) {
          const delay = this.retryDelay * Math.pow(2, retryCount);
          console.warn(`Rate limited. Retrying in ${delay}ms... (${retryCount + 1}/${this.maxRetries})`);
          await this.sleep(delay);
          return this.compare(targetUrl, people, retryCount + 1);
        }
        throw new Error(`Rate limit exceeded after ${this.maxRetries} retries`);
      }

      // Handle server errors with retry
      if (response.status === 500) {
        if (retryCount < this.maxRetries) {
          const delay = this.retryDelay * Math.pow(2, retryCount);
          console.warn(`Server error. Retrying in ${delay}ms... (${retryCount + 1}/${this.maxRetries})`);
          await this.sleep(delay);
          return this.compare(targetUrl, people, retryCount + 1);
        }
        throw new Error(`Server error after ${this.maxRetries} retries`);
      }

      if (!response.ok) {
        throw new Error(`HTTP ${response.status}: ${response.statusText}`);
      }

      return await response.json();
    } catch (error) {
      clearTimeout(timeoutId);
      if (error instanceof Error && error.name === 'AbortError') {
        throw new Error(`Request timeout after ${this.timeout}ms`);
      }
      throw error;
    }
  }

  /**
   * Get API statistics
   */
  async getStats(): Promise<{
    total_requests: number;
    requests_last_second: number;
    requests_last_minute: number;
    requests_last_hour: number;
  }> {
    const response = await fetch(`${this.baseUrl}/stats`);
    if (!response.ok) {
      throw new Error(`Failed to fetch stats: ${response.statusText}`);
    }
    return await response.json();
  }

  /**
   * Check API health
   */
  async healthCheck(): Promise<boolean> {
    try {
      const response = await fetch(`${this.baseUrl}/health`);
      return response.ok && await response.text() === 'OK';
    } catch {
      return false;
    }
  }

  /**
   * Find the best match from results
   */
  getBestMatch(response: CompareResponse): MatchResult | null {
    if (response.matches.length === 0) return null;
    return response.matches[0]; // Already sorted by probability
  }

  /**
   * Filter matches by minimum probability threshold
   */
  filterByThreshold(response: CompareResponse, minProbability: number): MatchResult[] {
    return response.matches.filter(m => m.probability >= minProbability);
  }

  private sleep(ms: number): Promise<void> {
    return new Promise(resolve => setTimeout(resolve, ms));
  }
}

// Usage Example
async function main() {
  const client = new IrisClient({
    baseUrl: 'http://localhost:8080',
    maxRetries: 3,
    timeout: 30000
  });

  try {
    // Check health first
    const isHealthy = await client.healthCheck();
    if (!isHealthy) {
      console.error('API is not healthy');
      return;
    }

    // Compare faces
    const result = await client.compare(
      'https://example.com/photos/event.jpg',
      [
        { name: 'Alice Johnson', image_url: 'https://example.com/photos/alice.jpg' },
        { name: 'Bob Williams', image_url: 'https://example.com/photos/bob.jpg' },
        { name: 'Carol Brown', image_url: 'https://example.com/photos/carol.jpg' }
      ]
    );

    // Process results
    const bestMatch = client.getBestMatch(result);
    if (bestMatch && bestMatch.probability >= 70) {
      console.log(`Best match: ${bestMatch.name} (${bestMatch.probability}% confidence)`);
    } else {
      console.log('No confident matches found');
    }

    // Show all high-confidence matches
    const highConfidenceMatches = client.filterByThreshold(result, 60);
    console.log(`High confidence matches: ${highConfidenceMatches.length}`);
    highConfidenceMatches.forEach(match => {
      console.log(`  - ${match.name}: ${match.probability}%`);
    });

    // Get stats
    const stats = await client.getStats();
    console.log('API Stats:', stats);
  } catch (error) {
    console.error('Error:', error);
  }
}

main();

Real-World Use Cases

Use Case 1: Event Photo Matching

Match attendees in event photos against a guest list:
from iris_client import IrisClient, Person
from typing import List, Dict
import os

class EventPhotoMatcher:
    def __init__(self, api_url: str):
        self.client = IrisClient(base_url=api_url)
    
    def match_photo_to_guests(
        self,
        photo_url: str,
        guest_list: List[Dict[str, str]],
        min_confidence: float = 70.0
    ) -> List[str]:
        """Match a photo against event guest list"""
        
        # Convert guest list to Person objects
        people = [
            Person(name=guest["name"], image_url=guest["photo_url"])
            for guest in guest_list
        ]
        
        # Compare
        result = self.client.compare(photo_url, people)
        
        # Return high-confidence matches
        matches = self.client.filter_by_threshold(result, min_confidence)
        return [m.name for m in matches]
    
    def process_event_photos(
        self,
        photo_urls: List[str],
        guest_list: List[Dict[str, str]]
    ) -> Dict[str, List[str]]:
        """Process all event photos and identify guests"""
        results = {}
        
        for photo_url in photo_urls:
            matched_guests = self.match_photo_to_guests(photo_url, guest_list)
            if matched_guests:
                results[photo_url] = matched_guests
        
        return results

# Usage
matcher = EventPhotoMatcher("http://localhost:8080")

guest_list = [
    {"name": "Alice Smith", "photo_url": "https://example.com/guests/alice.jpg"},
    {"name": "Bob Jones", "photo_url": "https://example.com/guests/bob.jpg"},
    # ... more guests
]

event_photos = [
    "https://example.com/event/photo1.jpg",
    "https://example.com/event/photo2.jpg",
]

matches = matcher.process_event_photos(event_photos, guest_list)
for photo, guests in matches.items():
    print(f"{photo}: {', '.join(guests)}")

Use Case 2: Security Access Control

Verify identity for building access:
// security-access.js
import { IrisClient } from './iris-client';
import { createReadStream } from 'fs';
import FormData from 'form-data';

class SecurityAccessControl {
  constructor(apiUrl, authorizedPersons) {
    this.client = new IrisClient({ baseUrl: apiUrl });
    this.authorizedPersons = authorizedPersons;
  }

  async verifyAccess(capturedImageUrl) {
    try {
      const result = await this.client.compare(
        capturedImageUrl,
        this.authorizedPersons
      );

      const bestMatch = this.client.getBestMatch(result);
      
      // Strict threshold for security
      if (bestMatch && bestMatch.probability >= 85) {
        return {
          granted: true,
          person: bestMatch.name,
          confidence: bestMatch.probability,
          timestamp: new Date().toISOString()
        };
      }

      return {
        granted: false,
        reason: 'No match found or confidence too low',
        timestamp: new Date().toISOString()
      };
    } catch (error) {
      console.error('Access verification failed:', error);
      return {
        granted: false,
        reason: 'System error',
        error: error.message
      };
    }
  }

  async logAccessAttempt(result) {
    // Log to database or security system
    console.log('Access attempt:', JSON.stringify(result, null, 2));
  }
}

// Usage
const authorizedPersons = [
  { name: 'Employee 001 - John Doe', image_url: 'https://secure.example.com/employees/001.jpg' },
  { name: 'Employee 002 - Jane Smith', image_url: 'https://secure.example.com/employees/002.jpg' },
];

const accessControl = new SecurityAccessControl(
  'http://localhost:8080',
  authorizedPersons
);

// Verify access from door camera
const capturedImage = 'https://security.example.com/camera/capture-123.jpg';
const result = await accessControl.verifyAccess(capturedImage);

if (result.granted) {
  console.log(`✓ Access granted to ${result.person}`);
  // Trigger door unlock
} else {
  console.log(`✗ Access denied: ${result.reason}`);
  // Sound alarm or notify security
}

await accessControl.logAccessAttempt(result);

Use Case 3: Photo Organization

Automatically tag and organize photos by people:
package main

import (
	"fmt"
	"path/filepath"
	"strings"
	"your-module/iris"
)

type PhotoOrganizer struct {
	client *iris.Client
	knownPeople []iris.Person
}

func NewPhotoOrganizer(apiURL string, knownPeople []iris.Person) *PhotoOrganizer {
	return &PhotoOrganizer{
		client: iris.NewClient(apiURL),
		knownPeople: knownPeople,
	}
}

func (p *PhotoOrganizer) TagPhoto(photoURL string) ([]string, error) {
	result, err := p.client.Compare(photoURL, p.knownPeople)
	if err != nil {
		return nil, fmt.Errorf("comparison failed: %w", err)
	}

	// Get all matches above 60% confidence
	matches := iris.FilterByThreshold(result, 60.0)
	
	tags := make([]string, len(matches))
	for i, match := range matches {
		tags[i] = match.Name
	}
	
	return tags, nil
}

func (p *PhotoOrganizer) OrganizePhotoCollection(photoURLs []string) map[string][]string {
	photoTags := make(map[string][]string)
	
	for _, photoURL := range photoURLs {
		tags, err := p.TagPhoto(photoURL)
		if err != nil {
			fmt.Printf("Error tagging %s: %v\n", photoURL, err)
			continue
		}
		
		if len(tags) > 0 {
			photoTags[photoURL] = tags
			fmt.Printf("Tagged %s: %v\n", filepath.Base(photoURL), strings.Join(tags, ", "))
		}
	}
	
	return photoTags
}

func main() {
	knownPeople := []iris.Person{
		{Name: "Family/Mom", ImageURL: "https://photos.example.com/family/mom.jpg"},
		{Name: "Family/Dad", ImageURL: "https://photos.example.com/family/dad.jpg"},
		{Name: "Family/Sister", ImageURL: "https://photos.example.com/family/sister.jpg"},
		{Name: "Friends/Alice", ImageURL: "https://photos.example.com/friends/alice.jpg"},
	}
	
	organizer := NewPhotoOrganizer("http://localhost:8080", knownPeople)
	
	photos := []string{
		"https://photos.example.com/vacation/IMG_001.jpg",
		"https://photos.example.com/vacation/IMG_002.jpg",
		"https://photos.example.com/vacation/IMG_003.jpg",
	}
	
	tags := organizer.OrganizePhotoCollection(photos)
	fmt.Printf("\nOrganized %d photos with tags\n", len(tags))
}

Using Data URIs

For client-side uploads or when you have image data in memory:
// Convert File to Data URI and send to API
async function compareUploadedImage(file, knownPeople) {
  const client = new IrisClient({ baseUrl: 'http://localhost:8080' });
  
  // Convert File to Data URI
  const dataUri = await fileToDataUri(file);
  
  // Compare
  const result = await client.compare(dataUri, knownPeople);
  return result;
}

function fileToDataUri(file) {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = (e) => resolve(e.target.result);
    reader.onerror = reject;
    reader.readAsDataURL(file);
  });
}

// Usage in a form handler
document.getElementById('photoUpload').addEventListener('change', async (e) => {
  const file = e.target.files[0];
  if (!file) return;
  
  const knownPeople = [
    { name: 'John', image_url: 'https://example.com/john.jpg' }
  ];
  
  try {
    const result = await compareUploadedImage(file, knownPeople);
    console.log('Matches:', result.matches);
  } catch (error) {
    console.error('Error:', error);
  }
});

Batch Processing

Process multiple comparisons efficiently:
import asyncio
import aiohttp
from typing import List

class AsyncIrisClient:
    def __init__(self, base_url: str):
        self.base_url = base_url
        self.session = None
    
    async def __aenter__(self):
        self.session = aiohttp.ClientSession()
        return self
    
    async def __aexit__(self, *args):
        await self.session.close()
    
    async def compare(self, target_url: str, people: List[dict]) -> dict:
        async with self.session.post(
            f"{self.base_url}/compare",
            json={"target_url": target_url, "people": people}
        ) as response:
            if response.status == 429:
                # Wait and retry
                await asyncio.sleep(1)
                return await self.compare(target_url, people)
            response.raise_for_status()
            return await response.json()

async def batch_compare(photos: List[str], people: List[dict]):
    async with AsyncIrisClient("http://localhost:8080") as client:
        # Process with controlled concurrency
        semaphore = asyncio.Semaphore(4)  # Max 4 concurrent requests
        
        async def compare_with_limit(photo):
            async with semaphore:
                return await client.compare(photo, people)
        
        # Run all comparisons
        tasks = [compare_with_limit(photo) for photo in photos]
        results = await asyncio.gather(*tasks, return_exceptions=True)
        
        return results

# Usage
photos = [f"https://example.com/photo{i}.jpg" for i in range(100)]
people = [{"name": "Person", "image_url": "https://example.com/person.jpg"}]

results = asyncio.run(batch_compare(photos, people))
print(f"Processed {len(results)} photos")

Testing Your Integration

1

Verify API Health

curl http://localhost:8080/health
# Expected: OK
2

Test with Known Images

Use the same image as both target and person to verify high match:
curl -X POST http://localhost:8080/compare \
  -H "Content-Type: application/json" \
  -d '{
    "target_url": "https://example.com/test.jpg",
    "people": [{"name": "Test", "image_url": "https://example.com/test.jpg"}]
  }'
# Should return high probability (>90%)
3

Test Rate Limiting

Send requests rapidly to trigger 429:
for i in {1..20}; do
  curl -X POST http://localhost:8080/compare \
    -H "Content-Type: application/json" \
    -d '{"target_url": "https://example.com/test.jpg", "people": []}' &
done
wait
4

Monitor Statistics

curl http://localhost:8080/stats
# Returns request metrics

Best Practices

Use Connection Pooling

Reuse HTTP clients/sessions instead of creating new connections for each request.

Implement Timeouts

Set reasonable timeouts (30s recommended) to avoid hanging requests.

Handle Rate Limits

Implement exponential backoff when receiving 429 responses.

Validate Images First

Verify image URLs are accessible before sending to API.

Log Errors

Log all API errors for debugging and monitoring.

Cache Results

Cache comparison results to reduce repeated API calls.

Use Async Processing

Process multiple comparisons asynchronously when possible.

Monitor Performance

Track response times and success rates in production.

Next Steps

API Reference

Explore detailed endpoint documentation

Error Handling

Learn about error responses and troubleshooting

Authentication

Implement security for production deployments

Statistics

Monitor API usage and performance

Build docs developers (and LLMs) love