0
# Retry System
1
2
Configurable retry logic with exponential backoff, status code filtering, and Retry-After header support. Ky automatically retries failed requests based on configurable conditions.
3
4
## Capabilities
5
6
### Retry Configuration
7
8
Configure retry behavior with comprehensive options for different failure scenarios.
9
10
```typescript { .api }
11
interface RetryOptions {
12
/** Maximum number of retry attempts */
13
limit?: number;
14
/** HTTP methods allowed to retry */
15
methods?: string[];
16
/** HTTP status codes that trigger retry */
17
statusCodes?: number[];
18
/** Status codes that respect Retry-After header */
19
afterStatusCodes?: number[];
20
/** Maximum wait time for Retry-After header */
21
maxRetryAfter?: number;
22
/** Upper limit for retry delay */
23
backoffLimit?: number;
24
/** Custom delay calculation function */
25
delay?: (attemptCount: number) => number;
26
}
27
```
28
29
**Default Values:**
30
31
```typescript
32
const defaultRetryOptions = {
33
limit: 2,
34
methods: ['get', 'put', 'head', 'delete', 'options', 'trace'],
35
statusCodes: [408, 413, 429, 500, 502, 503, 504],
36
afterStatusCodes: [413, 429, 503],
37
maxRetryAfter: undefined, // Uses timeout value
38
backoffLimit: Infinity,
39
delay: (attemptCount) => 0.3 * (2 ** (attemptCount - 1)) * 1000
40
};
41
```
42
43
**Usage Examples:**
44
45
```typescript
46
import ky from "ky";
47
48
// Simple retry limit
49
const simpleRetry = await ky.get("https://api.example.com/unstable", {
50
retry: 5 // Retry up to 5 times with default settings
51
}).json();
52
53
// Custom retry configuration
54
const customRetry = await ky.get("https://api.example.com/data", {
55
retry: {
56
limit: 3,
57
methods: ["get", "post"],
58
statusCodes: [408, 429, 500, 502, 503, 504],
59
delay: (attemptCount) => Math.min(1000 * attemptCount, 5000)
60
}
61
}).json();
62
63
// Aggressive retry for critical operations
64
const criticalClient = ky.create({
65
retry: {
66
limit: 10,
67
methods: ["get", "post", "put", "patch", "delete"],
68
statusCodes: [408, 413, 429, 500, 502, 503, 504],
69
backoffLimit: 30000, // Max 30 second delay
70
delay: (attemptCount) => {
71
// Exponential backoff with jitter
72
const baseDelay = 1000 * (2 ** (attemptCount - 1));
73
const jitter = Math.random() * 0.1 * baseDelay;
74
return baseDelay + jitter;
75
}
76
}
77
});
78
```
79
80
### Retry-After Header Support
81
82
Automatically respect server-provided retry timing through HTTP headers.
83
84
**Usage Examples:**
85
86
```typescript
87
import ky from "ky";
88
89
// Respect Retry-After header for rate limiting
90
const rateLimitedClient = ky.create({
91
retry: {
92
limit: 5,
93
afterStatusCodes: [413, 429, 503], // Honor Retry-After for these statuses
94
maxRetryAfter: 60000 // Wait maximum 60 seconds
95
}
96
});
97
98
// The client will automatically wait for the time specified in:
99
// - Retry-After header (seconds or HTTP date)
100
// - RateLimit-Reset header (fallback)
101
// - X-RateLimit-Reset header (GitHub-style)
102
// - X-Rate-Limit-Reset header (Twitter-style)
103
104
const data = await rateLimitedClient.get("https://api.example.com/limited").json();
105
106
// Custom max retry delay
107
const customDelayClient = ky.create({
108
retry: {
109
afterStatusCodes: [429],
110
maxRetryAfter: 10000, // Never wait more than 10 seconds
111
}
112
});
113
```
114
115
### Method-Specific Retry Behavior
116
117
Configure retry behavior per HTTP method for different operation types.
118
119
**Usage Examples:**
120
121
```typescript
122
import ky from "ky";
123
124
// Safe methods only (idempotent operations)
125
const safeRetryClient = ky.create({
126
retry: {
127
limit: 5,
128
methods: ["get", "head", "options"], // Only safe methods
129
statusCodes: [408, 429, 500, 502, 503, 504]
130
}
131
});
132
133
// Include PUT and DELETE (idempotent)
134
const idempotentClient = ky.create({
135
retry: {
136
limit: 3,
137
methods: ["get", "put", "delete", "head"],
138
statusCodes: [408, 500, 502, 503, 504]
139
}
140
});
141
142
// Retry POST for specific use cases (be careful!)
143
const postRetryClient = ky.create({
144
retry: {
145
limit: 2,
146
methods: ["get", "post"], // Only if POST is idempotent
147
statusCodes: [408, 500, 502, 503, 504]
148
}
149
});
150
151
// Different retry strategies per method
152
const hybridClient = ky.create({
153
prefixUrl: "https://api.example.com"
154
});
155
156
// GET requests with aggressive retry
157
const getData = (endpoint: string) => hybridClient.get(endpoint, {
158
retry: { limit: 5, methods: ["get"] }
159
});
160
161
// POST requests with conservative retry
162
const postData = (endpoint: string, data: any) => hybridClient.post(endpoint, {
163
json: data,
164
retry: { limit: 1, methods: ["post"], statusCodes: [408, 500] }
165
});
166
```
167
168
### Status Code Filtering
169
170
Configure which HTTP status codes should trigger retry attempts.
171
172
**Usage Examples:**
173
174
```typescript
175
import ky from "ky";
176
177
// Default retriable status codes
178
const defaultClient = ky.create({
179
retry: {
180
statusCodes: [
181
408, // Request Timeout
182
413, // Payload Too Large (with Retry-After)
183
429, // Too Many Requests (with Retry-After)
184
500, // Internal Server Error
185
502, // Bad Gateway
186
503, // Service Unavailable (with Retry-After)
187
504 // Gateway Timeout
188
]
189
}
190
});
191
192
// Custom status codes for specific APIs
193
const customStatusClient = ky.create({
194
retry: {
195
statusCodes: [
196
408, 429, 500, 502, 503, 504, // Standard codes
197
520, 521, 522, 523, 524 // Cloudflare-specific codes
198
]
199
}
200
});
201
202
// Minimal retry for fast-fail scenarios
203
const fastFailClient = ky.create({
204
retry: {
205
limit: 1,
206
statusCodes: [500, 502, 503], // Only server errors
207
delay: () => 100 // Very short delay
208
}
209
});
210
211
// Never retry client errors (4xx)
212
const noClientErrorRetry = ky.create({
213
retry: {
214
statusCodes: [408, 500, 502, 503, 504], // Excludes 413, 429
215
afterStatusCodes: [] // No Retry-After respect
216
}
217
});
218
```
219
220
### Custom Delay Strategies
221
222
Implement custom delay calculations for different backoff strategies.
223
224
**Usage Examples:**
225
226
```typescript
227
import ky from "ky";
228
229
// Linear backoff
230
const linearClient = ky.create({
231
retry: {
232
delay: (attemptCount) => attemptCount * 1000 // 1s, 2s, 3s, 4s...
233
}
234
});
235
236
// Exponential backoff with jitter
237
const jitterClient = ky.create({
238
retry: {
239
delay: (attemptCount) => {
240
const baseDelay = 1000 * (2 ** (attemptCount - 1));
241
const jitter = Math.random() * 0.1 * baseDelay;
242
return baseDelay + jitter;
243
},
244
backoffLimit: 10000 // Cap at 10 seconds
245
}
246
});
247
248
// Fibonacci backoff
249
const fibonacciClient = ky.create({
250
retry: {
251
delay: (attemptCount) => {
252
const fibonacci = (n: number): number => {
253
if (n <= 1) return n;
254
return fibonacci(n - 1) + fibonacci(n - 2);
255
};
256
return fibonacci(attemptCount) * 1000;
257
}
258
}
259
});
260
261
// Adaptive delay based on response time
262
let lastResponseTime = 1000;
263
264
const adaptiveClient = ky.create({
265
retry: {
266
delay: (attemptCount) => {
267
// Increase delay based on server response time
268
const baseDelay = Math.max(lastResponseTime * 2, 1000);
269
return baseDelay * attemptCount;
270
}
271
},
272
hooks: {
273
afterResponse: [
274
(request, options, response) => {
275
// Track response time (simplified)
276
lastResponseTime = Date.now() - (request as any).startTime || 1000;
277
}
278
]
279
}
280
});
281
```
282
283
### Retry with Timeout Interaction
284
285
Understanding how retry interacts with timeout settings.
286
287
**Usage Examples:**
288
289
```typescript
290
import ky from "ky";
291
292
// Total timeout includes all retry attempts
293
const timeoutClient = ky.create({
294
timeout: 30000, // 30 seconds total (including retries)
295
retry: {
296
limit: 3,
297
delay: (attemptCount) => 2000 * attemptCount // 2s, 4s, 6s
298
}
299
});
300
301
// Per-attempt timeout
302
const perAttemptClient = ky.create({
303
timeout: 5000, // 5 seconds per attempt
304
retry: {
305
limit: 5,
306
delay: (attemptCount) => 1000 * attemptCount
307
}
308
});
309
310
// Fast timeout, many retries
311
const fastRetryClient = ky.create({
312
timeout: 3000, // Fail fast per attempt
313
retry: {
314
limit: 10, // But try many times
315
delay: (attemptCount) => Math.min(500 * attemptCount, 5000)
316
}
317
});
318
319
// No retry on timeout
320
const noTimeoutRetryClient = ky.create({
321
timeout: 10000,
322
retry: {
323
limit: 3,
324
// Timeouts are never retried automatically
325
statusCodes: [500, 502, 503, 504] // Excludes timeout scenarios
326
}
327
});
328
```
329
330
## Types
331
332
```typescript { .api }
333
interface RetryOptions {
334
limit?: number;
335
methods?: string[];
336
statusCodes?: number[];
337
afterStatusCodes?: number[];
338
maxRetryAfter?: number;
339
backoffLimit?: number;
340
delay?: (attemptCount: number) => number;
341
}
342
343
// Retry can be configured as number (limit only) or full options
344
type RetryConfiguration = RetryOptions | number;
345
```