
Idempotency and Rate Limits: Writing API Order Flow That Never Double-Fires
Networks fail. Responses get lost after the server has already accepted your order. If your client reacts to an ambiguous failure by blindly re-sending, you can end up with double the position you intended on a funded account with a 3% daily loss limit. The MyFundedPerps API rate limits and idempotency system exist so you can retry aggressively and safely: this article explains the Idempotency-Key header, client_order_id, the request budgets, and the retry discipline that ties them together.
The Idempotency-Key header: required on every write
Every order creation and position-close request must include an Idempotency-Key header. That covers:
POST /v1/ordersPOST /v1/twap-ordersPOST /v1/scaled-ordersPOST /v1/positions/{position_id}/close
Use a unique value for each intended operation, and reuse that same value only when retrying the identical request on the same endpoint. The key may contain printable ASCII characters up to 128 characters; generate keys with real entropy, such as a UUIDv4 or ULID, and store them alongside your local order record.
MyFundedPerps binds the key to the API key, the endpoint, and a hash of the request. Critically, the binding is written in the same database transaction as the order, so two concurrent retries cannot create two accepted orders. That is the property that makes retries safe even under races.
What a retry returns
Two subtleties matter here. First, a 200 retry may show a more advanced state than the original response (pending may have become filled), so always use the status from the latest retry response. Second, a 422 rejection is itself idempotent: it keeps the original order_id and details on every matching retry, so a lost response cannot turn a rejected placement into an apparent success.
client_order_id: your reconciliation handle
Standard orders and position closes also accept an optional client_order_id in the JSON body. The two identifiers serve different purposes: Idempotency-Key prevents duplicate placement of one HTTP operation, while client_order_id names the resulting order in your own system.
The ID may contain non-space printable ASCII up to 128 characters, and IDs are unique across your entire MyFundedPerps user, including orders submitted with a different API key. If a response is lost, retrieve the order directly:
The response is the normal order page with either one match or an empty data array. You may add account_id to scope the match, but do not combine client_order_id with status, limit, or cursor. Reusing the ID with an identical placement returns the existing order, even after API key rotation; reusing it with a different payload returns 409 client_order_id_conflict.
The recommended pattern is both: persist a client_order_id with your local order intent before sending, and send a fresh Idempotency-Key per logical operation. Retries reuse the key; lost responses are reconciled by the client order ID.
The request budgets you must respect
Retry safety does not excuse hammering the API. The documented budgets:
These are shared ceilings, not a reserved allowance per user. At 7,500 simultaneous API users, the 30,000 requests/minute backstop averages four requests per user per minute. Do not build a continuous terminal by polling account, position, order, and quote endpoints every second; use the WebSocket market-data stream for prices and REST for state reconciliation.
Account-wide cancel and close operations have their own global budgets: empty operation snapshots at 60/minute (burst 12), planned work at 600 units/minute (burst 600, one unit per cancel target and two per close target), and advancing work at 600 units/minute (burst 50). A capacity rejection returns 429; wait for Retry-After and resume with the same idempotency key.
The retry discipline
The complete pattern for write requests:
- Generate a
client_order_idand persist it with your order intent before sending. - Generate a fresh
Idempotency-Keyfor the logical operation. - Send the request. On success, store the server order ID.
- On an ambiguous failure (timeout, connection drop,
500,503), retry with the same idempotency key, using exponential backoff with jitter. - On
429, respectRetry-Afterand resume with the same key. - If all retries fail ambiguously, reconcile by querying
client_order_idbefore even considering a new placement. - On
422, do not retry: the rejection is final and idempotent. Fix the underlying rule violation.
Error responses follow a stable envelope with code, message, and request_id; the same request_id appears in the X-Request-Id header. Keep it when contacting support. Validation failures (400) can include per-field details, which is also how you catch typos: the docs' own example flags a misspelled levrage field as unrecognized.
One special case: a voluntary Trader Lockout applies to API keys and bots as well as the terminal. Orders on a locked account return 422 with code account_trader_lockout_active and details.expiresAt as Unix milliseconds. Stop submitting until that time; neither the API nor Support can remove the lock early, and positions remain exposed to losses and normal liquidation rules while locked.
Test the failure paths before you fund them
All of this is testable without risk. The sandbox enforces identical rate limits and trading rules, so you can deliberately trigger 422, 429, and lost-response scenarios on paper. Read our sandbox testing guide for setup, and the WebSocket market data article for the read side of a well-behaved client. For the rules your orders will be judged against, see myfundedperps-rules-explained and myfundedperps-exposure-caps.
Frequently asked questions
Is the Idempotency-Key header optional on MyFundedPerps?
No. It is required on every order creation and position-close request: standard orders, TWAP orders, scaled orders, and position closes. Each logical operation gets a unique key, and you reuse that key only when retrying the identical request on the same endpoint.
What happens if I retry an order after a timeout?
With the same idempotency key and payload, a retry returns 200 OK with the order's current state instead of creating a duplicate. The binding is written in the same database transaction as the order, so even two concurrent retries cannot both create orders.
What are the MyFundedPerps API rate limits?
Reads: 120/minute per key (burst 60) and 300/minute per user across all keys. Trading writes: 30/minute per key (burst 15). There is also a 300/minute per-IP edge ceiling per environment and a shared 30,000/minute deployment backstop. Exceeding a budget returns 429; respect Retry-After.
What is the difference between Idempotency-Key and client_order_id?
The idempotency key identifies one HTTP operation and prevents duplicate placement when it is retried. The client order ID identifies the resulting order in your own system, is unique across your whole user, and lets you look up an order after a lost response, even after API key rotation.
Should I retry a 422 rejection?
No. A 422 means the trading engine rejected the order under a rule, and the rejection itself is idempotent: retries return the same order_id, rule code, and details. Diagnose the rule violation instead of resubmitting.