For the complete documentation index, see llms.txt. This page is also available as Markdown.

Error Handling

All errors follow a consistent format. Every error response includes an error type, a machine-readable code, and a human-readable message.

Error format

{
  "error": {
    "type": "INVALID_REQUEST_ERROR",
    "code": "quote_leverage_below_minimum",
    "message": "Leverage is below the minimum allowed for this market."
  }
}
Field
Type
Description

type

string

Error category (see below)

code

string

Machine-readable error code (snake_case). Use this for programmatic handling

message

string

Human-readable description of the error. Suitable for displaying to end users. Don't parse this — values are duplicated as typed fields under params

params

object

Optional. Structured hint values for codes that carry actionable data. See Errors with hints


Error types

Type
HTTP Statuses
Description

INVALID_REQUEST_ERROR

400, 404, 409

The request was malformed, missing parameters, or violated a business rule

AUTHENTICATION_ERROR

401, 403

Missing or invalid API key / JWT

RATE_LIMIT_ERROR

429

Too many requests

API_ERROR

500+

Something went wrong on our side


Error codes by endpoint

POST /tokens

Code
Status
When

customer_auth_invalid_wallet_address

400

Wallet address is not a valid EVM or Solana address

GET /markets/{ticker}

Code
Status
When

customer_market_not_found

404

No market with that ticker

GET /markets/{ticker}/positions

Code
Status
When

customer_market_not_found

404

No market with that ticker

GET /events/{event_ticker}/markets and GET /events/{event_ticker}/positions

Code
Status
When

customer_event_not_found

404

No event with that event ticker

GET /series/{series_ticker}/markets and GET /series/{series_ticker}/positions

Code
Status
When

customer_series_not_found

404

No series with that series ticker

GET /positions/{position_id}

Code
Status
When

customer_position_not_found

404

No position with that ID for this user

GET /positions/{position_id}/transactions

Code
Status
When

customer_position_transactions_not_found

404

No position with that ID for this user

GET /positions/{position_id}/partial-closes

Code
Status
When

beta_feature_not_enabled

400

Partial close is a beta feature and is not enabled for your partner account

customer_position_not_found

404

No position with that ID for this user

POST /relayer-submissions

Code
Status
When

customer_relayer_submission_insufficient_balance

400

The deposit wallet holds less pUSD than the batch's pUSD transfers add up to. Nothing was sent. params carries deposit_wallet_address, required_usdc_units and balance_usdc_units

customer_relayer_submission_invalid_call_target

400

A call in the batch targets a contract other than the vault or the pUSD token

customer_relayer_submission_downstream_error

400

