Skip to content

Error handling ​

This page shows practical patterns for handling non-2xx responses, timeouts, retries, and pagination.

For the canonical schema, see The error object.

Handle non-2xx responses ​

bash
curl https://api.fastapi.ai/v1/responses \
  -H "Authorization: Bearer $FAST_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"gpt-4.1","input":"Hello"}' \
  -i
javascript
const res = await fetch('https://api.fastapi.ai/v1/responses', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.FAST_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ model: 'gpt-4.1', input: 'Hello' }),
});

if (!res.ok) {
  const text = await res.text();
  throw new Error(`HTTP ${res.status}: ${text}`);
}

console.log(await res.json());
python
import os
import requests

resp = requests.post(
  "https://api.fastapi.ai/v1/responses",
  headers={
    "Authorization": f"Bearer {os.environ['FAST_API_KEY']}",
    "Content-Type": "application/json",
  },
  json={"model": "gpt-4.1", "input": "Hello"},
)

if resp.status_code >= 400:
  raise RuntimeError(f"HTTP {resp.status_code}: {resp.text}")

print(resp.json())
go
package main

import (
  "bytes"
  "encoding/json"
  "fmt"
  "io"
  "net/http"
  "os"
)

func main() {
  payload := map[string]any{"model": "gpt-4.1", "input": "Hello"}
  b, _ := json.Marshal(payload)

  req, _ := http.NewRequest("POST", "https://api.fastapi.ai/v1/responses", bytes.NewReader(b))
  req.Header.Set("Authorization", "Bearer "+os.Getenv("FAST_API_KEY"))
  req.Header.Set("Content-Type", "application/json")

  resp, err := http.DefaultClient.Do(req)
  if err != nil {
    panic(err)
  }
  defer resp.Body.Close()

  body, _ := io.ReadAll(resp.Body)
  if resp.StatusCode >= 400 {
    panic(fmt.Errorf("HTTP %d: %s", resp.StatusCode, string(body)))
  }

  fmt.Println(string(body))
}
java
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class Main {
  public static void main(String[] args) throws Exception {
    String body = "{\"model\":\"gpt-4.1\",\"input\":\"Hello\"}";

    HttpRequest req = HttpRequest.newBuilder()
      .uri(URI.create("https://api.fastapi.ai/v1/responses"))
      .header("Authorization", "Bearer " + System.getenv("FAST_API_KEY"))
      .header("Content-Type", "application/json")
      .POST(HttpRequest.BodyPublishers.ofString(body))
      .build();

    HttpResponse<String> resp = HttpClient.newHttpClient().send(req, HttpResponse.BodyHandlers.ofString());
    if (resp.statusCode() >= 400) {
      throw new RuntimeException("HTTP " + resp.statusCode() + ": " + resp.body());
    }
    System.out.println(resp.body());
  }
}

Timeouts ​

bash
curl https://api.fastapi.ai/v1/responses \
  -H "Authorization: Bearer $FAST_API_KEY" \
  -H "Content-Type: application/json" \
  --max-time 30 \
  -d '{"model":"gpt-4.1","input":"Hello"}'
javascript
const controller = new AbortController();
const t = setTimeout(() => controller.abort(), 30_000);

try {
  const res = await fetch('https://api.fastapi.ai/v1/responses', {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${process.env.FAST_API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ model: 'gpt-4.1', input: 'Hello' }),
    signal: controller.signal,
  });

  console.log(res.status, await res.text());
} finally {
  clearTimeout(t);
}
python
import os
import requests

resp = requests.post(
  "https://api.fastapi.ai/v1/responses",
  headers={
    "Authorization": f"Bearer {os.environ['FAST_API_KEY']}",
    "Content-Type": "application/json",
  },
  json={"model": "gpt-4.1", "input": "Hello"},
  timeout=30,
)

print(resp.status_code, resp.text)
go
package main

import (
  "bytes"
  "encoding/json"
  "fmt"
  "io"
  "net/http"
  "os"
  "time"
)

func main() {
  payload := map[string]any{"model": "gpt-4.1", "input": "Hello"}
  b, _ := json.Marshal(payload)

  client := &http.Client{Timeout: 30 * time.Second}
  req, _ := http.NewRequest("POST", "https://api.fastapi.ai/v1/responses", bytes.NewReader(b))
  req.Header.Set("Authorization", "Bearer "+os.Getenv("FAST_API_KEY"))
  req.Header.Set("Content-Type", "application/json")

  resp, err := client.Do(req)
  if err != nil {
    panic(err)
  }
  defer resp.Body.Close()

  body, _ := io.ReadAll(resp.Body)
  fmt.Println(resp.StatusCode, string(body))
}
java
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;

