ScavioScavio
ProductPricingDocs
Sign InGet Started
  1. Home
  2. Tutorials
  3. How to Build Retry-Safe Agent Tool Calls
Tutorial

How to Build Retry-Safe Agent Tool Calls

Handle API errors in AI agent tool calls gracefully. Retry logic, fallbacks, circuit breakers, and error classification.

Get Free API KeyAPI Docs

AI agents fail silently when API calls error out, producing hallucinated results instead of real data. This tutorial builds retry-safe tool call wrappers with exponential backoff, error classification, circuit breakers, and graceful fallbacks. Your agent never hallucinates a search result again.

Prerequisites

  • Python 3.8+
  • requests library
  • A Scavio API key from scavio.dev
  • An existing agent with tool calls

Walkthrough

Step 1: Build retry wrapper with exponential backoff

Wrap API calls with automatic retry and backoff for transient errors.

Python
import os, requests, json, time
from functools import wraps

API_KEY = os.environ['SCAVIO_API_KEY']
SH = {'x-api-key': API_KEY, 'Content-Type': 'application/json'}

def retry_with_backoff(max_retries=3, base_delay=1.0, max_delay=10.0):
    """Decorator: retry failed calls with exponential backoff."""
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            last_error = None
            for attempt in range(max_retries):
                try:
                    return func(*args, **kwargs)
                except requests.exceptions.Timeout:
                    last_error = 'timeout'
                    delay = min(base_delay * (2 ** attempt), max_delay)
                    print(f'  Retry {attempt+1}/{max_retries} after timeout ({delay:.1f}s)')
                    time.sleep(delay)
                except requests.exceptions.HTTPError as e:
                    status = e.response.status_code if e.response else 0
                    if status == 429:  # Rate limited
                        delay = min(base_delay * (2 ** attempt), max_delay)
                        print(f'  Retry {attempt+1}/{max_retries} after 429 ({delay:.1f}s)')
                        time.sleep(delay)
                    elif status >= 500:  # Server error
                        delay = min(base_delay * (2 ** attempt), max_delay)
                        print(f'  Retry {attempt+1}/{max_retries} after {status} ({delay:.1f}s)')
                        time.sleep(delay)
                    else:
                        raise  # Don't retry 4xx errors (except 429)
                except Exception as e:
                    last_error = str(e)
                    break
            return {'error': last_error or 'max_retries_exceeded', 'results': []}
        return wrapper
    return decorator

@retry_with_backoff(max_retries=3)
def safe_search(query, **kwargs):
    resp = requests.post('https://api.scavio.dev/api/v1/search',
        headers=SH, json={'query': query, 'country_code': 'us', **kwargs}, timeout=10)
    resp.raise_for_status()
    return resp.json()

result = safe_search('python web framework 2026')
print(f'Results: {len(result.get("organic_results", []))}')

Step 2: Add circuit breaker pattern

Stop calling a failing API after too many errors to avoid wasting budget.

Python
class CircuitBreaker:
    def __init__(self, failure_threshold=5, reset_timeout=60):
        self.failure_count = 0
        self.failure_threshold = failure_threshold
        self.reset_timeout = reset_timeout
        self.last_failure_time = 0
        self.state = 'closed'  # closed=normal, open=blocking, half_open=testing

    def call(self, func, *args, **kwargs):
        if self.state == 'open':
            if time.time() - self.last_failure_time > self.reset_timeout:
                self.state = 'half_open'
                print(f'  Circuit half-open: testing...')
            else:
                remaining = self.reset_timeout - (time.time() - self.last_failure_time)
                return {'error': f'circuit_open (retry in {remaining:.0f}s)', 'results': []}
        try:
            result = func(*args, **kwargs)
            if self.state == 'half_open':
                self.state = 'closed'
                self.failure_count = 0
                print(f'  Circuit closed: service recovered')
            return result
        except Exception as e:
            self.failure_count += 1
            self.last_failure_time = time.time()
            if self.failure_count >= self.failure_threshold:
                self.state = 'open'
                print(f'  Circuit OPEN: {self.failure_count} failures. Blocking calls for {self.reset_timeout}s.')
            return {'error': str(e), 'results': []}

search_breaker = CircuitBreaker(failure_threshold=3, reset_timeout=30)

def guarded_search(query):
    return search_breaker.call(safe_search, query)

# Test normal operation
result = guarded_search('test query')
print(f'Circuit state: {search_breaker.state}')
print(f'Failures: {search_breaker.failure_count}')

Step 3: Classify errors and provide agent-friendly messages

Give the agent clear error context so it can decide what to do next.

