Error Handling
The USDA FDC Python Client provides a comprehensive error handling system to help you handle various error conditions gracefully.
Exception Hierarchy
The library defines the following exception hierarchy:
FdcApiError: Base exception for all API errors (HTTP 5xx and anything unmapped) -FdcAuthError: Authentication failed — HTTP 401 or 403. FDC sits behindapi.data.gov, which rejects an invalid key with 403, so both are treated as an auth failure.
FdcRateLimitError: API rate limit exceeded — HTTP 429FdcTimeoutError: The API did not respond within the client timeoutFdcValidationError: The API rejected the request — HTTP 400, typically a parameter outside the range FDC accepts (page_sizeabove 200, say)FdcResourceNotFoundError: Requested resource not found — HTTP 404. A food that does not exist is an ordinary outcome of a lookup, not a breakdown, so it is worth catching on its own.
Every one of these is an FdcApiError, so a broad except FdcApiError still
catches the lot.
Request Timeouts
Every request is issued with a timeout, so a server that accepts a connection but never answers cannot block the caller indefinitely. The default is 30 seconds, and it applies to both connect and read:
from usda_fdc import FdcClient, FdcTimeoutError
# Use the 30 second default, or set your own
client = FdcClient(api_key="your_api_key_here", timeout=5.0)
try:
food = client.get_food(1750340)
except FdcTimeoutError as e:
print(f"FDC was too slow: {e}")
FdcTimeoutError subclasses FdcApiError, so existing except FdcApiError
blocks keep catching timeouts. Catch it separately when you want to tell “slow”
apart from “broken” — a timeout is usually worth retrying, a 400 is not.
Basic Error Handling
Here’s how to handle errors when using the client:
from usda_fdc import FdcClient, FdcApiError, FdcAuthError, FdcRateLimitError
client = FdcClient(api_key="your_api_key_here")
try:
food = client.get_food(1750340)
except FdcAuthError:
print("Authentication failed. Check your API key.")
except FdcRateLimitError:
print("Rate limit exceeded. Try again later.")
except FdcApiError as e:
print(f"API error: {e}")
except Exception as e:
print(f"Unexpected error: {e}")
Handling Specific HTTP Status Codes
Every FdcApiError carries the HTTP status the API replied with, as
status_code. It is None for failures that never reached the API — a
refused connection, a timeout — which is itself worth knowing:
from usda_fdc import FdcClient, FdcApiError, FdcResourceNotFoundError
client = FdcClient(api_key="your_api_key_here")
try:
food = client.get_food(1750340)
except FdcResourceNotFoundError:
print("No such food") # usually clearer than reading the status
except FdcApiError as e:
if e.status_code is None:
print(f"Never reached the API: {e}")
elif e.status_code >= 500:
print("Server error. Try again later.")
else:
print(f"API error {e.status_code}: {e}")
Retry Logic
For transient errors like rate limiting or server errors, you can implement retry logic:
import time
from usda_fdc import FdcClient, FdcApiError, FdcRateLimitError, FdcTimeoutError
client = FdcClient(api_key="your_api_key_here")
def get_food_with_retry(fdc_id, max_retries=3, retry_delay=5):
retries = 0
while retries < max_retries:
try:
return client.get_food(fdc_id)
except (FdcRateLimitError, FdcTimeoutError):
# Both are "try again", not "you asked for the wrong thing"
retries += 1
if retries < max_retries:
print(f"Throttled or timed out. Retrying in {retry_delay} seconds...")
time.sleep(retry_delay)
retry_delay *= 2 # Exponential backoff
else:
raise
except FdcApiError as e:
if e.status_code is not None and e.status_code >= 500:
retries += 1
if retries < max_retries:
print(f"Server error. Retrying in {retry_delay} seconds...")
time.sleep(retry_delay)
retry_delay *= 2 # Exponential backoff
else:
raise
else:
# A 400 or a 404 will not fix itself
raise
# Use the retry function
try:
food = get_food_with_retry(1750340)
except Exception as e:
print(f"Failed after retries: {e}")
Logging Errors
It’s a good practice to log errors for debugging:
import logging
from usda_fdc import FdcClient, FdcApiError
# Configure logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger('usda_fdc')
client = FdcClient(api_key="your_api_key_here")
try:
food = client.get_food(1750340)
except FdcApiError as e:
logger.error(f"API error when getting food {1750340}: {e}", exc_info=True)
# Handle the error appropriately
except Exception as e:
logger.exception(f"Unexpected error when getting food {1750340}")
# Handle the error appropriately