Running AI-powered features in production without proper load testing is like launching a rocket without checking the fuel gauge. When traffic spikes hit unexpectedly, your application either collapses or hemorrhages money through inefficient token usage. This guide walks you through building a production-grade load testing pipeline using Locust and k6 to benchmark, validate, and optimize AI API integrations—with real numbers from real deployments.

Case Study: How a Singapore SaaS Team Cut AI Costs by 84%

I worked with a Series-A B2B SaaS startup in Singapore that built an AI-powered contract analysis tool. Their product used GPT-4 for document parsing, and they were processing approximately 500,000 API calls per month through a major US-based provider. Within six months, they faced two existential problems:

The team had been using basic curl scripts for "testing" and had no visibility into token consumption patterns, retry behavior, or concurrent request bottlenecks. When they migrated their entire stack to HolySheep AI—a provider offering sub-50ms latency and ¥1=$1 pricing (85%+ cheaper than their ¥7.3/1K token previous rate)—they needed a proper benchmarking framework to validate the migration.

I helped them implement Locust + k6 for comprehensive load testing. The results after 30 days:

Why Load Testing AI APIs Is Different

Traditional web API load testing focuses on HTTP response times and throughput. AI API testing introduces unique challenges:

Tools: Locust vs. k6 for AI API Testing

FeatureLocustk6
LanguagePythonJavaScript/Go
Learning CurveLow (Python syntax)Medium (JS + k6-specific)
Distributed ModeBuilt-in master/workerRequires k6 Cloud or custom
Real-time UIYes (web dashboard)CLI only (or Cloud)
AI Token TrackingRequires custom codeRequires custom code
Best ForPython shops, quick iterationCI/CD pipelines, cloud-native
CostFree (self-hosted)Free (OSS) / Paid (Cloud)

We recommend using both: Locust for development/debugging with its interactive UI, and k6 for automated CI/CD pipelines and cloud-distributed testing.

Prerequisites

Project Structure

ai-load-testing/
├── locustfile.py           # Locust test definitions
├── k6-script.js            # k6 test definitions
├── config.yaml             # Test configuration
├── utils/
│   ├── token_tracker.py    # Token usage tracking
│   └── metrics_aggregator.py
├── data/
│   └── prompts.json        # Test prompt variations
└── reports/
    └── (generated reports)

Setting Up the HolySheep AI Test Client

First, let's create a reusable test client that handles authentication, retry logic, and metrics collection. This client will work with both Locust and k6.

import requests
import time
import json
from typing import Dict, Optional, Any
from dataclasses import dataclass, field
from datetime import datetime
import logging

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)


@dataclass
class TokenUsage:
    """Tracks token consumption for cost analysis."""
    prompt_tokens: int = 0
    completion_tokens: int = 0
    total_tokens: int = 0
    
    def add(self, prompt: int, completion: int):
        self.prompt_tokens += prompt
        self.completion_tokens += completion
        self.total_tokens += prompt + completion


@dataclass
class RequestMetrics:
    """Captures detailed metrics for each API call."""
    request_id: str
    timestamp: datetime
    latency_ms: float
    status_code: int
    model: str
    prompt_tokens: int = 0
    completion_tokens: int = 0
    error: Optional[str] = None
    
    def to_dict(self) -> Dict[str, Any]:
        return {
            "request_id": self.request_id,
            "timestamp": self.timestamp.isoformat(),
            "latency_ms": self.latency_ms,
            "status_code": self.status_code,
            "model": self.model,
            "prompt_tokens": self.prompt_tokens,
            "completion_tokens": self.completion_tokens,
            "error": self.error
        }


