Use when configuring SDK runtime behavior such as retries, timeouts, pagination, server selection, custom code hooks, or error handling...
Configure runtime behavior for Speakeasy-generated SDKs including retries, timeouts, pagination, server selection, custom code preservation, and error handling.
| Input | Required | Description |
|---|---|---|
| OpenAPI spec | Yes | Path to the OpenAPI spec to configure |
| gen.yaml | Sometimes | Generation config for error handling customization |
| Target language | Helpful | TypeScript, Python, or Go (defaults vary) |
| Output | Description |
|---|---|
| Updated OpenAPI spec | Spec with runtime extensions applied |
| SDK configuration | Runtime behavior configured in generated SDK |
| Custom code files | Hook files preserved across regeneration |
Runtime configuration is primarily done through OpenAPI extensions, not CLI commands. After modifying the spec, regenerate:
speakeasy run --output console
Add x-speakeasy-retries at the root of your OpenAPI document:
openapi: 3.1.0
info:
title: My API
version: 1.0.0
x-speakeasy-retries:
strategy: backoff
backoff:
initialInterval: 500 # milliseconds
maxInterval: 60000 # milliseconds
maxElapsedTime: 3600000 # milliseconds (1 hour)
exponent: 1.5
statusCodes:
- 5XX
- 408
- 429
retryConnectionErrors: true
Override retries on a specific operation by adding x-speakeasy-retries at the operation level with the same schema as above.
| Option | Type | Description |
|---|---|---|
strategy |
string | Must be backoff |
backoff.initialInterval |
integer | First retry delay in ms |
backoff.maxInterval |
integer | Maximum delay between retries in ms |
backoff.maxElapsedTime |
integer | Total time limit for all retries in ms |
backoff.exponent |
number | Backoff multiplier (e.g., 1.5, 2.0) |
statusCodes |
string[] | HTTP status codes to retry (supports 5XX patterns) |
retryConnectionErrors |
boolean | Retry on connection failures |
const res = await sdk.payments.create(
{ amount: 1000 },
{
retries: {
strategy: "backoff",
backoff: {
initialInterval: 1000,
maxInterval: 30000,
maxElapsedTime: 300000,
exponent: 2.0,
},
retryConnectionErrors: true,
},
}
);
from sdk.utils import BackoffStrategy, RetryConfig
res = sdk.payments.create(
amount=1000,
retries=RetryConfig("backoff",
backoff=BackoffStrategy(1000, 30000, 300000, 2.0),
retry_connection_errors=True),
)
res, err := sdk.Payments.Create(ctx, req, operations.WithRetries(retry.Config{
Strategy: "backoff",
Backoff: &retry.BackoffStrategy{
InitialInterval: 1000, MaxInterval: 30000,
MaxElapsedTime: 300000, Exponent: 2.0,
},
RetryConnectionErrors: true,
}))
Set a default timeout in milliseconds at the document root:
x-speakeasy-timeout: 30000 # 30 seconds
Override timeout on specific operations:
paths:
/reports/generate:
post:
operationId: generateReport
x-speakeasy-timeout: 120000 # 2 minutes for long-running ops
TypeScript:
const sdk = new SDK({ timeoutMs: 30000 }); // global
const res = await sdk.reports.generate({ type: "annual" }, { timeoutMs: 120000 }); // per-call
Python:
sdk = SDK(timeout_ms=30000) # global
res = sdk.reports.generate(type="annual", timeout_ms=120000) # per-call
Go:
sdk := SDK.New(SDK.WithTimeoutMs(30000)) // global
res, err := sdk.Reports.Generate(ctx, req, operations.WithTimeoutMs(120000)) // per-call
Add x-speakeasy-pagination to list endpoints:
paths:
/users:
get:
operationId: listUsers
x-speakeasy-pagination:
type: offsetLimit
inputs:
- name: offset
in: parameters
type: offset
- name: limit
in: parameters
type: limit
outputs:
results: $.data
| Type | Description | Use Case |
|---|---|---|
offsetLimit |
Offset + limit parameters | Traditional pagination |
cursor |
Cursor-based navigation | Large datasets, real-time data |
url |
Next-page URL in response | HATEOAS-style APIs |
For cursor type, use type: cursor in inputs and add nextCursor: $.meta.next_cursor to outputs.
// Auto-iterate through all pages
for await (const user of await sdk.users.list({ limit: 50 })) {
console.log(user.name);
}
// Or manually page with next()
let page = await sdk.users.list({ limit: 50 });
while (page) {
for (const user of page.data) { console.log(user.name); }
page = await page.next();
}
servers:
- url: https://api.example.com
description: Production
x-speakeasy-server-id: production
- url: https://sandbox.example.com
description: Sandbox
x-speakeasy-server-id: sandbox
TypeScript:
const sdk = new SDK({ server: "sandbox" });
// or custom URL:
const sdk = new SDK({ serverURL: "https://custom.example.com" });
Python: sdk = SDK(server="sandbox") or sdk = SDK(server_url="...")
Go: sdk := SDK.New(SDK.WithServer("sandbox")) or SDK.WithServerURL("...")
Files in src/hooks/ (TypeScript) or the equivalent hooks directory are preserved during SDK regeneration.
registration.ts is generated on the first run, then never overwrittensrc/hooks/ are preserved across regenerationregistration.ts to integrate them with the SDK lifecycle// src/hooks/registration.ts
import { Hooks } from "./types";
import { initLoggingHook } from "./logging";
import { initCustomAuthHook } from "./custom-auth";
export function initHooks(hooks: Hooks) {
initLoggingHook(hooks);
initCustomAuthHook(hooks);
}
generation:
errors:
statusCodes:
- statusCode: "4XX"
schema: "#/components/schemas/ClientError"
- statusCode: "5XX"
schema: "#/components/schemas/ServerError"
Define typed error responses in your OpenAPI spec by adding response schemas for specific status codes (e.g., 404, 422). The SDK generates typed error classes for each.
import { NotFoundError, ValidationError } from "./sdk/models/errors";
try {
const user = await sdk.users.get({ id: "123" });
} catch (err) {
if (err instanceof NotFoundError) {
console.log("User not found:", err.message);
} else if (err instanceof ValidationError) {
console.log("Validation failed:", err.errors);
} else {
throw err;
}
}
Add custom error transformation via hooks:
// src/hooks/error-transform.ts
export function initErrorTransformHook(hooks: Hooks) {
hooks.registerAfterError((_hookCtx, response, error) => {
if (error) { console.error("API error:", error.message); }
return { response, error };
});
}
Scenario: Configure retries, a 30-second global timeout, and sandbox server selection.
openapi: 3.1.0
info:
title: Payments API
version: 1.0.0
x-speakeasy-retries:
strategy: backoff
backoff:
initialInterval: 500
maxInterval: 60000
maxElapsedTime: 300000
exponent: 1.5
statusCodes: [5XX, 429]
retryConnectionErrors: true
x-speakeasy-timeout: 30000
servers:
- url: https://api.payments.com
x-speakeasy-server-id: production
- url: https://sandbox.payments.com
x-speakeasy-server-id: sandbox
Regenerate: speakeasy run --output console
Use with runtime overrides:
const sdk = new SDK({ server: "sandbox", timeoutMs: 30000 });
const payment = await sdk.payments.create(
{ amount: 5000, currency: "usd" },
{
retries: {
strategy: "backoff",
backoff: { initialInterval: 2000, maxInterval: 30000,
maxElapsedTime: 120000, exponent: 2.0 },
},
timeoutMs: 60000,
}
);
maxElapsedTime values that could hang requests indefinitelysrc/hooks/ as they will be overwritten on regenerationx-speakeasy-server-id and select at runtime| Issue | Cause | Solution |
|---|---|---|
| Retries not working | Extension at wrong level | Ensure x-speakeasy-retries is at document root or operation level |
| Timeout not applying | Value not in milliseconds | x-speakeasy-timeout expects milliseconds, not seconds |
Pagination next() undefined |
Missing extension | Add x-speakeasy-pagination to the list operation |
| Custom hooks lost on regen | Files outside hooks dir | Move custom code to src/hooks/ directory |
| Server ID not recognized | Missing extension | Add x-speakeasy-server-id to each server entry |
| Typed errors not generated | Missing error schemas | Define error response schemas per status code in OpenAPI spec |
| Hook not executing | Not registered | Register your hook in registration.ts |