SDKs
Official clients for TypeScript, Python and Go: install, set up, and how they handle idempotency, retries and errors. All three behave the same way.
Install
| Language | Package | Needs |
|---|---|---|
| TypeScript / JavaScript | @redswitches/api | Node 20+ or any runtime with fetch. No other dependencies. |
| Python | redswitches | Python 3.9+. One dependency: httpx. Sync Client and async AsyncClient. |
| Go | github.com/redswitches/redswitches-go | Go 1.23+. Standard library only. |
Until the packages are published, install from a checkout of the toolkit repository:
npm install ./sdk/typescript # TypeScript: @redswitches/api
pip install ./sdk/python # Python: redswitches
# Go: point a replace directive in your go.mod at the sdk/go folder
Set up a client
Each client reads the key from REDSWITCHES_API_KEY when you do not pass one. The key is sent only in the Authorization header and never appears in logs, errors or printed objects.
export REDSWITCHES_API_KEY='rsw_live_XXXX…'import { RedSwitches } from "@redswitches/api";
const rs = new RedSwitches(); // or new RedSwitches({ apiKey, timeoutMs: 30000, maxRetries: 3 })
const key = await rs.auth.verify();from redswitches import Client
with Client() as client: # or Client(api_key="rsw_live_XXXX…", max_retries=3)
key = client.auth.verify()client := redswitches.NewClient("") // "" reads REDSWITCHES_API_KEY
key, err := client.Auth.Verify(ctx)Lists can be read page by page (list with limit and cursor) or walked to the end (iterate / all in TypeScript and Python, All in Go), which follows next_cursor for you.
Idempotency
- Every POST and DELETE carries an
Idempotency-Key. Pass your own (idempotencyKey,idempotency_key=,RequestOptions{IdempotencyKey}), or the SDK makes a fresh one for the call. - Every retry of that call reuses the same key, so a retried order or reboot happens once.
- An order dry run (
dryRun: true,dry_run=True,OrderOptions{DryRun: true}) sends noIdempotency-Key: it changes nothing. - A real order needs
expected_totalfrom a dry run; the SDKs refuse to send one without it. - If a write fails with a network error, call it again with the same key to get the original outcome. Python errors carry the key that was sent (
err.idempotency_key).
See Idempotency and retries for what the API returns on a repeat.
Retries
The SDKs retry only when it is safe and the API asks for it:
| Answer | Retried? |
|---|---|
| Network error or timeout | Yes, with the same Idempotency-Key. Never for GET /actions/{id}/credentials. |
429 rate_limited | Yes, after Retry-After. |
503 unavailable | Yes, after Retry-After, else 2, 4, 8 seconds. Never for GET /actions/{id}/credentials. |
409 in_progress | Yes, after Retry-After / details.retry_after, with the same key. |
429 action_limit | No. A ceiling of the account; it will not pass in seconds. |
403 cooling_off | No. Ordering opens later; details.retry_after says when. |
Anything else (500, 409 server_busy, 409 price_changed, 409 order_under_review, …) | No. |
At most 3 retries by default. A requested wait longer than about a minute is returned as the error instead of slept.
GET /actions/{id}/credentials is never retried after a network error or a 5xx answer: the lost answer may already have used up the one read. It is retried only after 429 rate_limited, when nothing was read. See Rescue and one-time credentials.
Errors
Every failure is one error type with the API's code, status, message, details and request id. Write your code against code; quote the request id to support.
import { RedSwitchesError } from "@redswitches/api";
try {
await rs.orders.create({ ...body, expected_total: quote.total });
} catch (e) {
if (e instanceof RedSwitchesError && e.code === "price_changed") console.log("now", e.details.total);
else throw e;
}from redswitches import RedSwitchesError
try:
client.orders.create(**body, expected_total=quote.total)
except RedSwitchesError as err:
if err.code != "price_changed":
raise
print("now", err.details)_, err := client.Orders.Create(ctx, req, nil)
if redswitches.ErrorCode(err) == "price_changed" {
var apiErr *redswitches.Error
if errors.As(err, &apiErr) {
fmt.Println("now", apiErr.Details.Total)
}
}Client-side failures have their own codes in TypeScript (network_error, timeout, invalid_argument, …), and their own exception classes in Python (APIConnectionError, …). Go returns the transport error itself.
All error codes are on Rate limits and errors.
Also built on the SDKs
- CLI: rxctl, on the TypeScript SDK.
- MCP server, on the TypeScript SDK.
- Terraform provider, on the Go SDK.