class HolySheepClient:
    """
    Production-ready client for HolySheep AI API load testing.
    Supports streaming, retries, and comprehensive metrics collection.
    """
    
    # 2026 Pricing Reference (USD per 1M output tokens)
    PRICING = {
        "gpt-4.1": 8.00,
        "claude-sonnet-4.5": 15.00,
        "gemini-2.5-flash": 2.50,
        "deepseek-v3.2": 0.42,
        "default": 1.00
    }
    
    def __init__(
        self,
        api_key: str,
        base_url: str = "https://api.holysheep.ai/v1",
        timeout: int = 60,
        max_retries: int = 3,
        retry_delay: float = 1.0
    ):
        self.api_key = api_key
        self.base_url = base_url.rstrip("/")
        self.timeout = timeout
        self.max_retries = max_retries
        self.retry_delay = retry_delay
        self.session = requests.Session()
        self.session.headers.update({
            "Authorization": f"Bearer {api_key}",
            "Content-Type": "application/json"
        })
        self.total_usage = TokenUsage()
        self.metrics_history: list[RequestMetrics] = []
    
    def _calculate_cost(self, model: str, completion_tokens: int) -> float:
        """Calculate cost in USD for a completion."""
        price_per_mtok = self.PRICING.get(model, self.PRICING["default"])
        return (completion_tokens / 1_000_000) * price_per_mtok
    
    def chat_completions(
        self,
        model: str,
        messages: list,
        temperature: float = 0.7,
        max_tokens: int = 1000,
        stream: bool = False
    ) -> tuple[dict, RequestMetrics]:
        """
        Send a chat completion request with full metrics tracking.
        Returns (response_dict, metrics_object).
        """
        request_id = f"req_{int(time.time() * 1000)}"
        start_time = time.perf_counter()
        
        payload = {
            "model": model,
            "messages": messages,
            "temperature": temperature,
            "max_tokens": max_tokens,
            "stream": stream
        }
        
        for attempt in range(self.max_retries):
            try:
                response = self.session.post(
                    f"{self.base_url}/chat/completions",
                    json=payload,
                    timeout=self.timeout
                )
                
                latency_ms = (time.perf_counter() - start_time) * 1000
                
                if response.status_code == 200:
                    data = response.json()
                    usage = data.get("usage", {})
                    prompt_tokens = usage.get("prompt_tokens", 0)
                    completion_tokens = usage.get("completion_tokens", 0)
                    
                    self.total_usage.add(prompt_tokens, completion_tokens)
                    
                    metrics = RequestMetrics(
                        request_id=request_id,
                        timestamp=datetime.now(),
                        latency_ms=latency_ms,
                        status_code=200,
                        model=model,
                        prompt_tokens=prompt_tokens,
                        completion_tokens=completion_tokens
                    )
                    self.metrics_history.append(metrics)
                    
                    return data, metrics
                    
                elif response.status_code == 429:
                    # Rate limited - retry with exponential backoff
                    wait_time = self.retry_delay * (2 ** attempt)
                    logger.warning(f"Rate limited, retrying in {wait_time}s")
                    time.sleep(wait_time)
                    continue
                    
                else:
                    error_msg = f"HTTP {response.status_code}: {response.text}"
                    metrics = RequestMetrics(
                        request_id=request_id,
                        timestamp=datetime.now(),
                        latency_ms=latency_ms,
                        status_code=response.status_code,
                        model=model,
                        error=error_msg
                    )
                    return {}, metrics
                    
            except requests.exceptions.Timeout:
                logger.warning(f"Request timeout on attempt {attempt + 1}")
                if attempt == self.max_retries - 1:
                    metrics = RequestMetrics(
                        request_id=request_id,
                        timestamp=datetime.now(),
                        latency_ms=self.timeout * 1000,
                        status_code=0,
                        model=model,
                        error="Timeout"
                    )
                    return {}, metrics
                    
            except requests.exceptions.RequestException as e:
                logger.error(f"Request failed: {e}")
                if attempt == self.max_retries - 1:
                    metrics = RequestMetrics(
                        request_id=request_id,
                        timestamp=datetime.now(),
                        latency_ms=(time.perf_counter() - start_time) * 1000,
                        status_code=0,
                        model=model,
                        error=str(e)
                    )
                    return {}, metrics
        
        return {}, RequestMetrics(
            request_id=request_id,
            timestamp=datetime.now(),
            latency_ms=0,
            status_code=0,
            model=model,
            error="Max retries exceeded"
        )
    
    def get_summary(self) -> Dict[str, Any]:
        """Generate a cost and performance summary."""
        total_requests = len(self.metrics_history)
        successful = sum(1 for m in self.metrics_history if m.status_code == 200)
        failed = total_requests - successful
        
        latencies = [m.latency_ms for m in self.metrics_history if m.status_code == 200]
        avg_latency = sum(latencies) / len(latencies) if latencies else 0
        p95_latency = sorted(latencies)[int(len(latencies) * 0.95)] if latencies else 0
        p99_latency = sorted(latencies)[int(len(latencies) * 0.99)] if latencies else 0
        
        # Calculate total cost across all models
        total_cost = sum(
            self._calculate_cost(m.model, m.completion_tokens)
            for m in self.metrics_history
        )
        
        return {
            "total_requests": total_requests,
            "successful": successful,
            "failed": failed,
            "error_rate": failed / total_requests if total_requests > 0 else 0,
            "total_tokens": self.total_usage.total_tokens,
            "prompt_tokens": self.total_usage.prompt_tokens,
            "completion_tokens": self.total_usage.completion_tokens,
            "estimated_cost_usd": total_cost,
            "avg_latency_ms": round(avg_latency, 2),
            "p95_latency_ms": round(p95_latency, 2),
            "p99_latency_ms": round(p99_latency, 2)
        }
    
    def reset_metrics(self):
        """Reset all tracked metrics."""
        self.total_usage = TokenUsage()
        self.metrics_history = []


