Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/MiguelNavas19/miapibcv/llms.txt

Use this file to discover all available pages before exploring further.

Overview

Mi API BCV uses standard HTTP status codes and consistent JSON error responses to communicate issues. This guide covers all possible error scenarios and how to handle them in your application.

Error Response Format

All error responses follow this structure:
{
  "message": "Error description in Spanish"
}
Error messages are currently provided in Spanish to match the target audience in Venezuela.

HTTP Status Codes

The API uses the following HTTP status codes:
Status CodeMeaningWhen It Occurs
200OKRequest succeeded and data is returned
400Bad RequestInvalid date format or future date provided
404Not FoundNo data available for the requested parameters

Error Types

404 - Rates Not Available Yet

Source: app/Http/Controllers/Api/ScraperController.php:30 Endpoint: GET / When it occurs: When querying current rates before they’ve been scraped for today.
{
  "message": "Tasas no disponibles aún"
}
Translation: “Rates not yet available”
Common causes:
  • Requesting data too early in the morning before scraping runs
  • Scraping service hasn’t completed for today
  • Database is empty (fresh installation)
How to handle:
const response = await fetch('https://your-domain.com/api/');
const data = await response.json();

if (response.status === 404) {
  if (data.message === 'Tasas no disponibles aún') {
    // Rates not ready yet - retry later or show message to user
    console.log('Exchange rates not yet available for today');
    // Consider retrying after a delay
    setTimeout(() => fetchRates(), 60000); // Retry in 1 minute
  }
}

404 - No Data Found

Source: app/Http/Controllers/Api/ScraperController.php:67 Endpoint: GET /info/{date}/{source?} When it occurs: When no exchange rate data exists for the specified date or bank.
{
  "message": "No se encontraron datos"
}
Translation: “No data found”
Common causes:
  • Querying a date before the system started collecting data
  • Querying a weekend or holiday when banks don’t publish rates
  • Specifying a bank source that has no data for that date
  • Database doesn’t have historical data for the requested period
How to handle:
import requests
from datetime import datetime, timedelta

def fetch_rates_with_fallback(date):
    url = f"https://your-domain.com/api/info/{date}"
    response = requests.get(url)
    
    if response.status_code == 404:
        # Try previous day
        previous_date = (datetime.strptime(date, '%Y-%m-%d') - timedelta(days=1)).strftime('%Y-%m-%d')
        print(f"No data for {date}, trying {previous_date}")
        return fetch_rates_with_fallback(previous_date)
    
    return response.json()

400 - Invalid Date

Source: app/Http/Controllers/Api/ScraperController.php:50 Endpoint: GET /info/{date}/{source?} When it occurs: When the date parameter cannot be parsed or is in the future.
{
  "message": "Fecha inválida"
}
Translation: “Invalid date”
Common causes:
  • Malformed date string (e.g., 2026-13-45, invalid-date)
  • Future date provided (e.g., tomorrow’s date)
  • Date format that Carbon cannot parse
Examples of invalid dates:
# Future date
curl https://your-domain.com/api/info/2027-12-31
# Response: {"message": "Fecha inválida"}

# Malformed date
curl https://your-domain.com/api/info/2026-99-99
# Response: {"message": "Fecha inválida"}

# Invalid format
curl https://your-domain.com/api/info/not-a-date
# Response: {"message": "Fecha inválida"}
How to handle:
function validateDate(dateString) {
  const date = new Date(dateString);
  const today = new Date();
  
  // Check if date is valid
  if (isNaN(date.getTime())) {
    throw new Error('Invalid date format');
  }
  
  // Check if date is not in the future
  if (date > today) {
    throw new Error('Date cannot be in the future');
  }
  
  return dateString;
}

try {
  const date = validateDate('2026-03-04');
  const response = await fetch(`https://your-domain.com/api/info/${date}`);
  const data = await response.json();
  
  if (response.status === 400) {
    console.error('Server rejected date:', data.message);
  }
} catch (error) {
  console.error('Client-side validation failed:', error.message);
}