public class Main {
  public static void main(String[] args) throws Exception {
    String body = "{\"model\":\"gpt-4.1\",\"input\":\"Hello\"}";

    HttpClient client = HttpClient.newBuilder()
      .connectTimeout(Duration.ofSeconds(30))
      .build();

    HttpRequest req = HttpRequest.newBuilder()
      .uri(URI.create("https://api.fastapi.ai/v1/responses"))
      .header("Authorization", "Bearer " + System.getenv("FAST_API_KEY"))
      .header("Content-Type", "application/json")
      .timeout(Duration.ofSeconds(30))
      .POST(HttpRequest.BodyPublishers.ofString(body))
      .build();

    HttpResponse<String> resp = client.send(req, HttpResponse.BodyHandlers.ofString());
    System.out.println(resp.statusCode() + " " + resp.body());
  }
}

Retries (basic) ​

Retry is typically only safe for idempotent requests (for example, GET and some POSTs if you use your own idempotency keying).

The most common retry candidates are transient failures:

  • 429 (rate limits)
  • 500, 502, 503, 504 (server/network issues)
bash
url="https://api.fastapi.ai/v1/responses"
payload='{"model":"gpt-4.1","input":"Hello"}'

for i in 0 1 2 3 4; do
  resp=$(curl -s -w "\n%{http_code}" "$url" \
    -H "Authorization: Bearer $FAST_API_KEY" \
    -H "Content-Type: application/json" \
    -d "$payload")
  code=$(echo "$resp" | tail -n1)
  body=$(echo "$resp" | sed '$d')

  if [ "$code" -lt 400 ]; then
    echo "$body"
    break
  fi

  case "$code" in
    429|500|502|503|504)
      case "$i" in
        0) delay=0.2 ;;
        1) delay=0.4 ;;
        2) delay=0.8 ;;
        3) delay=1.6 ;;
        *) delay=2 ;;
      esac
      sleep "$delay"
      ;;
    *) echo "HTTP $code: $body" && exit 1 ;;
  esac
done
javascript
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));

async function postWithRetry(url, body, { max = 5 } = {}) {
  for (let attempt = 0; attempt < max; attempt++) {
    const res = await fetch(url, {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${process.env.FAST_API_KEY}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(body),
    });

    if (res.ok) return res.json();

    const retryable = [429, 500, 502, 503, 504].includes(res.status);
    if (!retryable || attempt === max - 1) {
      throw new Error(`HTTP ${res.status}: ${await res.text()}`);
    }

    // Exponential backoff with cap.
    const delay = Math.min(2000, 200 * 2 ** attempt);
    await sleep(delay);
  }
}

console.log(
  await postWithRetry('https://api.fastapi.ai/v1/responses', { model: 'gpt-4.1', input: 'Hello' })
);
python
import os
import time
import requests

url = "https://api.fastapi.ai/v1/responses"
headers = {
  "Authorization": f"Bearer {os.environ['FAST_API_KEY']}",
  "Content-Type": "application/json",
}
payload = {"model": "gpt-4.1", "input": "Hello"}

for attempt in range(5):
  resp = requests.post(url, headers=headers, json=payload, timeout=30)
  if resp.status_code < 400:
    print(resp.json())
    break

  if resp.status_code not in (429, 500, 502, 503, 504) or attempt == 4:
    raise RuntimeError(f"HTTP {resp.status_code}: {resp.text}")

  time.sleep(min(2, 0.2 * (2 ** attempt)))
go
package main

import (
  "bytes"
  "encoding/json"
  "fmt"
  "io"
  "net/http"
  "os"
  "time"
)

func main() {
  url := "https://api.fastapi.ai/v1/responses"
  payload := map[string]any{"model": "gpt-4.1", "input": "Hello"}
  b, _ := json.Marshal(payload)

  for attempt := 0; attempt < 5; attempt++ {
    req, _ := http.NewRequest("POST", url, bytes.NewReader(b))
    req.Header.Set("Authorization", "Bearer "+os.Getenv("FAST_API_KEY"))
    req.Header.Set("Content-Type", "application/json")

    resp, err := http.DefaultClient.Do(req)
    if err != nil {
      panic(err)
    }
    body, _ := io.ReadAll(resp.Body)
    resp.Body.Close()

    if resp.StatusCode < 400 {
      fmt.Println(string(body))
      break
    }

    retryable := resp.StatusCode == 429 || resp.StatusCode == 500 || resp.StatusCode == 502 || resp.StatusCode == 503 || resp.StatusCode == 504
    if !retryable || attempt == 4 {
      panic(fmt.Errorf("HTTP %d: %s", resp.StatusCode, string(body)))
    }

    time.Sleep(time.Duration(min(2000, 200*(1<<attempt))) * time.Millisecond)
  }
}

