ProjectX Error Codes by Endpoint: What Each Number Means

On September 17, 2026, we sent the ProjectX API a login with a username that doesn’t exist. It answered HTTP 200 with this body:

{"token":null,"success":false,"errorCode":3,"errorMessage":null}

On the login endpoint, errorCode: 3 means InvalidCredentials. The same 3 from Order/place means InsufficientFunds, and from Auth/validate it means ExpiredToken. So which one just hit your bot?

That’s the trap with ProjectX error codes. They aren’t one list. The Gateway API behind TopstepX defines 17 separate error enums, and the number 2 alone carries seven meanings. Our Order/place code table covers the ten order-placement codes a TradingView bridge runs into. This guide decodes everything around them. You’ll see which endpoint sent a number, the message text behind it, the rejections your own TopstepX settings cause, and when resending an order is safe.

Key Takeaways

  • ProjectX defines 17 error enums, so errorCode: 2 has seven meanings. Read every code together with its endpoint.
  • On Order/place, code 2 covers all validation rejections, and errorMessage names the cause.
  • Pending codes mean the outcome is unknown. Check orders and positions before resending.
  • Token and routing failures return HTTP errors with no errorCode at all.

errorCode is the integer ProjectX puts in a response’s JSON body, next to a success flag, and 0 always means success. errorMessage is the optional text field beside it, and it’s often null. An enum is a fixed list of named numbers. ProjectX gives each endpoint’s response its own list, which is where the confusion starts.

How are ProjectX error codes numbered by endpoint?

ProjectX numbers its error codes per endpoint, not globally. We analyzed the live Gateway API spec on September 17, 2026, and found 17 error enums holding 65 failure codes under 30 distinct names. Only 0 = Success means the same thing everywhere. Every other number depends on which endpoint sent it.

In fact, three of those enums, for account search, contract search and available contracts, define nothing but Success. The other 14 reuse the same small integers for unrelated failures. Take errorCode 1. It’s UserNotFound on login and InvalidSession on logout and validate. On contract lookups and historical bars it’s ContractNotFound, on order lookup by ID it’s OrderNotFound, and on every order, position and trade call it’s AccountNotFound.

How many meanings each ProjectX errorCode carries The same errorCode number means different things depending on which endpoint returned it. One number, up to seven meanings Distinct error names per errorCode across 17 ProjectX enums errorCode 1 5 errorCode 2 7 meanings errorCode 3 6 errorCode 4 6 errorCode 5 5 errorCode 6 4 errorCode 7 4 errorCode 8 3 errorCode 9 3 errorCode 10 2 errorCode 2 can mean any of these: PasswordVerificationFailed · UnknownError SessionNotFound · UnitInvalid · OrderRejected OrderNotFound · PositionNotFound Code 0 is always Success. Our count, API spec as of Sept 17, 2026.

Why does that matter once a bot is live? A bot that maps codes with one global table will mislabel most failures. It’ll report a failed position close as a funding problem, or a bars request with a wrong contract ID as a missing account. Then someone spends an hour fixing the wrong thing.

Build the error map from the spec

The fix is dull, and it works. Key your error map by endpoint first and code second, and you don’t even have to type it. Every enum is machine-readable in the Swagger spec at https://api.topstepx.com/swagger/v1/swagger.json, with the names in x-enumNames, and this standard-library Python builds the whole map from it:

import json
from urllib.request import urlopen

SPEC_URL = "https://api.topstepx.com/swagger/v1/swagger.json"
spec = json.load(urlopen(SPEC_URL, timeout=10))
defs = spec["definitions"]

ERRORS = {}  # {"/api/Order/place": {0: "Success", 1: "AccountNotFound", ...}, ...}
for path, ops in spec["paths"].items():
    for op in ops.values():
        ref = op["responses"]["200"]["schema"].get("$ref")
        if not ref:
            continue  # /api/Status/ping returns plain text
        code_ref = defs[ref.split("/")[-1]]["properties"]["errorCode"]["$ref"]
        enum = defs[code_ref.split("/")[-1]]
        ERRORS[path] = dict(zip(enum["enum"], enum["x-enumNames"]))