404 - Resource Not Found (Fallback)

Source: routes/api.php:10-14 Endpoint: Any undefined route When it occurs: When accessing an API endpoint that doesn’t exist.
{
  "error": "recurso no existente"
}
Translation: “resource does not exist”
Common causes:
  • Typo in the endpoint URL
  • Using incorrect HTTP method
  • Accessing a deprecated or removed endpoint
Example:
curl https://your-domain.com/api/invalid-endpoint
# Response: {"error": "recurso no existente"}
Note that this error uses the error key instead of message, different from other API errors.
How to handle:
$response = Http::get('https://your-domain.com/api/some-endpoint');
$data = $response->json();

if ($response->status() === 404) {
    if (isset($data['error']) && $data['error'] === 'recurso no existente') {
        // Invalid endpoint
        Log::error('Invalid API endpoint accessed');
        throw new \Exception('API endpoint not found');
    } elseif (isset($data['message'])) {
        // Data not found (valid endpoint)
        Log::info('No data available: ' . $data['message']);
    }
}

Error Handling Best Practices

1. Always Check Status Codes

async function fetchRates() {
  const response = await fetch('https://your-domain.com/api/');
  
  // Always check status before parsing
  if (!response.ok) {
    const errorData = await response.json();
    throw new Error(`API Error ${response.status}: ${errorData.message || errorData.error}`);
  }
  
  return response.json();
}

2. Implement Retry Logic for 404s

import time
import requests

def fetch_with_retry(url, max_retries=3, delay=60):
    """Retry fetching if rates aren't available yet."""
    for attempt in range(max_retries):
        response = requests.get(url)
        
        if response.status_code == 200:
            return response.json()
        
        if response.status_code == 404:
            data = response.json()
            if data.get('message') == 'Tasas no disponibles aún':
                if attempt < max_retries - 1:
                    print(f"Rates not ready, retrying in {delay}s...")
                    time.sleep(delay)
                    continue
        
        # Other errors or max retries reached
        response.raise_for_status()
    
    raise Exception('Max retries reached')

3. Validate Dates Client-Side

function formatDate(date) {
  // Ensure YYYY-MM-DD format
  const d = new Date(date);
  const year = d.getFullYear();
  const month = String(d.getMonth() + 1).padStart(2, '0');
  const day = String(d.getDate()).padStart(2, '0');
  return `${year}-${month}-${day}`;
}

function isValidQueryDate(date) {
  const queryDate = new Date(date);
  const today = new Date();
  today.setHours(23, 59, 59, 999); // End of today
  
  return queryDate <= today && !isNaN(queryDate.getTime());
}

// Usage
const date = '2026-03-04';
if (isValidQueryDate(date)) {
  const formattedDate = formatDate(date);
  const response = await fetch(`https://your-domain.com/api/info/${formattedDate}`);
}

4. Handle Missing Banks Gracefully

interface BankRate {
  value: string;
  date: string;
}

interface RatesResponse {
  message: string;
  bcv?: BankRate;
  banplus?: BankRate;
  bnc?: BankRate;
  bdv?: BankRate;
}

function getRateForBank(data: RatesResponse, bank: string): number | null {
  const bankData = data[bank as keyof RatesResponse];
  
  if (typeof bankData === 'object' && 'value' in bankData) {
    return parseFloat(bankData.value);
  }
  
  return null;
}

// Usage
const response = await fetch('https://your-domain.com/api/');
const data: RatesResponse = await response.json();

if (data.message === 'Consulta exitosa') {
  const bcvRate = getRateForBank(data, 'bcv');
  
  if (bcvRate === null) {
    console.warn('BCV rate not available');
  } else {
    console.log(`BCV rate: ${bcvRate}`);
  }
}

5. Log Errors for Debugging

import logging

logger = logging.getLogger(__name__)