Example initialization

client = HolySheepClient(

api_key="YOUR_HOLYSHEEP_API_KEY",

base_url="https://api.holysheep.ai/v1"

)

Locust Load Testing Implementation

Locust excels at simulating realistic user behavior with its Python-based task definitions. The following implementation includes weighted task distribution, gradual ramping, and real-time metrics reporting.

import os
from locust import HttpUser, task, between, events
from locust.runners import MasterRunner
import json
import logging

Import our custom client

from holy_sheep_client import HolySheepClient logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__)

Configuration

API_KEY = os.environ.get("HOLYSHEEP_API_KEY", "YOUR_HOLYSHEEP_API_KEY") BASE_URL = "https://api.holysheep.ai/v1"

Shared client instance (initialized on first user spawn)

_shared_client = None def get_client(): global _shared_client if _shared_client is None: _shared_client = HolySheepClient( api_key=API_KEY, base_url=BASE_URL ) return _shared_client

Test prompts organized by complexity

TEST_SCENARIOS = { "simple": { "model": "deepseek-v3.2", "messages": [ {"role": "user", "content": "What is 2 + 2? Answer in one word."} ], "weight": 40 # 40% of traffic }, "medium": { "model": "gemini-2.5-flash", "messages": [ {"role": "user", "content": "Explain quantum entanglement in 3 sentences."} ], "weight": 35 # 35% of traffic }, "complex": { "model": "gpt-4.1", "messages": [ {"role": "system", "content": "You are a code reviewer."}, {"role": "user", "content": "Review this Python function for bugs:\n\ndef fibonacci(n):\n if n <= 1:\n return n\n return fibonacci(n-1) + fibonacci(n-2)"} ], "weight": 20 # 20% of traffic }, "streaming": { "model": "claude-sonnet-4.5", "messages": [ {"role": "user", "content": "Count from 1 to 10, one number per line."} ], "weight": 5, # 5% of traffic "stream": True } } class AIAgentsUser(HttpUser): """ Simulates a user interacting with AI-powered features. Task weights determine traffic distribution. """ wait_time = between(0.5, 2.0) # Wait 0.5-2 seconds between requests def on_start(self): """Initialize the client when a virtual user starts.""" self.client_obj = get_client() self.request_count = 0 # Build weighted task list self.tasks_by_weight = [] for name, scenario in TEST_SCENARIOS.items(): self.tasks_by_weight.extend( [name] * scenario["weight"] ) def _make_request(self, scenario_name: str): """Execute a single API request with metrics collection.""" scenario = TEST_SCENARIOS[scenario_name] is_streaming = scenario.get("stream", False) try: response, metrics = self.client_obj.chat_completions( model=scenario["model"], messages=scenario["messages"], stream=is_streaming ) self.request_count += 1 # Log for Locust's web UI if metrics.status_code == 200: logger.info( f"Request {self.request_count}: {scenario_name} | " f"Latency: {metrics.latency_ms:.0f}ms | " f"Tokens: {metrics.completion_tokens}" ) else: logger.error( f"Request {self.request_count}: {scenario_name} | " f"Error: {metrics.error}" ) except Exception as e: logger.error(f"Unexpected error: {e}") @task def simple_query(self): """Low-complexity query - most common user interaction.""" self._make_request("simple") @task def medium_query(self): """Medium-complexity analytical query.""" self._make_request("medium") @task def complex_query(self): """High-complexity reasoning task.""" self._make_request("complex") @task def streaming_query(self): """Streaming response for real-time display.""" self._make_request("streaming") def on_stop(self): """Print summary when user stops.""" summary = self.client_obj.get_summary() logger.info(f"User summary: {json.dumps(summary, indent=2)}")

Event hooks for aggregate reporting

@events.test_start.add_listener def on_test_start(environment, **kwargs): logger.info("Load test starting...") client = get_client() client.reset_metrics() @events.test_stop.add_listener def on_test_stop(environment, **kwargs): logger.info("Load test completed!") client = get_client() summary = client.get_summary() print("\n" + "=" * 60) print("FINAL LOAD TEST REPORT") print("=" * 60) print(f"Total Requests: {summary['total_requests']}") print(f"Successful: {summary['successful']}") print(f"Failed: {summary['failed']}") print(f"Error Rate: {summary['error_rate']:.2%}") print(f"Avg Latency: {summary['avg_latency_ms']}ms") print(f"P95 Latency: {summary['p95_latency_ms']}ms") print(f"P99 Latency: {summary['p99_latency_ms']}ms") print(f"Total Tokens: {summary['total_tokens']:,}") print(f"Prompt Tokens: {summary['prompt_tokens']:,}") print(f"Completion Tokens: {summary['completion_tokens']:,}") print(f"Estimated Cost: ${summary['estimated_cost_usd']:.4f}") print("=" * 60)

To run the Locust test with a web UI:

locust -f locustfile.py \
    --host=https://api.holysheep.ai \
    --users=100 \
    --spawn-rate=10 \
    --run-time=5m \
    --headless \
    --csv=reports/locust_results

Or with the web UI for real-time visualization:

locust -f locustfile.py \
    --host=https://api.holysheep.ai \
    -p 8089

k6 Load Testing Implementation

k6 is ideal for CI/CD integration and cloud-distributed testing. The following script provides the same functionality with k6-specific constructs for better automation support.

// k6-script.js
// Run with: k6 run k6-script.js
// Cloud mode: k6 run -o cloud k6-script.js

import http from 'k6/http';
import { Rate, Trend, Counter, Gauge } from 'k6/metrics';
import { check, sleep } from 'k6';
import { SharedArray } from 'k6/data';

// Custom metrics
const latencyTrend = new Trend('ai_latency_ms');
const tokenCounter = new Counter('total_tokens');
const errorRate = new Rate('error_rate');
const successRate = new Rate('success_rate');
const costGauge = new Gauge('estimated_cost_usd');

// Configuration
const API_KEY = __ENV.HOLYSHEEP_API_KEY || 'YOUR_HOLYSHEEP_API_KEY';
const BASE_URL = 'https://api.holysheep.ai/v1';

// Pricing per 1M output tokens (USD)
const PRICING = {
    'gpt-4.1': 8.00,
    'claude-sonnet-4.5': 15.00,
    'gemini-2.5-flash': 2.50,
    'deepseek-v3.2': 0.42,
};

let totalTokens = 0;
let totalCost = 0;

// Test scenarios with weights
const scenarios = [
    {
        name: 'simple',
        weight: 40,
        model: 'deepseek-v3.2',
        max_tokens: 100,
        messages: [
            { role: 'user', content: 'What is 2 + 2? Answer in one word.' }
        ]
    },
    {
        name: 'medium',
        weight: 35,
        model: 'gemini-2.5-flash',
        max_tokens: 500,
        messages: [
            { role: 'user', content: 'Explain quantum entanglement in 3 sentences.' }
        ]
    },
    {
        name: 'complex',
        weight: 20,
        model: 'gpt-4.1',
        max_tokens: 2000,
        messages: [
            { role: 'system', content: 'You are a code reviewer.' },
            { role: 'user', content: 'Review this Python function for bugs:\n\ndef fibonacci(n):\n    if n <= 1:\n        return n\n    return fibonacci(n-1) + fibonacci(n-2)' }
        ]
    },
    {
        name: 'streaming',
        weight: 5,
        model: 'claude-sonnet-4.5',
        max_tokens: 500,
        stream: true,
        messages: [
            { role: 'user', content: 'Count from 1 to 10, one number per line.' }
        ]
    }
];

// Build weighted scenario selector
function selectScenario() {
    const rand = Math.random() * 100;
    let cumulative = 0;
    for (const scenario of scenarios) {
        cumulative += scenario.weight;
        if (rand <= cumulative) {
            return scenario;
        }
    }
    return scenarios[0];
}

// Calculate cost for a completion
function calculateCost(model, completionTokens) {
    const pricePerMTok = PRICING[model] || 1.00;
    return (completionTokens / 1_000_000) * pricePerMTok;
}

// Make API request with retry logic
function makeRequest(scenario) {
    const payload = JSON.stringify({
        model: scenario.model,
        messages: scenario.messages,
        temperature: 0.7,
        max_tokens: scenario.max_tokens,
        stream: scenario.stream || false
    });
    
    const params = {
        headers: {
            'Authorization': Bearer ${API_KEY},
            'Content-Type': 'application/json'
        },
        timeout: '60s'
    };
    
    const startTime = Date.now();
    let lastError = null;
    
    // Retry loop (max 3 attempts)
    for (let attempt = 0; attempt < 3; attempt++) {
        const response = http.post(
            ${BASE_URL}/chat/completions,
            payload,
            params
        );
        
        const latency = Date.now() - startTime;
        
        if (response.status === 200) {
            const data = JSON.parse(response.body);
            const usage = data.usage || {};
            const promptTokens = usage.prompt_tokens || 0;
            const completionTokens = usage.completion_tokens || 0;
            const tokens = promptTokens + completionTokens;
            const cost = calculateCost(scenario.model, completionTokens);
            
            // Update metrics
            latencyTrend.add(latency);
            tokenCounter.add(tokens);
            totalTokens += tokens;
            totalCost += cost;
            costGauge.add(totalCost);
            successRate.add(1);
            errorRate.add(0);
            
            return {
                success: true,
                latency: latency,
                tokens: tokens,
                cost: cost,
                response: data
            };
        } else if (response.status === 429) {
            // Rate limited - wait and retry
            sleep(Math.pow(2, attempt));
            lastError = 'Rate limited';
            continue;
        } else {
            lastError = HTTP ${response.status}: ${response.body};
            errorRate.add(1);
            successRate.add(0);
            return {
                success: false,
                latency: latency,
                error: lastError
            };
        }
    }
    
    errorRate.add(1);
    successRate.add(0);
    return {
        success: false,
        error: lastError || 'Max retries exceeded'
    };
}

// Test configuration
export const options = {
    scenarios: {
        // Ramp up from 0 to 100 users over 1 minute, hold for 3 minutes
        load_test: {
            executor: 'ramping-vus',
            startVUs: 0,
            stages: [
                { duration: '1m', target: 50 },
                { duration: '3m', target: 100 },
                { duration: '1m', target: 200 },
                { duration: '2m', target: 200 },
                { duration: '1m', target: 0 }
            ],
            tags: { test_type: 'load' }
        },
        // Stress test - spike to 500 users
        stress_test: {
            executor: 'ramping-arrival-rate',
            startRate: 1,
            timeUnit: '1s',
            preAllocatedVUs: 50,
            maxVUs: 500,
            stages: [
                { duration: '2m', target: 10 },
                { duration: '30s', target: 100 },
                { duration: '1m', target: 100 },
                { duration: '30s', target: 10 }
            ],
            tags: { test_type: 'stress' }
        }
    },
    thresholds: {
        'ai_latency_ms': ['p(95)<2000', 'p(99)<5000'],
        'error_rate': ['rate<0.05'],
        'success_rate': ['rate>0.95']
    },
    summaryTrendStats: ['avg', 'min', 'med', 'max', 'p(95)', 'p(99)']
};

export default function() {
    const scenario = selectScenario();
    const result = makeRequest(scenario);
    
    check(result, {
        'request succeeded': (r) => r.success === true,
        'latency under 2s': (r) => r.success && r.latency < 2000,
        'latency under 5s': (r) => r.success && r.latency < 5000
    });
    
    // Realistic think time between requests
    sleep(Math.random() * 2 + 0.5);
}

export function handleSummary(data) {
    return {
        'stdout': textSummary(data, { indent: ' ', enableColors: true }),
        'summary.json': JSON.stringify({
            metrics: data.metrics,
            totals: {
                requests: data.metrics['http_reqs']?.values?.count || 0,
                total_tokens: totalTokens,
                estimated_cost: totalCost.toFixed(4)
            }
        }, null, 2)
    };
}

function textSummary(data, opts) {
    const indent = opts.indent || '';
    let text = '\n' + '='.repeat(70) + '\n';
    text += indent + 'AI API LOAD TEST SUMMARY\n';
    text += '='.repeat(70) + '\n\n';
    
    text += indent + Total Requests:     ${data.metrics['http_reqs']?.values?.count || 0}\n;
    text += indent + Success Rate:        ${((data.metrics['success_rate']?.values?.rate || 0) * 100).toFixed(2)}%\n;
    text += indent + Error Rate:          ${((data.metrics['error_rate']?.values?.rate || 0) * 100).toFixed(2)}%\n;
    text += indent + Avg Latency:         ${(data.metrics['ai_latency_ms']?.values?.avg || 0).toFixed(0)}ms\n;
    text += indent + P95 Latency:         ${(data.metrics['ai_latency_ms']?.values?.['p(95)'] || 0).toFixed(0)}ms\n;
    text += indent + P99 Latency:         ${(data.metrics['ai_latency_ms']?.values?.['p(99)'] || 0).toFixed(0)}ms\n;
    text += indent + Total Tokens:        ${totalTokens.toLocaleString()}\n;
    text += indent + Estimated Cost:      $${totalCost.toFixed(4)}\n;
    text += '\n' + '='.repeat(70) + '\n';
    
    return text;
}

To run the k6 test locally:

k6 run k6-script.js \
    --env HOLYSHEEP_API_KEY="YOUR_HOLYSHEEP_API_KEY" \
    --summary-export=reports/k6_summary.json

For cloud-distributed testing with automatic report generation:

k6 login cloud
k6 run -o cloud k6-script.js \
    --env HOLYSHEEP_API_KEY="YOUR_HOLYSHEEP_API_KEY"

Interpreting Load Test Results

After running your tests, analyze these key metrics to determine if your AI integration is production-ready:

MetricGoodAcceptableCritical
P50 Latency< 200ms200-500ms> 500ms
P95 Latency< 1s1-3s> 3s
P99 Latency< 2s2-5s> 5s
Error Rate< 0.1%0.1-1%> 1%
Timeout Rate0%< 0.5%> 0.5%
Cost per 1K req< $0.50$0.50-2.00> $2.00

Common Errors and Fixes

Error 1: 401 Unauthorized - Invalid API Key

Symptom: All requests fail with "401 Unauthorized" or "Invalid API key" errors.

Cause: The API key is missing, malformed, or has been rotated.

# WRONG - Key not set
client = HolySheepClient(api_key="")

WRONG - Typo in header name

self.session.headers.update({ "Auth": f"Bearer {api_key}" # Should be "Authorization" })

CORRECT - Proper initialization

client = HolySheepClient( api_key="YOUR_HOLYSHEEP_API_KEY", base_url="https://api.holysheep.ai/v1" )

CORRECT - Environment variable usage

import os client = HolySheepClient( api_key=os.environ.get("HOLYSHEEP_API_KEY") )

Error 2: 429 Too Many Requests - Rate Limit Exceeded

Symptom: Requests succeed intermittently but fail with 429 errors during sustained load.

Cause: Exceeding the concurrent request limit or requests-per-minute quota.

# WRONG - No rate limiting strategy
for request in requests:
    make_request(request)  # Floods the API

CORRECT - Implement exponential backoff with jitter

import random import time def request_with_backoff(client, payload, max_retries=5): for attempt in range(max_retries): response = client.chat_completions(**payload) if response.status_code == 429: base_delay = 2 ** attempt # Exponential: 1, 2, 4, 8, 16 jitter = random.uniform(0, 1) # Add randomness wait_time = base_delay + jitter print(f"Rate limited, waiting {wait_time:.2f}s") time.sleep(wait_time) else: return response raise Exception("Max retries exceeded due to rate limiting")

CORRECT - Use semaphore to limit concurrency

import asyncio semaphore = asyncio.Semaphore(50) # Max 50 concurrent requests async def throttled_request(session, payload): async with semaphore: return await session.post(payload)

Error 3: Timeout Errors - Requests Hang Indefinitely

Symptom: Requests hang for over 60 seconds before failing, causing test timeouts.

Cause: No timeout configured, or timeout value is too high.

# WRONG - No timeout (will hang forever on network issues)
response = requests.post(url, json=payload)

WRONG - Timeout only on individual operations

response = requests.post(url, json=payload, timeout=30) # Only POST timeout

Long responses still timeout during read

CORRECT - Explicit timeout tuple (connect, read)

response = requests.post( url, json=payload, timeout=(5, 60) # 5s connect, 60s read )

CORRECT - Configurable timeout in client class

class HolySheepClient: def __init__(self, timeout=60): self.timeout = timeout # Set per-request def chat_completions(self, payload): return self.session.post( f"{self.base_url}/chat/completions", json=payload, timeout=(5, self.timeout)