Error Handling
Understand Guardian Health error formats, build resilient retry logic, and set up monitoring to keep your integration healthy.
1. Understanding API Errors
Guardian Health responses include a structured error object when requests fail. Use the code, message, and request_id fields for troubleshooting.
Error response
Successful response codes: HTTP 429
{
"error": {
"code": "rate_limit_exceeded",
"message": "Too many requests. Please retry after 5 seconds.",
"request_id": "req_01HXM6SY93A4E6X8K8FQBBE4J2"
},
"meta": {
"status": 429,
"retry_after": 5
}
}2. Common HTTP Status Codes
- 400 Bad Request: Verify required parameters and JSON structure.
- 401 Unauthorized: API key missing, expired, or revoked.
- 403 Forbidden: Key lacks access to the requested dataset.
- 404 Not Found: Resource does not exist or the ID is incorrect.
- 422 Unprocessable Entity: Validation failure. Adjust parameters and retry.
- 429 Too Many Requests: Rate limit exceeded. Retry after the interval provided.
- 500 Internal Server Error: Transient failure. Retry with backoff and contact support if persistent.
3. Handling Rate Limits
Inspect the X-RateLimit-Remaining and X-RateLimit-Reset headers to manage throughput. Guardian Health responds with HTTP 429 when limits are exceeded.
Inspect rate limit headers
Use curl to view rate limit metadata.
curl -i "https://api.guardianhealth.dev/api/v1/patients" \\
-H "Authorization: Bearer $GUARDIAN_API_KEY"Retry with exponential backoff
Python helper for re-running requests after rate limits.
import os
import time
import requests
def get_with_backoff(url, params=None, headers=None, retries=5):
for attempt in range(1, retries + 1):
response = requests.get(url, params=params, headers=headers)
if response.status_code == 429 and attempt < retries:
retry_after = int(response.headers.get('Retry-After', attempt * 2))
time.sleep(retry_after)
continue
response.raise_for_status()
return response
raise RuntimeError("Exceeded maximum retries")
response = get_with_backoff(
"https://api.guardianhealth.dev/api/v1/patients",
headers={"Authorization": f"Bearer {os.environ["GUARDIAN_API_KEY"]}"}
)
print(response.json())4. Retry Logic
Queue requests and apply exponential backoff for transient errors. Ensure idempotency when retrying POST requests in the future—Guardian Health currently offers read-only endpoints.
Queue-based retries
JavaScript example using p-queue to throttle outbound requests.
import PQueue from "p-queue";
const queue = new PQueue({ concurrency: 5, interval: 1000, intervalCap: 10 });
async function request(endpoint) {
return queue.add(async () => {
const response = await fetch(`https://api.guardianhealth.dev${endpoint}`, {
headers: { Authorization: `Bearer ${process.env.GUARDIAN_API_KEY}` }
});
if (response.status === 429) {
const retryAfter = Number(response.headers.get("retry-after")) || 1;
await new Promise((resolve) => setTimeout(resolve, retryAfter * 1000));
return request(endpoint);
}
if (!response.ok) throw new Error(`Request failed: ${response.status}`);
return response.json();
});
}5. Logging and Monitoring
Log request IDs, status codes, and error messages to trace issues quickly. Configure alerts for sustained error rates or repeated 429 responses.
Structured logging
Ruby example that logs request IDs and errors to stdout.
require 'net/http'
require 'json'
require 'logger'
LOGGER = Logger.new($stdout)
def safe_get(path)
uri = URI("https://api.guardianhealth.dev#{path}")
request = Net::HTTP::Get.new(uri)
request['Authorization'] = "Bearer #{ENV['GUARDIAN_API_KEY']}"
Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
http.request(request) do |response|
LOGGER.info({ status: response.code, request_id: response["x-request-id"] }.to_json)
raise "Unauthorized" if response.code == "401"
return JSON.parse(response.body)
end
end
rescue => e
LOGGER.error({ error: e.message, path: path }.to_json)
raise
end