Skip to main content

Conventions

Requests​

  • JSON only. Bodies need Content-Type: application/json (UTF-8), at most 64 KB and at most 20 levels of nesting.
  • Duplicate keys, NaN, Infinity and numbers too large to represent are rejected with 400 invalid_json.
  • Field names are camelCase. Enum values are lowercase snake_case strings.
  • Unknown fields are rejected. A create also rejects any field the API manages itself (for example id), with 422 field_read_only. A PATCH accepts those fields only when they match what is saved, so you can send back a resource you just read (see PATCH rules).
  • Times of day are "HH:MM" 24-hour strings in New York time (America/New_York), the same clock the web app uses. Times you send must fall on a 5-minute mark (09:30, 15:55), the same choices the web app offers. Timestamps are ISO 8601 in UTC, for example 2026-10-02T14:31:07Z.
  • Money values are plain numbers in your account currency. Limits are always positive amounts, for example "dailyLossLimit": 500 for a $500 loss limit.
  • Integers are fine in number fields (500 and 500.0 are the same). true/false are never accepted as numbers, and numbers are never accepted as text.

Path identifiers​

  • {account} is the account name, URL-encoded once. PROP 1234 (Eval) becomes PROP%201234%20(Eval).
    • NinjaTrader account names match exactly, including case.
    • Tradovate account names match without regard to case.
    • Account names, in a path or in a body, may contain ordinary spaces but cannot start or end with one. Any other whitespace (tabs, non-breaking spaces and similar), control characters, ; and = are refused with 400 invalid_path in a path and 422 validation_failed in a body.
  • {id} is the id the API returned when the resource was created. Ids contain only letters, digits, - and _.
  • A resource that does not exist, or belongs to someone else, returns 404 not_found.

Responses​

A read returns the resource in data:

{
"success": true,
"data": { "closingOnly": false },
"meta": { "requestId": "req_9f2c1a0b7d3e4f51", "etag": "\"c2:7Q0xMmZ3bTRkWmRVdz\"" }
}

A list returns an array, a count and an ETag for the whole list: "meta": { "requestId": "...", "count": 2, "etag": "\"c2:...\"" }. Lists are not paginated (the change history is the one exception).

A change returns the saved resource plus what happened:

{
"success": true,
"changed": true,
"data": { },
"changedFields": ["active", "sizing.maxQuantity"],
"delivery": { "status": "queued", "target": "nt8_addon" },
"warnings": [],
"meta": { "requestId": "req_...", "etag": "\"c2:...\"" }
}
  • changed: false means your request was valid but matched what was already saved. Nothing was written, and delivery is null. The call still counts toward your rate limit.
  • A create returns 201 Created with a Location header pointing at the new resource. A delete returns no data.
  • delivery says when the change reaches the engine that enforces it. See Effect timing.
  • warnings lists anything worth knowing that did not stop the request, each with a code and a detail. A read can carry warnings too, for example legacy_value_normalized when a stored value was saved before today's rules and is shown in its current form.

Errors use one shape everywhere. See Errors.

Every response, including errors, carries an X-Request-Id header. Include it when you contact support.

PATCH rules​

You sendResult
Field left outUnchanged
A valueSet
nullCleared, only where the field reference says the field can be null (for example autoFlattenTime: null turns auto-flatten off). Anywhere else, null is a validation error
A nested objectMerged: the children you send are set, the others are kept
An arrayReplaces the whole array
symbolReplacementsReplaces the whole map, like an array. Send {} to remove every replacement
A read-only field (for example id, created, state)Ignored when it matches what is saved, refused with a 422 when it does not (field_read_only, or a more specific code such as immutable_field; see Errors). updated is always ignored

The API merges your change onto the saved resource and validates the whole result before saving, so a change that would leave the resource in an invalid state is refused even if each field is valid on its own. A setting that was saved before today's rules and breaks one of them does not block unrelated changes.

Because matching read-only fields are ignored, you can read a resource, change the fields you care about, and send the whole object back as a PATCH. If a read-only value such as a monitor's state changed after your read, the echo no longer matches and is refused, so read again and resend.

ETags and If-Match​

Individual-resource reads, including global controls, news lockout and connection settings, return an ETag header and the same value in meta.etag. Creates, updates and restarts also return the saved resource's ETag. Lists, including audit pages, return an ETag for the whole list, which you can send in If-None-Match. Capability discovery and successful deletes do not return an ETag.

  • Send it back in If-Match on a PATCH, PUT, DELETE or restart to make sure nobody changed the resource since you read it. This uses strong comparison: a weak tag starting with W/ is rejected, even if the value otherwise matches. A mismatch returns 412 precondition_failed with the current strong ETag in meta.etag.
  • If-Match: * only checks that the resource exists.
  • If-Match is optional. Without it, your change applies on top of whatever is saved.
  • Send an ETag in If-None-Match on a GET to avoid downloading unchanged data: you get 304 Not Modified with no body when the tag matches. This uses weak comparison, so both "c2:..." and W/"c2:..." match the same current tag. The request still counts against your rate limit. A list's ETag changes whenever any item in it changes, including runtime status.

Management responses use Cache-Control: no-store, no-transform and are sent uncompressed, so the strong tag reaches you intact. If a proxy on your side still changes the header to a weak tag, use the strong value in meta.etag for If-Match and check that proxy's transformation settings.

An individual resource's ETag covers your settings only. Runtime status (for example a monitor's state) and timestamps do not change it.

If-Match protects against other API clients. It cannot stop a save made in the web app at the same moment, because the web app does not use ETags.

Retrying​

  • Rate-limit and concurrency refusals (429), and 503 management_busy, include a Retry-After header (seconds) and retryAfter in the body. Wait at least that long. Other 503 errors, such as disabled features or storage failures, do not necessarily include a retry delay; follow the specific error's guidance.
  • 503 outcome_unknown means the change may or may not have been saved. Read the resource before you retry.
  • Creates have no idempotency key. A create is refused as a duplicate when its natural key already exists: a monitor's account, a group or template name, or a copier with the same leader and follower and overlapping instruments (409 duplicate_copier, even when the existing copier is inactive). If a create response was lost, list the resources before you retry, so you can find the one that was saved instead of reading the 409 as a failure.