Python
def classify_error(error):
    """Convert raw errors into agent-friendly classifications."""
    error_str = str(error).lower()
    if 'timeout' in error_str:
        return {'type': 'transient', 'action': 'retry',
                'message': 'Search timed out. Retry with simpler query.'}
    if '429' in error_str or 'rate' in error_str:
        return {'type': 'rate_limit', 'action': 'wait',
                'message': 'Rate limited. Wait 10 seconds before next call.'}
    if '401' in error_str or '403' in error_str:
        return {'type': 'auth', 'action': 'stop',
                'message': 'API key invalid or expired. Cannot search.'}
    if '5' in error_str[:1]:
        return {'type': 'server', 'action': 'retry',
                'message': 'Server error. Retry in 5 seconds.'}
    if 'circuit' in error_str:
        return {'type': 'circuit_open', 'action': 'fallback',
                'message': 'Search API unavailable. Use cached results or skip.'}
    return {'type': 'unknown', 'action': 'log',
            'message': f'Unexpected error: {error_str[:100]}'}

def agent_safe_search(query):
    """Search with full error handling for agent use."""
    result = guarded_search(query)
    if 'error' in result and result['error']:
        classified = classify_error(result['error'])
        print(f'  Error: [{classified["type"]}] {classified["message"]}')
        print(f'  Action: {classified["action"]}')
        return {'status': 'error', **classified, 'results': []}
    results = result.get('organic_results', [])
    return {'status': 'ok', 'results': results, 'count': len(results)}

# Test
result = agent_safe_search('best ai framework 2026')
print(f'Status: {result["status"]}, Results: {result.get("count", 0)}')

Python Example

Python
import os, requests, time
SH = {'x-api-key': os.environ['SCAVIO_API_KEY'], 'Content-Type': 'application/json'}

def safe_search(query, retries=3):
    for i in range(retries):
        try:
            resp = requests.post('https://api.scavio.dev/api/v1/search',
                headers=SH, json={'query': query, 'country_code': 'us'}, timeout=10)
            resp.raise_for_status()
            return resp.json().get('organic_results', [])
        except Exception as e:
            if i < retries - 1:
                time.sleep(2 ** i)
            else:
                return []

results = safe_search('test query')
print(f'Results: {len(results)} (with retry safety)')

JavaScript Example

JavaScript
const SH = { 'x-api-key': process.env.SCAVIO_API_KEY, 'Content-Type': 'application/json' };
async function safeSearch(query, retries = 3) {
  for (let i = 0; i < retries; i++) {
    try {
      const resp = await fetch('https://api.scavio.dev/api/v1/search', {
        method: 'POST', headers: SH,
        body: JSON.stringify({ query, country_code: 'us' })
      });
      if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
      return (await resp.json()).organic_results || [];
    } catch (e) {
      if (i < retries - 1) await new Promise(r => setTimeout(r, 2 ** i * 1000));
    }
  }
  return [];
}
const results = await safeSearch('test query');
console.log(`Results: ${results.length}`);

Expected Output

JSON
Results: 10
Circuit state: closed
Failures: 0

Status: ok, Results: 10

# On failure:
  Error: [transient] Search timed out. Retry with simpler query.
  Action: retry

# After repeated failures:
  Circuit OPEN: 3 failures. Blocking calls for 30s.
  Error: [circuit_open] Search API unavailable. Use cached results or skip.
  Action: fallback

Related Tutorials

  • How to Build an Agent Search Retry Chain
  • How to Build an Agent Search Reliability Layer
  • How to Fix Agent Failed-to-Fetch Errors

Frequently Asked Questions

Most developers complete this tutorial in 15 to 30 minutes. You will need a Scavio API key (free tier works) and a working Python or JavaScript environment.

Python 3.8+. requests library. A Scavio API key from scavio.dev. An existing agent with tool calls. A Scavio API key gives you 50 free credits on signup.

Yes. The free tier includes 50 credits on signup, which is more than enough to complete this tutorial and prototype a working solution.

Scavio has a native LangChain package (langchain-scavio), an MCP server, and a plain REST API that works with any HTTP client. This tutorial uses the raw REST API, but you can adapt to your framework of choice.

Related Resources

Use Case

Agent Search Error Handling Patterns

Read more
Best Of

Best Tools for Building Agents Without Frameworks (2026)

Read more
Best Of

Best APIs for Agent Memory Search Grounding (2026)

Read more
Use Case

Multi-Platform Search API as AI Agent Tool

Read more
Solution

Coding Agent Search Tool Debugging

Read more
Glossary

Search API Provider Landscape (2026)

Read more

Start Building

Handle API errors in AI agent tool calls gracefully. Retry logic, fallbacks, circuit breakers, and error classification.

Get Free API KeyRead the Docs
ScavioScavio

Real-time search API for AI agents. Search every platform, not just Google.

Product

  • Features
  • Pricing
  • Dashboard
  • Affiliates

Developers

  • Documentation
  • API Reference
  • Quickstart
  • MCP Integration
  • Python SDK

Alternatives

  • Tavily Alternative
  • SerpAPI Alternative
  • Firecrawl Alternative
  • Exa Alternative

Tools

  • JSON Formatter
  • cURL to Code
  • Token Counter
  • All Tools

© 2026 Scavio. All rights reserved.

Featured on TAAFT
Terms of ServicePrivacy Policy