Tradovate API

Tradovate API liquidatePosition Endpoint 404

You POST to the liquidate endpoint expecting a clean close and get 404 Not Found instead. Nine times out of ten it's a mis-cased path, the wrong host, or the wrong HTTP verb, not a broken account or payload.

Reviewed by the PickMyTrade Trading Systems Team Last updated
· 8 min read
Tradovate API client showing a 404 Not Found response on a POST to order/liquidateposition

You want to flatten a position from code, so you POST to the liquidate endpoint expecting a clean close. Instead the server hands back 404 Not Found. Your token's valid, your other order calls work, the JSON looks perfect, and yet this one route acts like it doesn't exist. Frustrating, because it feels like the endpoint is broken or missing.

It isn't. A 404 is narrower than it feels: it's not about your account, your position, or your payload. It means the exact URL you POSTed to doesn't match any route on Tradovate's server. Nine times out of ten that's one of three things, the path is mis-cased or misspelled, you're pointed at the wrong host, or you sent the wrong HTTP verb. Let's rule them out in order, then lock down the correct call so you can flatten cleanly.

What a 404 Is Actually Telling You

Every HTTP error points at a different layer, and mixing them up sends you editing the wrong thing. A 404 means the path doesn't exist. A 401 means the path exists but you weren't authorized. A 400 means the path and auth were fine but your body was malformed. A 429 means you're hitting the rate limit. So if you're staring at a literal 404, stop tweaking your JSON body, the server never got far enough to care about it. It couldn't even find the route.

That single fact narrows the search a lot. Everything that produces a 404 lives in the request line: the method, the host, the version segment, and the path spelling. Work through those four, and the error clears.

Cause #1: The Path Is Mis-Cased or Misspelled

This is the big one, and it catches almost everyone at least once. Tradovate's REST paths are case-sensitive. The operation is spelled exactly liquidatePosition, lowercase l, camelCase capital P. Type it any other way and the router has nothing to match, so it answers 404. Lowercasing the whole thing is the classic slip, usually because you typed it from memory or your framework normalized the URL for you.

What you sent Result
/v1/order/liquidatePositionCorrect route
/v1/order/liquidateposition404 Not Found
/v1/order/LiquidatePosition404 Not Found
/v1/order/liquidate_position404 Not Found
/v1/order/liquidate-position404 Not Found
Diagram breaking down the correct Tradovate liquidatePosition URL into host, version segment, and camelCase path

The fix is boring and reliable: copy the operation name straight out of the API reference and paste it, rather than retyping it. One wrong character in the casing is all it takes. While you're at it, check for an accidental trailing slash or a duplicated segment like /order/order/liquidatePosition that a URL builder can sneak in.

Cause #2: Wrong Host or a Missing Version Segment

The full URL has four parts that all have to be right: scheme, host, the /v1 version segment, and the path. Put them together and you get:

  • Demo / simulation: https://demo.tradovateapi.com/v1/order/liquidatePosition
  • Live: https://live.tradovateapi.com/v1/order/liquidatePosition

Drop the /v1 and you 404, the root of the API has no /order/liquidatePosition route hanging directly off it. A typo in the host does the same, or fails DNS outright. And don't reach for the market-data host here: md.tradovateapi.com serves quotes, DOM, and charts, not order operations, so an md host won't route a liquidate call either.

One subtlety worth flagging: crossing environments, a demo token against the live host, or vice versa, more often shows up as a 401 than a 404, but it's still worth a look once your path is clean. Hostnames do get updated from time to time, so confirm the current ones in Tradovate's own developer documentation instead of trusting a URL you copied from an old gist.

Cause #3: You Sent the Wrong HTTP Method

The liquidate operation is POST-only. Send a GET and the server has no GET handler for that path, which surfaces as a not-found or method-not-allowed style error depending on how your client reports it. This is easy to trip over if you're testing by pasting the URL into a browser address bar, that's always a GET, so it will always fail here.

Test with a real POST from a proper client. You also need the right headers on the request: Content-Type: application/json and Authorization: Bearer <yourAccessToken>. A POST with the body sitting in the wrong place, or no JSON content type, can bounce in ways that look like a routing problem even when the path is fine.

Cause #4: Singular vs Plural, Which One Is Real?

Here's where a lot of the “the endpoint is missing” confusion comes from. The canonical, per-position operation is the singular /order/liquidatePosition. It takes an accountId and a single contractId and flattens that one net position. That's the route you'll find documented, and it's the one that consistently answers.

Some client libraries and community snippets reference a plural batch-style variant that takes a positions array instead of a single contract. If you copy a plural example but your target only exposes the singular route, or you assume the singular takes an array, you'll either 404 or send a body the endpoint ignores. When you're unsure, default to the singular /order/liquidatePosition with accountId and contractId, and verify the exact operation name and shape against the live API reference for the version you're calling. The spelling and the singular-vs-plural choice are exactly the two details that quietly produce a 404.

The Correct Call, Field by Field

Once the request line is right, the body is short.

POST https://demo.tradovateapi.com/v1/order/liquidatePosition
{ "accountId": 12345, "contractId": 67890, "admin": false, "customTag50": "" }

Field What it is
accountIdThe numeric account id, not the account name or spec. Pull it from /account/list.
contractIdThe numeric id of the contract you're holding, from /position/list. Must be greater than zero.
adminBoolean, and it needs to be present. Set it to false unless your API user genuinely has admin permission (see the gotcha below).
customTag50Optional label of up to 50 characters that rides along on the order. An empty string is fine.
Tradovate position/list JSON response with the accountId and contractId fields highlighted