def describe(path, body):
    name = ERRORS.get(path, {}).get(body["errorCode"], "unknown")
    return f"{path} errorCode {body['errorCode']} = {name}: {body.get('errorMessage')}"

print(describe("/api/Position/closeContract", {"errorCode": 6, "errorMessage": None}))

We ran it on September 17, 2026. It mapped 20 endpoints, since ping returns plain text, and printed OrderPending for that code 6 from Position/closeContract. Swap in Position/partialCloseContract and the same 6 comes back as OrderRejected.

What to log with every failure

Log the raw message next to the number, too, because the message often carries the real reason. A log line you can actually debug from, whether the bot runs on your desk or behind a bridge, needs four things:

  • The endpoint path, such as /api/Order/place
  • The HTTP status code
  • errorCode and errorMessage exactly as returned, including a null message, because null tells you something too
  • The X-Correlation-Id response header

Every response we received carried that correlation header, successes and failures alike, with a new value each time. It pins one request in your logs when you need to match a failure to a moment.

Which layer rejected the request?

A ProjectX request can fail at three layers. Only one of them uses errorCode. HTTP errors such as 401 and 400 arrive first, before any errorCode exists. Next comes the errorCode in the body. Then, even after success: true, the order itself can end up with status 5, Rejected.

What our test calls returned

We tested the first layer against the live API on September 17, 2026, with no account attached. Here’s what came back:

What we sentHTTP statusWhat came backWhat it tells you
Login with a made-up username and key200success: false, errorCode: 3, errorMessage: nullBad credentials, despite the 200
Login with an empty JSON body400A validation error naming userName and apiKeyThe login check never ran
Account search with no token401Empty body, header WWW-Authenticate: BearerNo token reached the API
Account search with a malformed token401Empty body, header WWW-Authenticate: Bearer error="invalid_token"A token arrived and was refused
GET instead of POST on Order/place405Empty body, header Allow: POSTWrong HTTP method
A misspelled path, /api/Orders/place404Empty bodyWrong URL

That table holds two details that save real debugging time. First, a failed login is still HTTP 200, so a client that only checks status codes treats bad credentials as success. Second, the WWW-Authenticate header tells you which 401 you’ve got. Plain Bearer means no token arrived at all, which usually points to a header-building bug. Bearer error="invalid_token" means a token arrived and was refused, typically because it expired or was malformed.

Bad request bodies belong to this layer too, and the documentation can hand you one. We found that the Search for Account page’s cURL sample uses -X 'undefined' and sends no body, although the endpoint requires onlyActiveAccounts. Copy the body from the Placing Your First Order page instead: {"onlyActiveAccounts": true}.

And rate limits? They live in this layer as well. ProjectX allows 200 requests per 60 seconds on most endpoints and 50 per 30 seconds on historical bars, then answers with HTTP 429. Our guide to API rate limits in trading automation covers backoff.

When ProjectX rejects an order it already accepted

The third layer is the one that catches people out. errorCode: 0 on Order/place only means ProjectX accepted the order. A fill comes later, if at all. OrderStatus is the lifecycle field every order carries after that: 1 Open, 2 Filled, 3 Cancelled, 4 Expired, 5 Rejected, 6 Pending, 7 PendingCancellation and 8 Suspended.

ProjectX can reject a working order well after accepting it. For example, TopstepX rejects a buy stop the moment it triggers if the fill would push the account past its maximum position size, then pulls it from working orders. No errorCode reports that. You’ll only see it through Order/search or the order events on the user hub.

Order/searchOpen also leaves out suspended bracket child orders. To see them, query /api/Order/v2/query with statuses set to Open and Suspended.

What do ProjectX login error codes 3, 7, 9 and 10 mean?

A failed login returns HTTP 200 with success: false, and four codes cover what you’ll actually meet. Our made-up username returned 3. That’s not the UserNotFound (1) the login enum also defines. A wrong username, a mistyped key and a revoked key all look the same. Can you tell them apart? Not from the response alone.

