> ## Documentation Index
> Fetch the complete documentation index at: https://docs.alura.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Handling errors

> Read problem details, retry what can be retried, and stop on what can't.

Every error has a stable `code`, a `suggestion` and a `retryable` flag (see [Errors](/api-reference/errors)). A good client:

1. Branches on `code`, never on the wording of `title` or `detail`.
2. Retries only when `retryable` is `true`, waiting for `Retry-After` when the response has it and backing off when it doesn't.
3. Stops on everything else and shows `suggestion`, because the same request will fail the same way.
4. Logs `request_id` from the body or the `Request-Id` header.

This helper does that. The call below sends a misspelled parameter on purpose, so you can see the answer:

<CodeGroup>
  ```bash cURL theme={null}
  # Errors are application/problem+json; -i shows the Request-Id and Retry-After headers too.
  curl -sS -i -G https://api.alura.io/v1/research/products \
    -H "Authorization: Bearer $ALURA_API_KEY" \
    --data-urlencode "q=mug" \
    --data-urlencode "sort_by=price"
  ```

  ```typescript TypeScript theme={null}
  interface Problem {
    code: string;
    detail: string;
    suggestion: string;
    retryable: boolean;
    request_id: string;
  }

  /** GET an Alura path, retrying errors marked `retryable` (up to 3 attempts). */
  async function alura(path: string, attempts = 3): Promise<Response> {
    for (let attempt = 1; ; attempt++) {
      const res = await fetch(`https://api.alura.io${path}`, { headers: { Authorization: `Bearer ${process.env.ALURA_API_KEY}` } });
      if (res.ok || attempt >= attempts) return res;
      const problem = (await res.clone().json()) as Problem;
      if (!problem.retryable) return res;
      // Retry-After is in seconds; without it, back off 2, 4, 8... seconds.
      const wait = Number(res.headers.get('Retry-After') ?? 2 ** attempt);
      await new Promise((resolve) => setTimeout(resolve, wait * 1000));
    }
  }

  const res = await alura('/v1/research/products?q=mug&sort_by=price');
  if (!res.ok) {
    const problem = (await res.json()) as Problem;
    console.log(`${res.status} ${problem.code}: ${problem.suggestion}`);
  }
  ```

  ```python Python theme={null}
  import json
  import os
  import time
  import urllib.error
  import urllib.request


  def alura(path, attempts=3):
      """GET an Alura path, retrying errors marked `retryable` (up to 3 attempts). Returns (status, body)."""
      for attempt in range(1, attempts + 1):
          request = urllib.request.Request(
              f"https://api.alura.io{path}",
              headers={"Authorization": f"Bearer {os.environ['ALURA_API_KEY']}"},
          )
          try:
              with urllib.request.urlopen(request) as response:
                  return response.status, json.load(response)
          except urllib.error.HTTPError as error:
              problem = json.load(error)
              if not problem["retryable"] or attempt == attempts:
                  return error.code, problem
              # Retry-After is in seconds; without it, back off 2, 4, 8... seconds.
              time.sleep(int(error.headers.get("Retry-After") or 2**attempt))


  status, body = alura("/v1/research/products?q=mug&sort_by=price")
  if status >= 400:
      print(f"{status} {body['code']}: {body['suggestion']}")
  ```
</CodeGroup>

The ones you are most likely to meet:

| Code | Retry? | What to do |
| - | - | - |
| `rate_limited` | Yes | Wait for `Retry-After`; slow down |
| `credits_exhausted` | No | The month's API credits are used up: upgrade with `upgrade_url`, or wait for `resets_at` |
| `daily_credit_cap_exceeded` | Yes | Wait for the next UTC day |
| `fetch_unavailable`, `fetch_limit_reached` | Yes | Alura can't fetch that new item now; use what it already has |
| `not_found` | No | Check the ID, or search for it |
| `service_unavailable`, `credits_unavailable` | Yes | Retry shortly; nothing was charged |

Errors never cost API credits, so a retry that fails again costs nothing.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.