Skip to main content
< All Topics
Print

Tavily API Quick Reference – Factchecker Plugin

Tavily API Quick Reference – Factchecker Plugin

For Developers: Quick reference for implementing Tavily integration

## API Basics

Base URL: https://api.tavily.com

Authentication: API Key in request body

Method: POST

Content-Type: application/json

Primary Endpoint: Search

Request


POST https://api.tavily.com/search

{
  "api_key": "tvly-xxxxxxxxxxxxx",
  "query": "electric vehicle sales 2023 statistics",
  "search_depth": "advanced",
  "include_answer": true,
  "include_raw_content": false,
  "max_results": 5,
  "include_domains": ["reuters.com", "apnews.com", ".gov", ".edu"],
  "exclude_domains": ["unreliablesource.com"],
  "include_images": false
}

Response


{
  "query": "electric vehicle sales 2023 statistics",
  "answer": "Electric vehicle sales increased by approximately 40% in 2023...",
  "results": [
    {
      "title": "Global EV Outlook 2024",
      "url": "https://iea.org/reports/global-ev-outlook-2024",
      "content": "Electric vehicle sales reached record levels in 2023, with a 40% increase...",
      "score": 0.95,
      "published_date": "2024-03-15"
    },
    {
      "title": "EV Sales Hit Record in 2023",
      "url": "https://bloomberg.com/news/ev-sales-2023",
      "content": "Bloomberg analysis shows electric vehicle sales grew 40% year-over-year...",
      "score": 0.92,
      "published_date": "2024-01-10"
    }
  ],
  "response_time": 1.23,
  "images": []
}

Parameters Reference

Parameter Type Required Default Description
api_key string Yes Your Tavily API key
query string Yes Search query (max 400 chars)
search_depth string No “basic” “basic” or “advanced”
include_answer boolean No false Include AI-generated summary
include_raw_content boolean No false Include full page content
max_results integer No 5 Number of results (1-10)
include_domains array No [] Whitelist domains
exclude_domains array No [] Blacklist domains
include_images boolean No false Include image results

Search Depth Comparison

Feature Basic Advanced
Sources searched Fewer More comprehensive
Response time ~1-2 seconds ~2-4 seconds
Result quality Good Excellent
Cost 1 credit 2 credits
Best for Quick checks, common facts Critical claims, research

WordPress Implementation Example

1. Store API Key Securely


// Save API key (encrypted)
update_option('factchecker_tavily_api_key', 
    openssl_encrypt($api_key, 'AES-256-CBC', SECURE_KEY, 0, SECURE_IV)
);

// Retrieve API key
$encrypted_key = get_option('factchecker_tavily_api_key');
$api_key = openssl_decrypt($encrypted_key, 'AES-256-CBC', SECURE_KEY, 0, SECURE_IV);

2. Make API Request


function factchecker_tavily_search($query, $max_results = 5) {
    $api_key = factchecker_get_tavily_api_key();
    
    if (empty($api_key)) {
        return new WP_Error('no_api_key', 'Tavily API key not configured');
    }
    
    // Check cache first
    $cache_key = 'tavily_' . md5($query . $max_results);
    $cached = get_transient($cache_key);
    if ($cached !== false) {
        return $cached;
    }
    
    $body = json_encode([
        'api_key' => $api_key,
        'query' => $query,
        'search_depth' => get_option('factchecker_tavily_depth', 'advanced'),
        'include_answer' => true,
        'max_results' => $max_results,
        'include_domains' => factchecker_get_trusted_domains(),
        'exclude_domains' => factchecker_get_blocked_domains(),
    ]);
    
    $response = wp_remote_post('https://api.tavily.com/search', [
        'timeout' => 30,
        'headers' => [
            'Content-Type' => 'application/json',
        ],
        'body' => $body,
    ]);
    
    if (is_wp_error($response)) {
        return $response;
    }
    
    $status_code = wp_remote_retrieve_response_code($response);
    $body = wp_remote_retrieve_body($response);
    $data = json_decode($body, true);
    
    // Handle errors
    if ($status_code !== 200) {
        return factchecker_handle_tavily_error($status_code, $data);
    }
    
    // Cache successful response for 24 hours
    set_transient($cache_key, $data, DAY_IN_SECONDS);
    
    // Track usage
    factchecker_increment_tavily_usage();
    
    return $data;
}

