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

# Handle errors

> Telling a rejected request from a failed page

Fetch fails in two places, and they need different handling.

A **rejected request** raises. Nothing was fetched and nothing was billed.
A **failed page** does not raise: the call returns `200`, and that URL's result
carries `status = "error"` with an `error.code`.

So a caller has to do both — catch around the call, and branch inside the loop.

<CodeGroup>
  ```python Python theme={null}
  from seltz import (
      FetchStatus,
      Seltz,
      SeltzAPIError,
      SeltzAuthenticationError,
      SeltzConnectionError,
      SeltzRateLimitError,
      SeltzTimeoutError,
  )

  client = Seltz()

  try:
      response = client.fetch(["https://example.com/"])
  except SeltzAuthenticationError:
      print("Error: Invalid API key")
  except SeltzConnectionError:
      print("Error: Could not connect to Seltz API")
  except SeltzTimeoutError:
      print("Error: Request timed out")
  except SeltzRateLimitError:
      print("Error: Rate limit exceeded, try again later")
  except SeltzAPIError as e:
      print(f"API Error: {e.grpc_code} - {e.grpc_details}")
  else:
      for result in response.results:
          if result.status == FetchStatus.FETCH_STATUS_OK:
              print(result.requested_url, len(result.markdown))
          else:
              print(result.requested_url, result.error.code, result.error.message)
  ```

  ```typescript TypeScript theme={null}
  import {
    FetchStatus,
    Seltz,
    SeltzAPIError,
    SeltzAuthenticationError,
    SeltzConnectionError,
    SeltzRateLimitError,
    SeltzTimeoutError,
  } from "seltz";

  const client = new Seltz();

  try {
    const response = await client.fetch({ urls: ["https://example.com/"] });

    for (const result of response.results) {
      if (result.status === FetchStatus.OK) {
        console.log(result.requestedUrl, result.markdown?.length);
      } else {
        console.log(result.requestedUrl, result.error?.code, result.error?.message);
      }
    }
  } catch (error) {
    if (error instanceof SeltzAuthenticationError) {
      console.error("Error: Invalid API key");
    } else if (error instanceof SeltzConnectionError) {
      console.error("Error: Could not connect to Seltz API");
    } else if (error instanceof SeltzTimeoutError) {
      console.error("Error: Request timed out");
    } else if (error instanceof SeltzRateLimitError) {
      console.error("Error: Rate limit exceeded, try again later");
    } else if (error instanceof SeltzAPIError) {
      console.error(`API error: ${error.code} - ${error.message}`);
    } else {
      throw error;
    }
  }
  ```
</CodeGroup>

<Note>
  Branch on `status`, not on whether `markdown` is set. A format the page could
  not produce is unset on an otherwise successful result.
</Note>

Treat an unrecognized `error.code` as a generic failure rather than rejecting
the result — the set grows. The `message` beside it is prose for logs, and its
wording can change; never parse it.

The per-URL codes and the request-level codes are listed in
[Reference](/fetch/reference#errors).
