Safe AI Workbench Developer Docs

Error Handling

Understand Guardian Health error formats, build resilient retry logic, and set up monitoring to keep your integration healthy.

400401403404422429500

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

application/json
{
  "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.

bash
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.

python
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.

javascript
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.

ruby
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