3. Process Results


function factchecker_process_tavily_results($results, $claim) {
    if (empty($results['results'])) {
        return [
            'status' => 'unverified',
            'confidence' => 'low',
            'message' => 'No sources found to verify this claim.',
            'sources' => []
        ];
    }
    
    $sources = [];
    $high_credibility_count = 0;
    
    foreach ($results['results'] as $result) {
        $credibility = factchecker_assess_source_credibility($result['url']);
        
        if ($credibility === 'tier1' || $credibility === 'tier2') {
            $high_credibility_count++;
        }
        
        $sources[] = [
            'title' => $result['title'],
            'url' => $result['url'],
            'excerpt' => $result['content'],
            'published_date' => $result['published_date'] ?? null,
            'credibility' => $credibility,
            'relevance_score' => $result['score'],
        ];
    }
    
    // Determine verification status
    if ($high_credibility_count >= 3) {
        $status = 'verified';
        $confidence = 'high';
        $message = 'Multiple authoritative sources confirm this claim.';
    } elseif ($high_credibility_count >= 1) {
        $status = 'partially_verified';
        $confidence = 'medium';
        $message = 'Limited source support. Consider additional verification.';
    } else {
        $status = 'unverified';
        $confidence = 'low';
        $message = 'No highly credible sources found.';
    }
    
    return [
        'status' => $status,
        'confidence' => $confidence,
        'message' => $message,
        'sources' => $sources,
        'ai_summary' => $results['answer'] ?? null,
    ];
}

4. Source Credibility Assessment


function factchecker_assess_source_credibility($url) {
    $domain = parse_url($url, PHP_URL_HOST);
    
    // Tier 1: Highest credibility
    $tier1_patterns = [
        '/\.gov$/',
        '/\.mil$/',
        '/\.edu$/',
        '/^(www\.)?(reuters|apnews|bbc|npr)\.com$/',
        '/^(www\.)?(who|un|worldbank)\./',
    ];
    
    foreach ($tier1_patterns as $pattern) {
        if (preg_match($pattern, $domain)) {
            return 'tier1';
        }
    }
    
    // Tier 2: High credibility
    $tier2_domains = [
        'nytimes.com', 'washingtonpost.com', 'theguardian.com',
        'bloomberg.com', 'wsj.com', 'economist.com',
        'nature.com', 'science.org', 'pnas.org',
        'factcheck.org', 'snopes.com', 'politifact.com',
    ];
    
    foreach ($tier2_domains as $trusted) {
        if (strpos($domain, $trusted) !== false) {
            return 'tier2';
        }
    }
    
    // Tier 3: Moderate credibility
    return 'tier3';
}

Error Handling

HTTP Status Codes


function factchecker_handle_tavily_error($status_code, $data) {
    switch ($status_code) {
        case 401:
            return new WP_Error(
                'tavily_auth_failed',
                'Invalid Tavily API key. Please check your settings.',
                ['status' => 401]
            );
            
        case 429:
            return new WP_Error(
                'tavily_rate_limit',
                'Tavily API rate limit exceeded. Using cached results only.',
                ['status' => 429]
            );
            
        case 500:
        case 503:
            return new WP_Error(
                'tavily_server_error',
                'Tavily service temporarily unavailable. Continuing without real-time verification.',
                ['status' => $status_code]
            );
            
        default:
            return new WP_Error(
                'tavily_unknown_error',
                'Unexpected error from Tavily API.',
                ['status' => $status_code, 'data' => $data]
            );
    }
}

Usage Tracking

Track API Calls


function factchecker_increment_tavily_usage() {
    $current_month = date('Y-m');
    $usage_key = 'factchecker_tavily_usage_' . $current_month;
    
    $usage = get_option($usage_key, 0);
    update_option($usage_key, $usage + 1);
    
    // Check if approaching limit
    $quota = get_option('factchecker_tavily_quota', 1000);
    if ($usage >= $quota * 0.8) {
        factchecker_send_quota_warning($usage, $quota);
    }
}