def fetch_rates(date=None):
    url = f"https://your-domain.com/api/info/{date}" if date else "https://your-domain.com/api/"
    
    try:
        response = requests.get(url)
        data = response.json()
        
        if response.status_code != 200:
            logger.error(
                f"API Error: {response.status_code} - "
                f"{data.get('message') or data.get('error')} "
                f"(URL: {url})"
            )
            return None
        
        logger.info(f"Successfully fetched rates for {date or 'today'}")
        return data
        
    except requests.RequestException as e:
        logger.exception(f"Network error fetching rates: {e}")
        return None

Complete Error Handling Example

class ExchangeRateClient {
  constructor(baseUrl) {
    this.baseUrl = baseUrl;
  }
  
  async getCurrentRates(retries = 3) {
    for (let i = 0; i < retries; i++) {
      try {
        const response = await fetch(`${this.baseUrl}/`);
        const data = await response.json();
        
        if (response.status === 200) {
          return data;
        }
        
        if (response.status === 404 && data.message === 'Tasas no disponibles aún') {
          if (i < retries - 1) {
            console.log(`Attempt ${i + 1}: Rates not ready, retrying in 60s...`);
            await new Promise(resolve => setTimeout(resolve, 60000));
            continue;
          }
          throw new Error('Rates not available after multiple retries');
        }
        
        throw new Error(`HTTP ${response.status}: ${data.message || data.error}`);
        
      } catch (error) {
        if (i === retries - 1) throw error;
        console.error(`Attempt ${i + 1} failed:`, error.message);
      }
    }
  }
  
  async getHistoricalRates(date, source = null) {
    // Validate date client-side
    if (!this.isValidDate(date)) {
      throw new Error('Invalid date format. Use YYYY-MM-DD');
    }
    
    if (new Date(date) > new Date()) {
      throw new Error('Date cannot be in the future');
    }
    
    const url = source 
      ? `${this.baseUrl}/info/${date}/${source}`
      : `${this.baseUrl}/info/${date}`;
    
    const response = await fetch(url);
    const data = await response.json();
    
    if (response.status === 200) {
      return data;
    }
    
    // Handle specific error cases
    switch (response.status) {
      case 400:
        throw new Error(`Invalid date: ${data.message}`);
      case 404:
        if (data.message === 'No se encontraron datos') {
          throw new Error(`No data available for ${date}`);
        }
        throw new Error(`Endpoint not found: ${data.error}`);
      default:
        throw new Error(`Unexpected error: ${response.status}`);
    }
  }
  
  isValidDate(dateString) {
    const regex = /^\d{4}-\d{2}-\d{2}$/;
    if (!regex.test(dateString)) return false;
    
    const date = new Date(dateString);
    return !isNaN(date.getTime());
  }
}

// Usage
const client = new ExchangeRateClient('https://your-domain.com/api');

try {
  const rates = await client.getCurrentRates();
  console.log('Current rates:', rates);
} catch (error) {
  console.error('Failed to fetch rates:', error.message);
}

Troubleshooting Guide

Possible causes:
  • Scraping service is not running
  • Database is empty
  • Today’s date in server timezone hasn’t been scraped yet
Solutions:
  • Check if the scraping cron job is configured and running
  • Verify database has records with today’s date
  • Check server timezone vs. your local timezone
Possible causes:
  • Date is in the future (check server time vs. your time)
  • Date format needs adjustment
  • Server timezone difference
Solutions:
  • Ensure date is in YYYY-MM-DD format
  • Compare your timezone with the server’s
  • Use a date that’s definitely in the past (e.g., yesterday)
Possible causes:
  • Not all banks publish rates at the same time
  • Scraper failed for specific bank
  • Bank didn’t publish rates for that day
Solutions:
  • This is normal behavior - check later for complete data
  • Query specific banks individually to see which are available
  • Implement fallback logic to use previous day’s rate
Possible causes:
  • Wrong endpoint URL
  • Extra/missing slashes in URL
  • Incorrect HTTP method
Solutions:
  • Verify endpoint is exactly / or /info/{date}/{source?}
  • Check for typos in the URL
  • Ensure you’re using GET method

Next Steps

API Overview

Review API fundamentals and authentication

Response Structure

Understand response formats in detail

Build docs developers (and LLMs) love