errorCodeNameWhat it meansHow to clear it
3InvalidCredentialsThe username and API key don’t match an active keySend your TopstepX sign-in username. An email address fails. Generate a fresh key and copy all of it.
7AgreementsNotSignedPlatform agreements are waiting. The message reads “Please log into the ProjectX platform and complete the required agreements”.Sign in to TopstepX, accept them, then retry
9ApiSubscriptionNotFoundNo active API subscription, so ProjectX can’t issue a tokenCheck the ProjectX Dashboard subscription and its link to your TopstepX profile
10ApiKeyAuthenticationDisabledAPI key login is switched off for your firmContact Topstep

Two failures never reach those codes. A body missing userName or apiKey gets HTTP 400 before any login check runs, exactly as our empty-body test showed. And a session token lasts 24 hours. After that, every call fails with HTTP 401 until you get a new one.

Close-up of Python code on a screen with a try and except block, the pattern a bot uses to catch a failed login or API call.

It’s easy to miss that logging in again issues a new token without invalidating tokens already in use. As a result, two scripts sharing one API key won’t knock each other offline.

Expiry is murkier, because the documentation disagrees with itself. The authentication page says to refresh with Auth/validate before the token expires, while the validate page says to re-validate once it has, and validate’s own enum even includes ExpiredToken (3).

We couldn’t test an expired token without an account. A validate call with no token simply returned 401. The safe pattern is to refresh well inside the 24 hours, and to log in again on any 401.

What’s inside errorCode 2 when Order/place rejects an order?

On Order/place, errorCode 2 (OrderRejected) is a catch-all that covers every validation rejection, including unsupported order types, invalid side or size, bad trailing-stop prices, duplicate customTag values and bracket rules. The number alone tells you almost nothing, so the reason you actually need sits in errorMessage, and that’s the field to log.

The Position Brackets rejection

The bracket message is the one to know first:

Brackets cannot be used with Position Brackets. You must enable Auto OCO Brackets.

It fires when an order includes stopLossBracket or takeProfitBracket while the account still uses Position Brackets, TopstepX’s default.

Heads up: ProjectX still creates the rejected order, however, and returns its ID in orderId. In other words, a returned orderId isn’t proof of a live order.

To clear it, go completely flat first, because TopstepX won’t switch bracket types while you hold a position. Then open Settings, choose Risk Settings, click Switch to Auto OCO Brackets and save the bracket setup. Or skip the bracket objects and manage exits another way.

Trailing stop, customTag and order type rejections

Trailing stops add five more:

errorMessageWhat triggered itHow to clear it
Trail Distance not set.Order type 5 sent without trailPriceSend trailPrice as a price level
Invalid trail price. Price is not aligned to tick size.trailPrice isn’t a multiple of the tick sizeRound the price to a valid tick
Cannot trail without a last priceThe contract hasn’t traded yetWait for a first trade, then resend
Invalid symbolNo quote is available for the contractCheck the contract ID and the session
Trail Distance exceeds maximum (1000)The distance works out above 1,000 ticksSend the price level where the trail should start

Why would a trail ever work out above 1,000 ticks? Almost always because someone sent a distance where trailPrice expects an absolute price. Take a contract with a 0.01 tick that last traded at 70.50. A 6-tick sell trail needs trailPrice 70.44. Send 0.06 instead and ProjectX reads a price near zero, works out a 7,044-tick distance and rejects it.

Code 2 hides two more causes in plain sight. A customTag is an optional label you attach to an order, and it must be unique across the account, so ProjectX rejects any order that reuses one. The enum also lists StopLimit as order type 3, but the Place Order page documents only types 1, 2, 4, 5, 6 and 7. We’d treat 3 as unsupported.

What about the other nine Order/place codes, from AccountNotFound (1) to AccountRejected (10)? Our plain-English guide to the ten Order/place codes covers them, so we won’t repeat them here.

Cancel and modify number the same failure differently