function factchecker_get_tavily_usage() {
    $current_month = date('Y-m');
    $usage_key = 'factchecker_tavily_usage_' . $current_month;
    return get_option($usage_key, 0);
}

Cache Management


function factchecker_get_cache_stats() {
    global $wpdb;
    
    $cache_count = $wpdb->get_var(
        "SELECT COUNT(*) FROM {$wpdb->options} 
         WHERE option_name LIKE '_transient_tavily_%'"
    );
    
    return [
        'cached_queries' => $cache_count,
        'cache_size' => factchecker_get_cache_size(),
    ];
}

function factchecker_clear_tavily_cache() {
    global $wpdb;
    
    $wpdb->query(
        "DELETE FROM {$wpdb->options} 
         WHERE option_name LIKE '_transient_tavily_%' 
         OR option_name LIKE '_transient_timeout_tavily_%'"
    );
    
    return true;
}

Query Optimization

Generate Effective Queries


function factchecker_generate_tavily_query($claim) {
    // Remove common words
    $stopwords = ['the', 'a', 'an', 'and', 'or', 'but', 'in', 'on', 'at', 'to', 'for'];
    $words = explode(' ', strtolower($claim));
    $words = array_diff($words, $stopwords);
    
    // Extract key terms
    $query = implode(' ', $words);
    
    // Add context for better results
    if (preg_match('/\d{4}/', $claim, $year)) {
        // Year found, ensure it's in query
        if (strpos($query, $year[0]) === false) {
            $query .= ' ' . $year[0];
        }
    }
    
    // Limit length
    if (strlen($query) > 200) {
        $query = substr($query, 0, 200);
    }
    
    return trim($query);
}

Batch Similar Queries


function factchecker_batch_tavily_queries($claims) {
    $queries = [];
    
    foreach ($claims as $claim) {
        $query = factchecker_generate_tavily_query($claim);
        
        // Check for similar existing queries
        $similar_found = false;
        foreach ($queries as &$existing) {
            if (similar_text($query, $existing['query']) > 0.8) {
                $existing['claims'][] = $claim;
                $similar_found = true;
                break;
            }
        }
        
        if (!$similar_found) {
            $queries[] = [
                'query' => $query,
                'claims' => [$claim],
            ];
        }
    }
    
    return $queries;
}

Admin Settings UI

Settings Page Structure


function factchecker_render_tavily_settings() {
    ?>
    <div class="wrap">
        <h1>Tavily Integration Settings</h1>
        
        <form method="post" action="options.php">
            <?php settings_fields('factchecker_tavily'); ?>
            
            <table class="form-table">
                <tr>
                    <th scope="row">Enable Tavily</th>
                    <td>
                        <label>
                            <input type="checkbox" 
                                   name="factchecker_tavily_enabled" 
                                   value="1" 
                                   <?php checked(get_option('factchecker_tavily_enabled'), 1); ?>>
                            Enable real-time source verification
                        </label>
                    </td>
                </tr>
                
                <tr>
                    <th scope="row">API Key</th>
                    <td>
                        <input type="password" 
                               name="factchecker_tavily_api_key" 
                               value="<?php echo esc_attr(factchecker_get_tavily_api_key_masked()); ?>" 
                               class="regular-text">
                        <p class="description">
                            <a href="https://tavily.com/signup" target="_blank">Get your API key</a>
                        </p>
                        <button type="button" 
                                class="button" 
                                id="test-tavily-connection">
                            Test Connection
                        </button>
                        <span id="tavily-test-result"></span>
                    </td>
                </tr>
                
                <tr>
                    <th scope="row">Search Depth</th>
                    <td>
                        <label>
                            <input type="radio" 
                                   name="factchecker_tavily_depth" 
                                   value="basic" 
                                   <?php checked(get_option('factchecker_tavily_depth', 'advanced'), 'basic'); ?>>
                            Basic (faster, fewer sources)
                        </label><br>
                        <label>
                            <input type="radio" 
                                   name="factchecker_tavily_depth" 
                                   value="advanced" 
                                   <?php checked(get_option('factchecker_tavily_depth', 'advanced'), 'advanced'); ?>>
                            Advanced (thorough, more sources)
                        </label>
                    </td>
                </tr>
                
                <tr>
                    <th scope="row">Usage This Month</th>
                    <td>
                        <?php
                        $usage = factchecker_get_tavily_usage();
                        $quota = get_option('factchecker_tavily_quota', 1000);
                        $percentage = ($usage / $quota) * 100;
                        ?>
                        <div class="tavily-usage-bar">
                            <div class="tavily-usage-progress" 
                                 style="width: <?php echo $percentage; ?>%"></div>
                        </div>
                        <p><?php echo $usage; ?> / <?php echo $quota; ?> (<?php echo round($percentage, 1); ?>%)</p>
                        <button type="button" 
                                class="button" 
                                id="clear-tavily-cache">
                            Clear Cache
                        </button>
                    </td>
                </tr>
            </table>
            
            <?php submit_button(); ?>
        </form>
    </div>
    <?php
}

