> For the complete documentation index, see [llms.txt](https://dev.strixlab.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://dev.strixlab.io/typescript-sdk/errors-and-rate-limits.md).

# Errors & Rate Limits

### Error types

<table><thead><tr><th width="220">Class</th><th>Thrown when</th></tr></thead><tbody><tr><td><code>StrixConfigError</code></td><td>At construction — no <code>baseUrl</code>, or a malformed one.</td></tr><tr><td><code>StrixAuthError</code></td><td>Before any request — you called an authenticated method without credentials.</td></tr><tr><td><code>StrixApiError</code></td><td>The API returned a failure. Carries <code>status</code>, <code>endpoint</code>, and <code>retryAfter</code> on 429.</td></tr></tbody></table>

{% code title="Handling all three" %}

```ts
import { StrixApiError, StrixAuthError, StrixConfigError } from 'strix-sdk';

try {
  await client.orders.place({ tokenId, side: 'BUY', price: 0.65, quantity: 100 });
} catch (err) {
  if (err instanceof StrixAuthError) {
    console.error('no credentials configured');
  } else if (err instanceof StrixApiError) {
    console.error(`HTTP ${err.status} on ${err.endpoint}: ${err.message}`);
    if (err.status === 429 && err.retryAfter) await sleep(err.retryAfter * 1000);
  } else {
    throw err;
  }
}
```

{% endcode %}

Network failures and timeouts also surface as `StrixApiError` — `status: 0` for a network error, `status: 408` for a timeout — so a single catch covers transport and API failures alike.

### Error codes

Every failure carries a stable, machine-readable `code`. **Branch on the code, never on the message** — messages are written for humans and change between releases; a code's meaning is frozen once published.

{% tabs %}
{% tab title="400 — bad request" %}

<table><thead><tr><th width="270">Code</th><th>Meaning</th></tr></thead><tbody><tr><td><code>invalid_request</code></td><td>Malformed body or query.</td></tr><tr><td><code>missing_parameter</code> / <code>invalid_parameter</code></td><td>A required field is absent or unusable.</td></tr><tr><td><code>invalid_price</code></td><td>Price is not a number the venue can use.</td></tr><tr><td><code>tick_size_violation</code></td><td>Price is not a multiple of the market's <code>tickSize</code>.</td></tr><tr><td><code>price_out_of_range</code></td><td>Outside <code>0.0001 … 0.9999</code>.</td></tr><tr><td><code>invalid_quantity</code></td><td>Zero, negative, or too many decimals.</td></tr><tr><td><code>below_min_notional</code></td><td><code>price × quantity</code> is under $1.00.</td></tr></tbody></table>
{% endtab %}

{% tab title="401 / 403 — auth" %}

<table><thead><tr><th width="270">Code</th><th>Meaning</th></tr></thead><tbody><tr><td><code>missing_credentials</code></td><td>Auth headers absent.</td></tr><tr><td><code>unknown_api_key</code></td><td>Key not recognised — or it was regenerated.</td></tr><tr><td><code>invalid_signature</code></td><td>The HMAC doesn't match. Nearly always the signed path (see below).</td></tr><tr><td><code>invalid_passphrase</code></td><td>Passphrase doesn't match the key.</td></tr><tr><td><code>expired_timestamp</code></td><td>Your clock is more than 30 s from the server's.</td></tr><tr><td><code>account_restricted</code></td><td>The account may not trade.</td></tr><tr><td><code>geo_restricted</code></td><td>Opening new positions is not available from this region.</td></tr></tbody></table>
{% endtab %}

{% tab title="404 / 409 — state" %}

<table><thead><tr><th width="270">Code</th><th>Meaning</th></tr></thead><tbody><tr><td><code>market_not_found</code> / <code>token_not_found</code></td><td>Bad id — check you passed <code>tokenId</code> and not <code>outcome.id</code>.</td></tr><tr><td><code>order_not_found</code></td><td>Unknown order hash.</td></tr><tr><td><code>order_not_cancellable</code></td><td>Already filled, cancelled, or locked in settlement. <strong>It may still be live</strong> — re-list before assuming.</td></tr><tr><td><code>duplicate_client_order_id</code></td><td>That client order id was already used.</td></tr><tr><td><code>already_exists</code></td><td>The resource is already there.</td></tr></tbody></table>
{% endtab %}

{% tab title="422 — can't act now" %}

<table><thead><tr><th width="270">Code</th><th>Meaning</th></tr></thead><tbody><tr><td><code>insufficient_balance</code></td><td>Not enough USDC after open-order holds.</td></tr><tr><td><code>insufficient_shares</code></td><td>Not enough settled shares to sell.</td></tr><tr><td><code>max_open_orders</code></td><td>Over the open-order ceiling (2,000 for API-key accounts).</td></tr><tr><td><code>market_closed</code> / <code>market_paused</code> / <code>market_resolved</code></td><td>The market isn't accepting orders.</td></tr><tr><td><code>no_liquidity</code></td><td>Nothing to sweep — common on a market buy against a bid-only book.</td></tr></tbody></table>
{% endtab %}

{% tab title="429 / 5xx" %}

<table><thead><tr><th width="270">Code</th><th>Meaning</th></tr></thead><tbody><tr><td><code>rate_limited</code></td><td>Over your budget. <code>Retry-After</code> tells you how long to wait.</td></tr><tr><td><code>internal_error</code></td><td>Something failed on our side. Safe to retry idempotent reads.</td></tr><tr><td><code>service_unavailable</code></td><td>A dependency is down. Back off.</td></tr><tr><td><code>maintenance</code></td><td>Planned maintenance window.</td></tr></tbody></table>
{% endtab %}
{% endtabs %}

{% hint style="info" %}
**`invalid_signature` on every request?** You are almost certainly signing the wrong path. Sign the path **without** the `/api` prefix, and **with** the query string: `/orders?marketId=abc`, not `/api/orders`. See [Authentication](/http-and-websocket-api/authentication.md#the-signature).
{% endhint %}

### Rate limits

Buckets are per account and per credential type. API-key (HMAC) traffic gets a much larger budget than a browser session.

<table><thead><tr><th width="260">Bucket</th><th width="200">API-key limit</th><th>Applies to</th></tr></thead><tbody><tr><td><strong>Order writes</strong></td><td>300 per 10 s (≈30/s)</td><td><code>place</code>, <code>placeMarket</code>, <code>placeBatch</code>, single <code>cancel</code>.</td></tr><tr><td><strong>Reads</strong></td><td>500 per second</td><td>Events, order books, portfolio, order listing.</td></tr><tr><td><strong>Bulk cancels</strong></td><td><strong>Exempt</strong></td><td><code>cancelAll</code> and <code>cancelBatch</code> — cancels only remove risk.</td></tr></tbody></table>

A batch counts as **one** order write however many orders it carries, which makes `placeBatch` the cheapest way to requote.

{% hint style="success" %}
Need more than this? Talk to us — limits are configurable per account for market makers.
{% endhint %}

#### Handling 429

Every rate-limit response carries a `Retry-After` header, and the SDK surfaces it as `err.retryAfter` (seconds).

{% tabs %}
{% tab title="Automatic" %}

```ts
const client = new StrixClient({
  baseUrl,
  retryOn429: true,   // waits out Retry-After, retries once, then throws
  // …
});
```

Off by default, so a bot managing its own pacing is never surprised by a hidden sleep. The wait is capped at 60 s.
{% endtab %}

{% tab title="Manual" %}

```ts
async function withBackoff<T>(fn: () => Promise<T>, tries = 3): Promise<T> {
  for (let i = 0; ; i++) {
    try {
      return await fn();
    } catch (err) {
      if (!(err instanceof StrixApiError) || err.status !== 429 || i >= tries) throw err;
      await sleep((err.retryAfter ?? 1) * 1000);
    }
  }
}
```

{% endtab %}
{% endtabs %}

### Other ceilings

<table><thead><tr><th width="300">Limit</th><th>Value</th></tr></thead><tbody><tr><td>Orders per <code>placeBatch</code></td><td>15</td></tr><tr><td>Hashes per <code>cancelBatch</code></td><td>100</td></tr><tr><td>Open orders per account</td><td>2,000</td></tr><tr><td>Order-book depth per side</td><td>200</td></tr><tr><td>HMAC timestamp window</td><td>30 seconds</td></tr></tbody></table>


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://dev.strixlab.io/typescript-sdk/errors-and-rate-limits.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
