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"}' \
-ijavascript
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
donejavascript
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);
}
}
}