Order/cancel and Order/modify share one numbering. It doesn’t match Order/place. On both, 2 means OrderNotFound, 3 Rejected, 4 Pending, 5 UnknownError and 6 AccountRejected. Modify adds 7 for ContractNotFound. A code 3 from modify has nothing to do with funds, whatever 3 means on place.

The clearest example is a misaligned trailing-stop price. Send it through Order/place and you get errorCode 2. Send the same bad trailPrice through Order/modify and you get errorCode 3, with identical message text. Same mistake, different number.

Cancel and modify aren’t the only pair that renumbers a failure. A contract ID ProjectX doesn’t recognize, for instance, arrives under four different numbers depending on the call:

The same ProjectX failure under different errorCode numbers Map codes per endpoint, never with one global list. Same failure, different number The errorCode each endpoint uses for the same problem ContractNotFound 8 place 7 modify 3 close · partial 1 lookup · bars AccountRejected 10 place 6 cancel · modify 8 close 9 partial close Still processing (OrderPending, Pending) 6 place 4 cancel · modify 6 close 7 partial close Engine rejection (OrderRejected, Rejected) 2 place 3 cancel · modify 5 close 6 partial close UnknownError 7 place 5 cancel · modify 7 close 8 partial close 8 login 4 validate 2 logout place = Order/place · close = Position/closeContract partial close = Position/partialCloseContract lookup = Contract/searchById · bars = History/retrieveBars

Watch out: modify has no maximum-distance check. Order/modify accepts the 0.06 mistake that Order/place rejects, and your stop starts trailing 7,044 ticks behind the market with no error and no warning. If your code modifies trailing stops, check the price level yourself before you send it.

Cancel’s code 6, AccountRejected, comes with two messages. “Follower accounts cannot cancel orders” means the account mirrors a leader in a copy group, so cancel through the leader. “Live accounts not supported” means the account is live or brokerage, because this endpoint only works on simulated accounts. That matches Topstep’s rulebook anyway, since its Live Funded Account rules prohibit automated trading through the ProjectX API.

Before you debug either one, pull the account from Account/search. Its simulated and canTrade fields tell you in one call whether the account is simulated and allowed to trade.

Close and partial close are off by one

Position/closeContract and Position/partialCloseContract do nearly the same job, and their error codes match from 1 to 4. But partial close inserts InvalidCloseSize at 5 and pushes every later code down one slot, so a 6 from close means OrderPending while a 6 from partial close means OrderRejected.

errorCodePosition/closeContractPosition/partialCloseContract
1AccountNotFoundAccountNotFound
2PositionNotFoundPositionNotFound
3ContractNotFoundContractNotFound
4ContractNotActiveContractNotActive
5OrderRejectedInvalidCloseSize
6OrderPendingOrderRejected
7UnknownErrorOrderPending
8AccountRejectedUnknownError
9not usedAccountRejected

Which of these need special handling in code? Two of them do. InvalidCloseSize (5) means the size was zero, negative or larger than the open position, which often means the bot’s position count went stale, so re-read Position/searchOpen before closing.

OrderRejected (6) has two causes and no message to tell them apart: either the market was closed or halted, or the engine had no current price to close against. errorMessage is null either way. Both clear once the market reopens and starts quoting again.

PositionNotFound (2) deserves a second look before you log it as a failure. On a flatten command it often just means you’re already flat. Right after a contract roll, though, it can also mean the bot is closing the new month while the position still sits in the old one.

The rejections your own TopstepX Risk Settings cause

TopstepX’s Risk Settings page holds 10 tools, and several of them block orders just as a broker rejection would. Your bot only sees a refused order. Nothing in the API response says the block came from your own settings, so rule these out before you touch the code.

These settings can refuse an order outright:

SettingWhat it does to new ordersCan you reverse it?
Symbol BlockRejects every order for that symbol. PickMyTrade shows the rejection as “symbol is blocked”.Yes, remove the block
Contract LimitsRejects any order that would push open positions plus working orders past a per-symbol cap on that side. Brackets don’t count.Yes, change the limit
Trade LimitsBlocks new positions and orders once you hit a daily or weekly trade count. With a position open, only stops, targets, breakeven, flatten and cancel-all actions go through.No
Trade ClockBlocks new orders for a set time while you manage the open tradeNo, it runs until the timer ends or the trade closes
Personal Daily Loss Limit or Profit Target, set to Liquidate and BlockFlattens the account and blocks trading until the next trading dayNo
Manual Lock-OutLiquidates positions, cancels working orders and blocks trading for the time you pickNo

Traps that only show up under automation

Trade Limits don’t cancel orders placed before the limit hit, so a resting entry stays working after the lockout unless you cancel it. Contract Limits, on the other hand, count working orders on each side, so a bot that stacks resting entries can get its next order rejected before any of them fill.

Copy trading adds one more. In a TopstepX trade copier, you can’t create, close or adjust orders directly on a Follower account, and the API’s cancel endpoint enforces the same rule, as the cancel section above showed. TopstepX also ignores Trade Limits on Follower accounts.

Where should you look first? Open Settings, then Risk Settings, on the exact account the bot trades. The bracket error and the symbol block, both on PickMyTrade’s alert errors list, are fixed on that one screen.

Why are orders rejected at 3:10 PM CT or during a pause?

Topstep requires every position closed by 3:10 PM CT on weekdays and reopens trading at 5:00 PM CT, so orders sent inside that window fail. On Order/place, a closed market returns OutsideTradingHours (5), while PickMyTrade’s help pages show this alert-log message instead: “Trading is currently unavailable. The instrument is not in an active trading status.”

When TopstepX rejects new orders during the day Time-based rejections clear on their own. Resending during the window only repeats the error. When TopstepX turns new orders away One trading day in Central Time, 5:00 PM to 5:00 PM Equity index, FX, energy, metals, rates 3:10 PM: orders and positions start auto-cancelling (3:08: flattening) CBOT grains (corn, wheat, soybeans, meal, oil) 7:45-8:30 AM pause: no orders accepted 5 PM 9 PM 1 AM 5 AM 9 AM 1 PM 5 PM Open Paused Closed to new orders 4-5 PM roll Friday 3:10 PM to Sunday 5:00 PM: closed all weekend. CME Velocity Logic can briefly pause any product. Hover a product in TopstepX: orange means paused, red means closed.

The daily close isn’t the only wall. In particular, CBOT grain contracts, meaning corn, wheat, soybeans, soybean meal and soybean oil, pause every weekday from 7:45 to 8:30 AM CT, and Topstep accepts no orders on them then. CME Velocity Logic events can also pause any product briefly during a fast move.

Timing inside the window matters too. Topstep’s risk managers start flattening at 3:08 PM CT, and open positions and pending orders begin cancelling automatically at 3:10. An entry your strategy fires at 3:09 might fill. It won’t last.

How do you confirm a time-based rejection? Hover over the product in TopstepX’s product drop-down, which uses CME’s color codes: blue for pre-open, orange for paused, green for open and red for closed. A paused or closed market isn’t something to retry around, since resending inside the window just repeats the error.

What causes ContractNotFound and ContractNotActive around a roll?

On Order/place, ContractNotFound (8) means ProjectX doesn’t recognize the contract ID at all. ContractNotActive (9) means the ID is real, but it isn’t the contract ProjectX currently marks active. The second one is the roll-week error. TopstepX moves each product to the new front month in its 4 to 5 PM CT window, on a day its own algorithm picks.

ProjectX contract IDs look like CON.F.US.ENQ.Z26: futures, US, a product root, then the month code and two-digit year. The roots aren’t always the ticker you’d expect. E-mini S&P 500 is EP and E-mini Nasdaq-100 is ENQ, while Micro E-mini Nasdaq-100 keeps MNQ. Hard-code the September ID, and the bot starts getting code 9 the day December becomes the active contract.

A pink push pin marking a date on a paper calendar, like the roll date when a futures contract stops being the active month.