The Polymarket relayer refused the batch. params carries polymarket_status, polymarket_error (the relayer's response body, verbatim) and reason (walletBusy, nonceMismatch or other)

customer_relayer_submission_failed

400

The batch could not be sent to the Polymarket relayer

beta_feature_not_enabled

400

The batch contains requestPartialClose and partial close is not enabled for your partner account

POST /quotes

Market eligibility:

Code
Status
When

quote_market_not_eligible

400

Market exists but isn't eligible for leverage

quote_market_not_ready

400

Market is not yet ready for trading

quote_market_not_active

400

Market is not active

quote_market_risk_too_high

400

Market risk is too high right now — try again later

quote_market_unsupported_category

400

This market category is not supported

quote_market_unsupported_crypto_asset

400

Crypto market whose underlying asset has no supported price feed

quote_event_not_started

400

Event has not started yet — trading opens at the scheduled start time

quote_trading_window_closing

400

Market is too close to its hard-exit deadline to open a new position

risk_engine_market_closed

400

Market is no longer accepting new positions

risk_engine_market_disabled

400

Market has been disabled (see params.reason)

risk_engine_market_settlement_detected

400

Market settlement has been detected

circuit_breaker_price_divergence_tripped

400

Price divergence circuit breaker has been triggered

quote_polymarket_market_closed

400

Polymarket market is closed

quote_polymarket_market_inactive

400

Polymarket market is inactive

quote_polymarket_market_not_accepting_orders

400

Polymarket market is not accepting orders

quote_polymarket_missing_token

400

Polymarket market is missing token configuration

quote_market_missing_polymarket_condition_id

400

Market is missing required Polymarket condition ID

quote_creation_disabled

503

Quote creation is temporarily disabled by a server kill switch — retry shortly

quote_liquidation_not_viable

400

Liquidation price would be too low to maintain a position

Leverage validation:

Code
Status
When

quote_leverage_below_minimum

400

Leverage is below the minimum (2x)

quote_leverage_exceeds_maximum

400

Leverage exceeds the absolute maximum

quote_leverage_exceeds_model_max

400

Leverage exceeds the model's maximum for this market

quote_leverage_exceeds_collateral_floor

400

Implied collateral is below the minimum at this leverage

quote_notional_below_minimum

400

Notional is below the minimum for this account

quote_leverage_too_high_for_price

400

Leverage is too high relative to the current entry price

quote_price_too_low

400

Entry price is too low for a leveraged position

Market quality filters:

Code
Status
When

quote_entry_depth_too_low

400

Not enough order book depth

quote_entry_bid_depth_too_low

400

Minimum bid depth below threshold

quote_entry_book_one_sided

400

Order book is missing or one-sided — market isn't tradable now

quote_entry_price_out_of_range

400

Current market price is outside tradeable range

quote_entry_spread_too_wide

400

Market spread exceeds maximum

quote_entry_order_book_stale

400

Order book data is stale

quote_entry_price_stale

400

Price data is stale

quote_entry_crypto_price_stale

400

Crypto price data is stale

quote_entry_sport_data_stale

400

Sport event data is stale

quote_entry_excluded_market_type

400

Market type is excluded from trading (see params.market_type)

quote_entry_excluded_sport

400

Sport type is excluded from trading (see params.sport)

quote_entry_market_too_elapsed

400

Market is too close to expiry

quote_entry_top_holder_too_high

400

Top holder concentration exceeds maximum

quote_entry_volume_too_low

400

24h trading volume is below minimum

quote_twap_data_unavailable

400

TWAP price data is unavailable for this market

quote_twap_data_stale

400

TWAP price data is stale

notional_selector_insufficient_liquidity

400

The requested size exceeds the liquidity fillable within slippage; retry smaller

notional_selector_below_min_notional

400

The order book is too thin to fill even the minimum position size on this side

notional_selector_empty_order_book

400

One side of the order book is currently empty

notional_selector_pregame_insufficient_liquidity

400

A pre-game market does not yet have enough estimated live liquidity to open

quote_side_capacity_exceeded

400

Notional exceeds available capacity on this side of the market

Position limits:

Code
Status
When

quote_user_position_limit_exceeded

400

User has reached their maximum number of open positions

quote_market_position_limit_exceeded

400

Market-level position limit reached

quote_side_position_limit_exceeded

400

Too many positions on one side of this market

quote_global_position_limit_exceeded

400

Platform-wide position limit reached

quote_partner_position_limit_exceeded

400

Partner's aggregate exposure cap reached

Borrowing limits:

These cap the protocol-borrowed capital (notional minus your collateral), separately from the position-count limits above. A quote is rejected when the new position's borrow plus the existing outstanding borrow would exceed a cap.

Code
Status
When

quote_user_capital_exceeded

400

Your wallet's total borrowed capital would exceed the per-user limit

quote_market_capital_exceeded

400

The market's total borrowed capital would exceed the per-market limit

quote_side_capital_exceeded

400

Borrowed capital on this side of the market would exceed the side limit

quote_total_capital_exceeded

400

The protocol-wide borrowed capital limit would be exceeded

Partial-open (FAK) validation:

Code
Status
When

quote_min_fill_bps_requires_fak

400

min_fill_bps sent without allow_partial_fill: true. min_fill_bps is only valid when partial-open is enabled. See Partial-Open (FAK)

quote_min_fill_bps_out_of_range

400

min_fill_bps outside 2000–7000. Params: min_fill_bps, absolute_min_bps, max_bps

quote_min_fill_bps_step_invalid

400

min_fill_bps not divisible by 500. Params: min_fill_bps, step_bps

quote_min_fill_bps_below_floor

400

min_fill_bps would shrink the post-partial position below the minimum collateral or minimum notional for this size. Params: min_fill_bps (requested), floor_min_fill_bps (smallest accepted — raise min_fill_bps to at least this, or increase size), bound_by ("collateral" or "notional" — which minimum bound the floor), requested_collateral_usd_pips, requested_notional_usd_pips

When partial-open is disabled server-side, quote creation is rejected with quote_creation_disabled (503) — the same kill switch that gates all quote creation. Omit allow_partial_fill to place an atomic order.

Committed risk mode (see Committed Risk Mode):

Code
Status
When

beta_feature_not_enabled

400

Committed mode is a beta feature and is not enabled for your partner account

quote_committed_risk_mode_unavailable

400

Committed mode could not be priced for this request. Params: reason, max_leverage_bps, requested_leverage_bps

quote_committed_plan_more_restrictive

400

A promotion's freshly priced ladder is harsher than the one the draft agreed to. Params: max_leverage_bps, agreed_margin_usdc_units, current_margin_usdc_units, current_planned_unwinds, requested_leverage_bps

A committed request is answered in committed mode or rejected — the API never substitutes an adaptive quote. quote_committed_plan_more_restrictive carries max_leverage_bps: the highest leverage that would still honour the draft's terms. Retry the promotion at that leverage, or re-draft to show the user the current plan. It is null when no leverage would work.

Other:

Code
Status
When

quote_slippage_too_high

400

Slippage tolerance exceeds maximum allowed

quote_invalid_polymarket_wallet_address

400

Polymarket requires a valid EVM address


Beta features

Some features ship to named partners before general release. When a request touches a beta feature that is not enabled for your account, the API answers 400 with:

params.feature names which one — currently partial_close or committed_risk_mode. The condition is per-partner and permanent until we enable it, so do not retry: hide or disable the entry point in your UI and contact us to have the feature turned on.

Feature
Surfaces that return this code

partial_close

GET /positions/{id}/partial-closes; POST /relayer-submissions for a batch containing requestPartialClose

committed_risk_mode

POST /quotes and POST /promoted-quotes/{id} with risk_mode: "committed"

While partial_close is off, current.min_partial_close_token_units and current.max_partial_close_token_units are null on every position — use them to decide whether to render a partial-close control. While committed_risk_mode is off, committed_unwinds is absent from every quote response.


Errors with structured hints

Some error codes return additional machine-readable fields under error.params so you can act on them programmatically — for example, pre-filling an input with the maximum supported size instead of asking the user to retry until something fits. Don't regex-parse the message string — the values you need are typed under params.

params is only present on the codes listed below. Dispatch on code first, then read the keys you expect. Most codes here are raised by the quoting (POST /quotes) path.

Units & types:

Suffix
Type
Meaning

*_usd_pips

string

Decimal string, 10,000 pips = $1.00

*_usdc_units

string

Decimal string, 1,000,000 units = $1.00 (capital/borrow limits use these)

*_bps

number

Basis points (10,000 bps = 1x leverage / 100%)

*_pct_elapsed

number

Fraction in 0–1

*_age_ms

number

Milliseconds

Values are strings unless the unit table above marks them as numbers (side, market_type, sport, market_id, and reason are also strings). Treat capacity, depth, and price values as a snapshot — the order book shifts continuously, so a user who waits before retrying may still hit the same error with different numbers.

code

params keys

notional_selector_below_min_notional

max_supported_collateral_usd_pips, min_notional_usd_pips, slippage_max_usd_pips ¹

notional_selector_insufficient_liquidity

max_supported_collateral_usd_pips, min_notional_usd_pips, slippage_max_usd_pips ¹

notional_selector_pregame_insufficient_liquidity

max_supported_collateral_usd_pips, min_notional_usd_pips, slippage_max_usd_pips ¹

quote_entry_bid_depth_too_low

current_bid_depth_usd_pips, threshold_usd_pips

quote_entry_crypto_price_stale

last_update_age_ms, market_id

quote_entry_depth_too_low

current_depth_usd_pips, min_depth_usd_pips

quote_entry_excluded_market_type

market_type

quote_entry_excluded_sport

sport

quote_entry_market_too_elapsed

max_pct_elapsed, pct_elapsed

quote_entry_order_book_stale

last_update_age_ms, market_id

quote_entry_price_out_of_range

current_price_usd_pips, max_price_usd_pips, min_price_usd_pips

quote_entry_price_stale

last_update_age_ms, market_id

quote_entry_sport_data_stale

last_update_age_ms, market_id

quote_entry_spread_too_wide

current_spread_usd_pips, max_spread_usd_pips

quote_entry_volume_too_low

current_volume24h_usd_pips, min_volume24h_usd_pips

quote_global_position_limit_exceeded

available_capacity_usd_pips, current_notional_usd_pips, limit_usd_pips ²

quote_leverage_below_minimum

current_book_leverage_bps, min_leverage_bps

quote_leverage_exceeds_collateral_floor

current_book_leverage_bps, current_collateral_usd_pips, max_leverage_bps, min_collateral_usd_pips ³

quote_leverage_exceeds_maximum

current_book_leverage_bps, max_leverage_bps

quote_leverage_exceeds_model_max

current_book_leverage_bps, max_leverage_bps

quote_leverage_too_high_for_price

current_book_leverage_bps, entry_price_usd_pips, max_acceptable_leverage_bps, minimum_buffer_usd_pips

quote_liquidation_not_viable

entry_price_usd_pips, liquidation_price_usd_pips, min_tolerance_pct_bps, side

quote_market_capital_exceeded

available_borrowed_usdc_units, available_collateral_usdc_units, available_notional_usdc_units, cap_usdc_units, current_borrowed_usdc_units, requested_borrowed_usdc_units ⁴

quote_market_position_limit_exceeded

available_capacity_usd_pips, current_notional_usd_pips, limit_usd_pips ²

quote_max_leverage_too_low

max_leverage_bps, min_leverage_bps

quote_notional_below_minimum

min_notional_usd_pips, requested_notional_usd_pips

quote_partner_position_limit_exceeded

active_notional_usd_pips, available_capacity_usd_pips, current_notional_usd_pips, in_flight_notional_usd_pips, limit_usd_pips ² ⁵

quote_side_capacity_exceeded

available_capacity_usd_pips, max_supported_collateral_usd_pips, min_notional_usd_pips ⁶

quote_side_capital_exceeded

available_borrowed_usdc_units, available_collateral_usdc_units, available_notional_usdc_units, cap_usdc_units, current_borrowed_usdc_units, requested_borrowed_usdc_units ⁴

quote_side_position_limit_exceeded

available_capacity_usd_pips, current_notional_usd_pips, limit_usd_pips ²

quote_slippage_too_high

current_slippage_bps, max_slippage_bps

quote_total_capital_exceeded

available_borrowed_usdc_units, available_collateral_usdc_units, available_notional_usdc_units, cap_usdc_units, current_borrowed_usdc_units, requested_borrowed_usdc_units ⁴

quote_user_capital_exceeded

available_borrowed_usdc_units, available_collateral_usdc_units, available_notional_usdc_units, cap_usdc_units, current_borrowed_usdc_units, requested_borrowed_usdc_units ⁴

quote_user_position_limit_exceeded

available_capacity_usd_pips, current_notional_usd_pips, limit_usd_pips ²

risk_engine_market_disabled

reason ⁷

¹ The three notional_selector_* liquidity codes above all carry these params. notional_selector_empty_order_book is raised before any sizing is computed (one side of the book is empty) and carries no params.

² The *_position_limit_exceeded codes share this shape. available_capacity_usd_pips is limit − current, clamped at

³ There is no escape via lowering leverage — raise collateral to at least min_collateral_usd_pips.

⁴ The four *_capital_exceeded codes share this shape and cap protocol-borrowed capital (notional − collateral). requested_borrowed_usdc_units is the borrow this quote would add; available_borrowed_usdc_units is cap − current, clamped at 0. available_collateral_usdc_units and available_notional_usdc_units re-express that same borrow headroom as the collateral and notional you could still deploy at the leverage this quote requested — surface these directly in the UI instead of converting available_borrowed_usdc_units yourself. Both are present only when the requested leverage is above 1× (a 1× position borrows nothing, so there is no borrow headroom to convert).

⁵ Same base shape as the other position-limit codes, plus active_notional_usd_pips and in_flight_notional_usd_pips (the cap counts open positions + in-flight quotes).

⁶ max_supported_collateral_usd_pips is the value to surface in the UI — the maximum collateral the user can submit at their currently selected leverage and still fit. It maps directly to the collateral input.

⁷ params is only present when a disable reason is set. Current reason values: force_disabled (admin killswitch).

Example (quote_leverage_exceeds_model_max — the most common rejection):

Param
Meaning

max_leverage_bps

The accepted leverage — the highest the risk model allows on this market right now (50000 = 5×). Clamp the input to this and re-quote

current_book_leverage_bps

The leverage that was requested and rejected (80000 = 8×)

Snap the leverage slider to max_leverage_bps (rounded down to the market's leverage.step_bps) rather than asking the user to guess. The cap moves with market conditions, so re-read it from every rejection instead of caching it.

Example (quote_user_capital_exceeded, quote requested 2× leverage):

Rate limiting (429)

All endpoints are subject to rate limiting. Throttled responses include a Retry-After header indicating how many seconds to wait before retrying. Use this value instead of a fixed backoff when available.

Internal server error (500)

A 500 response indicates an unexpected error on our side. These are always safe to retry with exponential backoff.


Handling errors in practice

Check the error type and code

Retry logic

Some errors are retryable, others aren't.

Retryable
Not retryable

internal_server_error

quote_market_not_eligible

too_many_requests

quote_leverage_below_minimum

request_already_in_progress

quote_user_position_limit_exceeded

risk_engine_market_closed

risk_engine_market_disabled

quote_entry_excluded_market_type

quote_entry_excluded_sport

quote_event_not_started

customer_auth_invalid_wallet_address

For retryable errors, use exponential backoff starting at 1 second.

SDK note: The SDK's HttpClient handles 429 retries (with Retry-After backoff) and 401 token refresh automatically, so you only need to handle business-logic errors in your application code.

Last updated