Testing

Test Connection


function factchecker_test_tavily_connection() {
    $result = factchecker_tavily_search('test query', 1);
    
    if (is_wp_error($result)) {
        return [
            'success' => false,
            'message' => $result->get_error_message(),
        ];
    }
    
    return [
        'success' => true,
        'message' => 'Connection successful! Tavily is working correctly.',
        'response_time' => $result['response_time'] ?? null,
    ];
}

AJAX Handler


add_action('wp_ajax_factchecker_test_tavily', 'factchecker_ajax_test_tavily');

function factchecker_ajax_test_tavily() {
    check_ajax_referer('factchecker_settings', 'nonce');
    
    if (!current_user_can('manage_options')) {
        wp_send_json_error(['message' => 'Unauthorized']);
    }
    
    $result = factchecker_test_tavily_connection();
    
    if ($result['success']) {
        wp_send_json_success($result);
    } else {
        wp_send_json_error($result);
    }
}

Rate Limiting

Client-Side Rate Limiting


function factchecker_check_rate_limit() {
    $usage = factchecker_get_tavily_usage();
    $quota = get_option('factchecker_tavily_quota', 1000);
    
    if ($usage >= $quota) {
        return new WP_Error(
            'quota_exceeded',
            'Monthly Tavily quota exceeded. Using cached results only.'
        );
    }
    
    return true;
}

Best Practices

1. Always Check Cache First


// ✅ Good
$cached = get_transient($cache_key);
if ($cached !== false) return $cached;
$result = factchecker_tavily_search($query);

// ❌ Bad
$result = factchecker_tavily_search($query); // Wastes API calls

2. Handle Errors Gracefully


// ✅ Good
$result = factchecker_tavily_search($query);
if (is_wp_error($result)) {
    // Continue with local analysis
    return factchecker_local_verification($claim);
}

// ❌ Bad
$result = factchecker_tavily_search($query);
// No error handling - analysis fails completely

3. Limit Queries Per Analysis


// ✅ Good
$max_queries = 10;
$queries_made = 0;
foreach ($claims as $claim) {
    if ($queries_made >= $max_queries) break;
    // Process claim
    $queries_made++;
}

// ❌ Bad
foreach ($claims as $claim) {
    // No limit - could make 100+ queries
}

4. Use Appropriate Search Depth


// ✅ Good
$depth = $is_critical_claim ? 'advanced' : 'basic';

// ❌ Bad
$depth = 'advanced'; // Always uses 2x credits

Troubleshooting

Common Issues

Issue: “Invalid API key”

  • Check: API key is correct and not expired
  • Check: API key is properly decrypted
  • Solution: Re-enter API key in settings

Issue: “Rate limit exceeded”

  • Check: Current usage vs. quota
  • Solution: Clear cache, upgrade plan, or wait for reset

Issue: “Slow response times”

  • Check: Using ‘advanced’ depth unnecessarily
  • Check: Too many results requested
  • Solution: Use ‘basic’ depth, reduce max_results

Issue: “No results returned”

  • Check: Query is well-formed
  • Check: Not too specific or too vague
  • Solution: Improve query generation logic

Resources

  • Tavily Documentation: https://docs.tavily.com
  • API Reference: https://docs.tavily.com/api-reference
  • Pricing: https://tavily.com/pricing
  • Support: support@tavily.com

Last Updated: January 9, 2026

Table of Contents