The two ids are where people stall, so be deliberate. Call /account/list and read the id off the account object you want to trade. Call /position/list and each open position hands you both an accountId and a contractId along with its net quantity. Copy those exact numbers, the endpoint works on the (accountId, contractId) pair, not on a position id or a symbol string.

Tradovate API client showing a successful POST to order/liquidatePosition returning a 200 response

The Admin Gotcha: a 401 Hiding Behind Your Fix

This one trips people the moment they clear the 404. The admin field is required in the body, but the value you give it matters. Set admin: true when your API user isn't provisioned for admin access, and the call flips straight to 401 Unauthorized. Set admin: false and the same request goes through. So unless you know your user carries admin scope, send "admin": false. If you just fixed a 404 and now you're staring at a 401 on the very next attempt, this is almost always the reason, not your token, not your account id.

What the Endpoint Does, and Doesn't, Give You

A few behaviors are worth knowing before you build on top of this call:

  • It flattens the whole net position for that (accountId, contractId) pair. Under the hood it places a closing order, which means it won't fill when the market's closed, run it during trading hours for the instrument.
  • It's one call per contract. To flatten everything on an account, pull /position/list and loop, firing one liquidatePosition per open contractId. There's no single “flatten the account” body on this route.
  • It's race-safe. If the position already closed between your check and the call, a stop filled, say, you get a clean 200 with an empty body instead of an error. That's deliberate, and it's safer than synthesizing an opposite-side order, which could accidentally open a reverse position.
  • It returns no orderId. Because there's no order id in the response, you can't read the closing fill price directly. If you need that price for P&L, reconcile against the execution and fill reports (execution reports, fill pairs) or the position log.

A Quick Checklist to Clear the 404

Check What to confirm
Exact spellingliquidatePosition in camelCase, not lowercase, not snake_case, not hyphenated.
Full URLScheme + host + /v1 + /order/liquidatePosition, with the version segment present.
Right hostdemo.tradovateapi.com or live.tradovateapi.com, never the md market-data host.
POST, not GETPOST with Content-Type: application/json and a Bearer token; no browser address-bar tests.
Numeric idsaccountId from /account/list, contractId from /position/list, both greater than zero.
admin valueInclude admin; set it to false unless your user has admin permission, to dodge a follow-on 401.

Where PickMyTrade Fits

Most people wrestling with this 404 don't actually want to become Tradovate API plumbers, they want a strategy to open, manage, and close positions without babysitting endpoints, ids, and tokens. PickMyTrade routes TradingView alerts to Tradovate and can close or flatten positions as part of a strategy, so you don't have to hand-build liquidate calls, chase account and contract ids, or manage tokens.

  • No hand-built liquidate calls your strategy enters and flattens on Tradovate without a single hand-coded API request.
  • Account and contract resolution handled the ids that trip up raw calls are resolved for you.
  • Managed auth tokens and hosts are handled automatically, so a 401-hiding-behind-a-404 never becomes your problem.
  • Rate-limit-safe routing spaces order flow so nothing bounces off Tradovate's request limits.

Automate the Whole Order Flow

Want your TradingView strategy to enter and flatten on Tradovate without hand-coding a single API call? See how PickMyTrade automates the whole order flow.

Start Your Free 5-Day Trial

Frequently Asked Questions

A 404 means the URL path doesn't exist exactly as you sent it. The most common cause is casing: Tradovate's REST paths are case-sensitive, and the operation is spelled liquidatePosition with a capital P. POST to /v1/order/liquidateposition in all lowercase and you get 404 Not Found, because that route isn't defined. The same happens if you drop the /v1 version segment, point at the wrong host, or send a GET instead of a POST. Fix the path first, before you touch the body.

The canonical, per-position endpoint is the singular /order/liquidatePosition. It takes a numeric accountId plus a single contractId and flattens that one net position. Some client libraries and snippets reference a plural batch variant that accepts a positions array, so copying a plural example against a route that only serves the singular (or the reverse) can 404. When in doubt, use the singular /order/liquidatePosition with accountId and contractId, and confirm the exact operation name against the live API reference for your version.

Both are numeric ids, not names. Call /account/list to get your account's id field, and call /position/list to get each open position's accountId and contractId. Copy those numeric values straight into the request body. The contractId must be greater than zero, and it has to be the contract you're actually holding, not a product root or a symbol string.

A 401 means the route exists but the request wasn't authorized, which is a different problem than a 404. On this endpoint the usual trigger is the admin field. It's required in the body, but if you set admin to true when your API user isn't provisioned for admin permission, the call comes back 401 Unauthorized. Set admin to false and it goes through. So if fixing the path turned your 404 into a 401, flip admin to false.

No. A successful liquidation returns a clean 200 with no orderId, so you can't read the closing fill price directly from the response. It's race-safe: if the position already closed, you still get a 200 with an empty body instead of an error. To capture the closing price for P&L, reconcile against the execution and fill reports or the position log rather than expecting an order id back.

Yes. PickMyTrade routes TradingView alerts to Tradovate and can close or flatten positions as part of a strategy, so you don't have to hand-build liquidatePosition calls, chase accountId and contractId lookups, or manage tokens yourself. It's a shortcut for automation, not a replacement for the raw API when you specifically need low-level control.

This guide is for educational and informational purposes only and is not financial, investment, or trading advice. Trading futures and other leveraged products carries a substantial risk of loss and is not suitable for every investor. PickMyTrade is an independent third-party automation platform and is not affiliated with, endorsed by, or sponsored by Tradovate, Inc. All related names, logos, and trademarks are the property of their respective owners. Platform features and steps change over time, so always confirm the current process in the official Tradovate platform and documentation before acting.