func min(a, b int) int {
  if a < b {
    return a
  }
  return b
}
java
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class Main {
  public static void main(String[] args) throws Exception {
    String url = "https://api.fastapi.ai/v1/responses";
    String body = "{\"model\":\"gpt-4.1\",\"input\":\"Hello\"}";
    HttpClient client = HttpClient.newHttpClient();

    for (int attempt = 0; attempt < 5; attempt++) {
      HttpRequest req = HttpRequest.newBuilder()
        .uri(URI.create(url))
        .header("Authorization", "Bearer " + System.getenv("FAST_API_KEY"))
        .header("Content-Type", "application/json")
        .POST(HttpRequest.BodyPublishers.ofString(body))
        .build();

      HttpResponse<String> resp = client.send(req, HttpResponse.BodyHandlers.ofString());
      int code = resp.statusCode();
      if (code < 400) {
        System.out.println(resp.body());
        break;
      }

      boolean retryable = code == 429 || code == 500 || code == 502 || code == 503 || code == 504;
      if (!retryable || attempt == 4) {
        throw new RuntimeException("HTTP " + code + ": " + resp.body());
      }

      long delay = Math.min(2000, 200L * (1L << attempt));
      Thread.sleep(delay);
    }
  }
}

Pagination (list endpoints) ​

Most list endpoints support limit and after. A typical response includes data, first_id, last_id, and has_more.

bash
curl "https://api.fastapi.ai/v1/files?limit=100" \
  -H "Authorization: Bearer $FAST_API_KEY"
javascript
let after;

while (true) {
  const qs = new URLSearchParams();
  qs.set('limit', '100');
  if (after) qs.set('after', after);

  const res = await fetch(`https://api.fastapi.ai/v1/files?${qs.toString()}`, {
    headers: { Authorization: `Bearer ${process.env.FAST_API_KEY}` },
  });
  if (!res.ok) throw new Error(`HTTP ${res.status}: ${await res.text()}`);

  const page = await res.json();
  for (const item of page.data ?? []) {
    console.log(item.id);
  }

  if (!page.has_more) break;
  after = page.last_id;
}
python
import os
import requests

after = None
while True:
  params = {"limit": 100}
  if after:
    params["after"] = after

  resp = requests.get(
    "https://api.fastapi.ai/v1/files",
    headers={"Authorization": f"Bearer {os.environ['FAST_API_KEY']}"},
    params=params,
    timeout=30,
  )
  resp.raise_for_status()

  page = resp.json()
  for item in page.get("data", []):
    print(item["id"])

  if not page.get("has_more"):
    break
  after = page.get("last_id")
go
package main

import (
  "encoding/json"
  "fmt"
  "io"
  "net/http"
  "net/url"
  "os"
)

func main() {
  var after string

  for {
    qs := url.Values{}
    qs.Set("limit", "100")
    if after != "" {
      qs.Set("after", after)
    }

    req, _ := http.NewRequest("GET", "https://api.fastapi.ai/v1/files?"+qs.Encode(), nil)
    req.Header.Set("Authorization", "Bearer "+os.Getenv("FAST_API_KEY"))

    resp, err := http.DefaultClient.Do(req)
    if err != nil {
      panic(err)
    }
    body, _ := io.ReadAll(resp.Body)
    resp.Body.Close()

    var page map[string]any
    _ = json.Unmarshal(body, &page)

    data, _ := page["data"].([]any)
    for _, item := range data {
      m, _ := item.(map[string]any)
      fmt.Println(m["id"])
    }

    hasMore, _ := page["has_more"].(bool)
    if !hasMore {
      break
    }
    after, _ = page["last_id"].(string)
  }
}
java
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class Main {
  public static void main(String[] args) throws Exception {
    String after = null;
    HttpClient client = HttpClient.newHttpClient();

    while (true) {
      String url = "https://api.fastapi.ai/v1/files?limit=100" + (after != null ? "&after=" + after : "");

      HttpRequest req = HttpRequest.newBuilder()
        .uri(URI.create(url))
        .header("Authorization", "Bearer " + System.getenv("FAST_API_KEY"))
        .GET()
        .build();

      HttpResponse<String> resp = client.send(req, HttpResponse.BodyHandlers.ofString());
      String body = resp.body();
      System.out.println(body);

      if (!body.contains("\"has_more\":true")) {
        break;
      }
      int idx = body.indexOf("\"last_id\":\"");
      if (idx == -1) break;
      int start = idx + 11;
      int end = body.indexOf("\"", start);
      if (end <= start) break;
      after = body.substring(start, end);
    }
  }
}

智元 Fast API