So how does a bot always send the right month? It asks once per session. Search the product with Contract/search, set live to false when you’re using sim data, and take the result that has activeContract: true. Don’t store contract IDs between sessions.

Trading NQ1! through PickMyTrade? Then a second calendar is in play. Under PickMyTrade’s contract rollover rule, continuous symbols switch to the next contract four days before expiration, with a warning six days out.

The September 2026 equity contracts expire on Friday, September 18, so that switch fell on Monday, September 14. TopstepX picks its own roll date, and nothing guarantees the two line up. The same rollover page recommends naming the exact contract month in your alerts, and roll week is when that advice pays off.

History and bar errors

History/retrieveBars has a short enum of its own: 1 ContractNotFound, 2 UnitInvalid, 3 UnitNumberInvalid and 4 LimitInvalid. The Retrieve Bars page lists units 1 to 6, from Second to Month, although the spec also defines 7 for Tick. One request returns at most 20,000 bars, so page through long histories.

We found one more documentation slip here. The same page labels contractId as an integer, yet the spec and every example use string IDs such as CON.F.US.RTY.Z24. Send the string.

Is it safe to resend an order after a ProjectX error?

Sometimes. Among ProjectX error codes, the pending codes are the risky ones. OrderPending, which is 6 on Order/place, means ProjectX hadn’t finished processing the order when it replied, so the order may already exist. Resend it blindly and you can double your position. The flowchart below sorts failures into four actions.

OrderPending means unfinished processing. It isn’t a resting limit order, which returns success with order status 1, Open. The unknown codes deserve the same caution. UnknownError is 7 on place and on close, and it doesn’t say whether anything went through.

Should you resend a failed ProjectX call? Pending and unknown codes are the dangerous ones: the order may already exist. Should you resend it? Triage a failed ProjectX call in this order 1. HTTP status not 200? 401, 429, 400, 404 or 405 Yes 401: log in again, then retry once. 429: wait, then retry. 400, 404, 405: fix the request itself. No 2. A pending or unknown errorCode? place 6-7 · cancel/modify 4-5 close 6-7 · partial close 7-8 Yes Don't resend yet. Check open orders and positions first. Resend only with the original customTag. No 3. Closed or paused market? OutsideTradingHours (5), 3:10 PM CT close, pauses Yes Wait for the session or pause to end, then resend. No 4. Everything else: fix the cause first Validation (errorCode 2 on place), funds, account, contract and login errors. Resending the same request gets the same rejection. ProjectX rejects a duplicate customTag with errorCode 2, so reusing the original tag can't open a second position.

That’s where customTag earns its keep. Give each new trading decision its own tag, and reuse that tag when you resend the same decision. ProjectX rejects a duplicate tag with errorCode 2, so a resend can’t open a second position.

If the resend does come back as a duplicate, the first order exists. Go find it instead of sending a third. Once you’ve fixed a validation error, though, send the corrected order under a fresh tag.

That said, closes are more forgiving than entries. Closing a position that’s already flat returns PositionNotFound rather than opening anything new. That makes a flatten the one command where a quick retry is low-risk.

How do ProjectX errors show up in PickMyTrade?

PickMyTrade connects to TopstepX with your ProjectX username and API key, then places orders through the same Gateway API covered above. ProjectX error codes and rejection messages land in your alert log next to PickMyTrade’s own. The table quotes 6 of them from PickMyTrade’s help pages, 3 from each side, so you know where each fix lives.

MessageWhere it comes fromHow to clear it
Brackets cannot be used with Position Brackets. You must enable Auto OCO Brackets.ProjectX, Order/place errorCode 2Go flat, then switch to Auto OCO Brackets in TopstepX Risk Settings
Trading is currently unavailable. The instrument is not in an active trading status.ProjectXWait for the session, then check pauses, Symbol Block and the contract month
symbol is blockedProjectX Risk SettingsRemove the symbol from Symbol Blocks
Symbol Mapping Not FoundPickMyTradeMap the symbol in your PickMyTrade settings
Account id … not found in user connectionPickMyTradeGenerate a new alert and select the connected account
Can not place order … check manual trade pause timePickMyTrade Trading Time SettingsCheck your trading window, set in Eastern Time, and any manual pause

