> For clean Markdown of any page, append .md to the page URL. > For a complete documentation index, see https://partner.ninjatrader.com/connect/overview/core-concepts/architecture-overview/llms.txt. > For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://partner.ninjatrader.com/_mcp/server. # API Architecture & Design Understanding the NinjaTrader Partner API architecture helps you build robust, scalable integrations that leverage our high-performance trading infrastructure. ## Architecture Overview The NinjaTrader Partner API is built on a modern, cloud-native architecture designed for institutional-grade trading operations: ![NinjaTrader Partner API Architecture Overview](/_fern-img/f5002ffd3250f8f2ebf55d60de4b33ecc216358d2809111165bf120638068d44.webp) ## Core Design Principles ### Operation-Based Design Our API follows operation-based patterns with predictable endpoint structures: **Common Operations:** These `GET` query operations are common across most entity types. Familiarize yourself with these requests as they will be some of the most common requests you make during your integration. ``` GET /[entity]/item?id={id} # Get specific entity by ID GET /[entity]/list # List all entities GET /[entity]/find?name={name} # Find entity by name GET /[entity]/deps?masterid={id} # Get dependent entities GET /[entity]/items?ids={id1,id2} # Get multiple entities by IDs ``` **Action Operations:** ``` POST /user/createEvaluationAccounts # Create new account entities POST /entity/update # Update existing entity ``` ### Real-Time Data Streaming Real-time updates are a core service of our API. You can run your whole application from WebSocket instances. We now support "socket sharding" as well, to assist in recording and handling large amounts of real-time data. Our WebSocket protocol is new-line delimited (`\n`), where each line represents a separate parameter of the request. #### WebSocket Message Format ```json \n\n\n ``` #### Example Message: ```javascript // Get details on a list of accounts ws.send( 'account/items\n1\n\n{"ids": [123456, 234567, 345678]}' ); ``` The query and body fields are each optional, so a request can be endpoint-only, endpoint plus query, or endpoint plus body: * Endpoint only: `executionReport/list\n4\n\n` * Endpoint plus query: `tradingPermission/ldeps\n8\nmasterids=1` * Endpoint plus body: `contract/rollcontract\n33\n\n{"name":"YMZ6","forward":true,"ifExpired":true}` For more information on real time data access, see the [Websockets](/overview/core-concepts/web-sockets/connection-overview) section. **Response Format:** Our WebSocket frame format is inherited from the [SockJS protocol](http://sockjs.github.io/sockjs-protocol/sockjs-protocol-0.3.3.html#section-42). WebSocket responses are one of four characters: * `o`: open frame. Every time a new session is established, the server must immediately send the open frame. * `a`: data frame, this message is always followed by a JSON array * `h`: heartbeat frame, basically a ping. The server sends a heartbeat about every 2.5 seconds. To keep the connection alive the client must also send a response beat in the form of an empty array, stringified ('\[]') * `c`: close frame Each of the `a` data frames will be followed by a JSON array. There are two main types of message you can receive from the server - real-time event messages, and request-response messages. #### Parsing Frames Each frame is a single type character followed by an optional JSON payload. A small helper like `prepareMsg` splits a raw frame into a `[type, payload]` tuple you can destructure: ```js function prepareMsg(raw) { const T = raw.slice(0, 1) const data = raw.slice(1) return [T, data ? JSON.parse(data) : null] } // usage: mySocket.onmessage = msg => { const [T, data] = prepareMsg(msg.data) if (T === 'a') { // react to data } } ``` #### Server Event Message An event message has the following structure: ```js { "e":"props", "d":{ "entityType":"order", "eventType":"Created", "entity":{ "id":210518, "accountId":25, "contractId":560901, "timestamp":"2016-11-04T00:02:36.626Z", "action":"Sell", "ordStatus":"PendingNew", "admin":false } } } ``` The "e" field specifies an event kind: * `"props"`: this is a notification that some entity was created, updated or deleted. The `"d"` field in this case specifies details of the event with the next structure: * `"entityType"` field * `"entity"` field. JSON structure of object (or array of objects) specified in this field is identical to JSON of entity that accessible via corresponding REST API request like entityType/item. For example, if entityType=account, JSON can be found in the response specification of account/item call * `"eventType"` field with options `"Created"`, `"Updated"` or `"Deleted"` * `"shutdown"`: a notification before graceful shutdown of connection. For this type `"d"` field specifies details: * `"reasonCode"` field with options `"Maintenance"`, `"ConnectionQuotaReached"`, `"IPQuotaReached"`, `"DeviceQuotaReached"` * `"reason"` field is optional and may contain a readable explanation * `"md"` and `"chart"`: these notifications are used by market data feed services. Their `"d"` fields will be described in the Market Data section of WebSockets. * `"clock"`: Market Replay clock synchronization message. #### Response Message A response message is issued when a client makes a request. These messages are intended to mimic REST API responses and have the following structure: ```js { "i":26, "s":200, "d":{ "id":478866, "name":"6EZ6", "contractMaturityId":23574 } } ``` * `"i"` field is an id of the corresponding client request (see "Client requests" below). A response's id will always match the id of the request that generated it. * `"s"` field is an HTTP status code of the response * `"d"` field is a content of response. If HTTP status is 2xx, this field contains JSON response as defined in Swagger specification of the corresponding request. Otherwise, "d" is a string representation of error text. ## Error Handling Architecture ### Error Classification The NinjaTrader Partner API uses a consistent error handling pattern across all endpoints. Errors are communicated through HTTP status codes and structured response fields. #### REST Response Types We follow typical HTTP status code format for most responses. See [Error Handling](/overview/core-concepts/error-handling) for more details. #### Standard Error Response Format Most API errors that send a `200` level response return JSON containing the `errorText` field. This is the standard error response structure used throughout the API: ```json { "errorText": "Invalid access token", "accessToken": null } ``` ## API Usage Patterns & Conventions The NinjaTrader Partner API exposes data with fine granularity to avoid limiting how applications compose them. It's the responsibility of client applications to request all needed dependencies and join them. ### Query Data Operations All data query operations use HTTP GET method when called via REST API. #### Query by ID All entities have unique IDs and can be requested using the `item` operation: **`cURL`** ```bash cURL curl -X GET \ --header 'Accept: application/json' \ --header 'Authorization: Bearer YOUR_TOKEN' \ 'https://demo.tradovateapi.com/v1/order/item?id=1000' ``` **`TypeScript`** ```typescript TypeScript const response = await fetch(`${baseUrl}/order/item?id=1000`, { headers: { Authorization: `Bearer ${accessToken}`, "Content-Type": "application/json", }, }); const order = await response.json(); ``` **`Python`** ```python Python import requests response = requests.get( f'{base_url}/order/item', params={'id': 1000}, headers={ 'Authorization': f'Bearer {access_token}', 'Content-Type': 'application/json' } ) order = response.json() ``` #### Query All Entities Use the `/list` endpoint to retrieve all entities of a particular type: **`cURL`** ```bash cURL curl -X GET \ --header 'Accept: application/json' \ --header 'Authorization: Bearer YOUR_TOKEN' \ 'https://demo.tradovateapi.com/v1/fill/list' ``` **`TypeScript`** ```typescript TypeScript const response = await fetch(`${baseUrl}/fill/list`, { headers: { Authorization: `Bearer ${accessToken}`, "Content-Type": "application/json", }, }); const fills = await response.json(); ``` **`Python`** ```python Python response = requests.get( f'{base_url}/fill/list', headers={'Authorization': f'Bearer {access_token}'} ) fills = response.json() ``` **`C#`** ```csharp C# List result = apiInstance.FillList(); ``` #### Query by Master-Detail Relationship Use the `deps` operation to load dependent entities. The `masterid` parameter should be the ID of the master entity: **`cURL`** ```bash cURL curl -X GET \ --header 'Accept: application/json' \ --header 'Authorization: Bearer YOUR_TOKEN' \ 'https://demo.tradovateapi.com/v1/position/deps?masterid=123' ``` **`TypeScript`** ```typescript TypeScript const response = await fetch(`${baseUrl}/position/deps?masterid=123`, { headers: { Authorization: `Bearer ${accessToken}`, "Content-Type": "application/json", }, }); const positions = await response.json(); ``` **`Python`** ```python Python response = requests.get( f'{base_url}/position/deps', params={'masterid': 123}, headers={'Authorization': f'Bearer {access_token}'} ) positions = response.json() ``` **`C#`** ```csharp C# List result = apiInstance.PositionDependents(accountId); ``` #### Query by Name Some entities (accounts, products, contracts, currencies) have names and can be found using the `find` operation: **`cURL`** ```bash cURL curl -X GET \ --header 'Accept: application/json' \ --header 'Authorization: Bearer YOUR_TOKEN' \ 'https://demo.tradovateapi.com/v1/product/find?name=ES' ``` **`TypeScript`** ```typescript TypeScript const response = await fetch(`${baseUrl}/product/find?name=ES`, { headers: { Authorization: `Bearer ${accessToken}`, "Content-Type": "application/json", }, }); const product = await response.json(); ``` **`Python`** ```python Python response = requests.get( f'{base_url}/product/find', params={'name': 'ES'}, headers={'Authorization': f'Bearer {access_token}'} ) product = response.json() ``` **`C#`** ```csharp C# Product result = apiInstance.ProductFind("ES"); ``` ### Submit Data Operations Operations that submit data use the HTTP POST method, with the payload as a JSON object in the request body. Operation names follow no general rule; each reflects what it does (for example, `order/placeorder` places an order and `order/liquidateposition` cancels all orders for a position). ```js const URL = 'https://demo.tradovateapi.com/v1' const body = { accountSpec: yourUserName, accountId: yourAcctId, action: "Buy", symbol: "MYMM1", orderQty: 1, orderType: "Market", isAutomated: true // must be true unless a human placed the order directly } const response = await fetch(URL + '/order/placeorder', { method: 'POST', headers: { 'Accept': 'application/json', 'Authorization': `Bearer ${myAccessToken}`, }, body: JSON.stringify(body) }) const json = await response.json() // { orderId: 0000000 } ``` ### Batch Operations For efficiency, replace multiple `item` operations with one `items` operation using comma-separated IDs: **`cURL`** ```bash cURL curl -X GET \ --header 'Accept: application/json' \ --header 'Authorization: Bearer YOUR_TOKEN' \ 'https://demo.tradovateapi.com/v1/contract/items?ids=840972,840944' ``` **`TypeScript`** ```typescript TypeScript const response = await fetch(`${baseUrl}/contract/items?ids=840972,840944`, { headers: { Authorization: `Bearer ${accessToken}`, "Content-Type": "application/json", }, }); const contracts = await response.json(); ``` **`Python`** ```python Python response = requests.get( f'{base_url}/contract/items', params={'ids': '840972,840944'}, headers={'Authorization': f'Bearer {access_token}'} ) contracts = response.json() ``` **`C#`** ```csharp C# var ids = new List { 840972, 840944 }; List result = apiInstance.ContractItems(ids); ``` **Note:** The number of returned entities may be less than requested IDs (if not found), and order is not guaranteed. ### Error Handling Patterns The API uses two methods to communicate errors: #### HTTP Status Codes Used for request-level errors: * **401** - Unauthorized (invalid token) * **403** - Forbidden (insufficient permissions) * **404** - Not Found (resource doesn't exist) * **429** - Too Many Requests (rate limited) * **500** - Internal Server Error #### Business Logic Errors Communicated via `errorText` field in successful HTTP responses: ```json { "errorText": "Insufficient buying power for this order", "orderId": null, "timestamp": "2024-03-15T14:30:00Z" } ``` ## Best Practices ### Performance Optimization 1. **Minimize API calls** - API calls are rate-limited. Although this structure is flexible, to prevent disruptions consider the following: * Use batch endpoints where possible * Implement efficient caching * Optimize database queries by storing relevant entries in your database. * In many cases it can help to create an in-memory map of relevant entities. 2. **Handle rate limits gracefully** * See [Rate Limits & Best Practices](/overview/core-concepts/rate-limits) ## Next Steps Now that you understand our architecture: 1. **[Rate Limits & Best Practices](/overview/core-concepts/rate-limits)** - Learn usage limits and optimization 2. **[Penalty Tickets](/overview/core-concepts/penalty-tickets)** - Handle penalty-ticket rate-limit responses 3. **[Error Handling](/overview/core-concepts/error-handling)** - Implement robust error handling 4. **[WebSocket Connections](/overview/core-concepts/web-sockets/connection-overview)** - Real-time data streaming