Step-by-step fixes for the first two messages live in PickMyTrade’s help center: the Auto OCO Brackets switch and the “Trading is currently unavailable” guide.

A bridge does take some of this off your plate. PickMyTrade manages the session token for a ProjectX connection, so you’re not writing 24-hour refresh logic or 401 handling yourself. Its continuous-symbol mapping follows the rollover rule described above.

One thing no tool changes is Topstep’s policy. Topstep treats third-party integrations as used at your own risk and requires trading activity to originate from your personal device. Read how automation fits Topstep’s rules and confirm your setup with Topstep before you automate an evaluation.

Want these rejections in an alert log instead of raw JSON? PickMyTrade’s 7-day free trial needs no credit card, and the monthly plan is $50 with unlimited accounts.

The Topstep connection page shows what’s supported. You can also read who’s behind PickMyTrade or contact the PickMyTrade team with a specific error first.

Frequently asked questions

What does errorCode 3 mean in the ProjectX API?

It depends on the endpoint, because ProjectX error codes change meaning from one endpoint to the next. On login it’s InvalidCredentials, on Order/place it’s InsufficientFunds and on Auth/validate it’s ExpiredToken. Cancel and modify use 3 for Rejected, both position close calls use it for ContractNotFound, and bars use it for UnitNumberInvalid. Always read a code together with its endpoint.

Why does a failed ProjectX login return HTTP 200?

ProjectX reports login failures in the response body and leaves the status code at 200. A wrong username or API key returns HTTP 200 with success: false and errorCode 3, which we confirmed with a test call on September 17, 2026. Only a request missing userName or apiKey gets HTTP 400. Check success before reading the token.

Does success: true mean my ProjectX order filled?

No. On Order/place, success: true with errorCode 0 only means ProjectX accepted the order. What happens next shows up in the order’s status, where 2 means Filled and 5 means Rejected. TopstepX can still reject a working stop later, for example when it would exceed the account’s maximum position size as it triggers.

How do I fix “Brackets cannot be used with Position Brackets”?

Your TopstepX account is on Position Brackets while the order includes bracket objects, so Order/place returns errorCode 2. Go flat, open Settings, then Risk Settings, and switch to Auto OCO Brackets. Or remove the brackets from the order. ProjectX still assigns the rejected order an ID, so don’t mistake that orderId for a live order.

Can I cancel orders on a live account through the ProjectX API?

Not through Order/cancel. That endpoint only works on simulated accounts, so a live or brokerage account gets errorCode 6, AccountRejected, with the message “Live accounts not supported”. Follower accounts in a copy group get the same code with “Follower accounts cannot cancel orders”, so cancel those through the leader account.

Before you blame the bot

Most ProjectX error codes stop being mysterious once you read three things together: the HTTP status, the endpoint and the errorMessage. With 17 enums behind the API, the number alone tells you the least. Read it last, after the endpoint and the message.

Key your error map by endpoint, and treat pending and unknown codes as a signal to check orders and positions before you resend anything. Before you rewrite a line of code, check TopstepX’s Risk Settings, the clock and the contract month. Each one can reject a perfectly good order.

Setting up API access from scratch? Start with our Topstep API key walkthrough. Once orders are flowing, the TopstepX rules for payouts, drawdown and automation cover what a bot has to respect.


Written by the PickMyTrade Team, the automated futures and execution specialists behind PickMyTrade’s TradingView-to-broker webhook routing. Editorial note: we checked error codes, messages and test responses against the live ProjectX Gateway API and its documentation on September 17, 2026, and ProjectX can change them without notice. This article is for information only, not trading or financial advice.


Also Checkout: Tradeify Webhooks: Automate Trades from TradingView

Automate Your TradingView Strategies
Connect your alerts with PickMyTrade — automated trade execution, no coding required. Start free →
For AI tools & developers:View Markdown →

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top
Markdown version