Getting started
This reference has one page per endpoint and one per object the API returns. This page covers what every call shares and indexes the endpoints. Limits says how limits behave, and Errors and exit codes says which errors to retry. For a working client, read the open-source Chartnaut CLI on GitHub: src/client.ts makes every call on these pages.
Base URL
https://api.chartnaut.com/v1Every path in this reference is relative to it. GET /runs means GET https://api.chartnaut.com/v1/runs. The API is included on Starter and above.
Authentication
Send an API key in the Authorization header on every call except Start a sign-in and Poll a sign-in, which create a key:
curl https://api.chartnaut.com/v1/me \
-H "Authorization: Bearer cn_live_XXXX"The header must read Bearer, one space, then the key. Chartnaut checks each call in this order, and the first check that fails answers:
Check | Answer |
|---|---|
More than 30 failed key checks from your IP address in the last minute |
|
No |
|
The key is wrong, expired or revoked |
|
The account is on the waitlist |
|
The account is on Free |
|
Past your plan's requests a minute |
|
The key lacks the endpoint's scope |
|
A call refused for its scope has already counted toward your requests a minute. Most endpoint pages leave these answers out of their status tables.
Scopes
A key carries one or more scopes. Each endpoint needs exactly one, shown in the index below and on its page.
Scope | Lets the key |
|---|---|
| Read your account, instruments, docs, scripts, versions, a definition's events and runs, search the library, and validate a script |
| Create, save, rename and delete scripts |
| Start and cancel runs, and run a definition over history |
Revoke the current key works with any valid key, whatever its scopes.
Requests and responses
Send request bodies as JSON with Content-Type: application/json. Responses are JSON, except GET /docs/{topic} (markdown), format=csv on run results and a definition's events (CSV), and the two 204 answers, which have no body.
A request body can be up to 2.25 MB; a larger one gets 413 invalid_request. A script's files can total 2 MB, with at most 50 files of 512 KB each.
Heavy calls and long polls
Every call that carries a key counts toward your plan's requests a minute, across all your keys. API limits has the numbers for each plan.
Five calls are heavy: they validate code or start work, and only a few can be in flight at once. Past your plan's heavy calls at once, the next one gets 429 busy with Retry-After: 2.
POST /scripts/validatePOST /scriptsPUT /scripts/{slug}POST /scripts/{slug}/collectPOST /runs
Two calls can be long polls: POST /runs and GET /runs/{run_id} with wait above 0. A long poll holds until the run finishes or wait seconds pass. Past your plan's long polls at once, the call answers straight away with the run as it is, and nothing fails. POST /runs with wait keeps its heavy place for as long as it holds, so start runs with wait at 0 and wait on GET /runs/{run_id} instead.
Pagination
List endpoints take limit and cursor and answer with a page:
{
"data": [],
"next_cursor": "50"
}Pass next_cursor back as cursor for the next page, and stop when it is null. Treat it as opaque. A limit of 0, a negative one or one that is not a number falls back to the default, and one above the most is cut to the most. Run results and a definition's events put their rows under outputs or events instead of data.
Endpoint | Default | Most |
|---|---|---|
| 50 | 500 |
| 50 | 200 |
| 5,000 | 50,000 |
| 100 | 1,000 |
Errors
Every refused call answers with the same envelope:
{
"error": {
"code": "version_conflict",
"message": "ema-cross is at version 5, not 4. Pull it, re-apply your change, and save again.",
"details": {
"latest_version": 5
}
}
}Field | Type | Description |
|---|---|---|
| string | Stable. Branch on this |
| string | For people. It can change |
| object | Present only when there is something extra to act on. Today only |
| array of Diagnostic | Present only beside |
Errors and exit codes lists every code, its status and whether to retry it.
Timestamps and units
Times in JSON are RFC 3339 strings in UTC, such as 2026-09-01T00:00:00Z. Times Chartnaut stamps itself, such as created_at, can carry fractions of a second. Times you send in query parameters must be RFC 3339.
Indicator points carry t in Unix seconds. Durations are in seconds. Bar counts are at the run's timeframe. One compute unit is 1,000 bars evaluated, warmup and dependencies included.
Idempotency
POST /runs reads an Idempotency-Key header: any string up to 128 characters. A second POST /runs from your account with the same key returns the first run instead of starting another. The key has no expiry and is not checked against the body, so use a new key for each run you mean to start.
No other endpoint reads the header, and retrying them is still safe: a retried POST /scripts gets 409 conflict once the first one landed, and a retried PUT /scripts/{slug} gets 409 version_conflict. Read the script and carry on.
Endpoints
Account
Method | Path | Scope | Summary |
|---|---|---|---|
|
|
| |
|
|
| |
|
|
|
Instruments
Method | Path | Scope | Summary |
|---|---|---|---|
|
|
| |
|
|
|
Docs
Method | Path | Scope | Summary |
|---|---|---|---|
|
|
| |
|
|
|
Scripts
Method | Path | Scope | Summary |
|---|---|---|---|
|
|
| |
|
|
| |
|
|
| |
|
|
| |
|
|
| |
|
|
| |
|
|
| |
|
|
|
Definition events
Method | Path | Scope | Summary |
|---|---|---|---|
|
|
| |
|
|
| |
|
|
|
Runs
Method | Path | Scope | Summary |
|---|---|---|---|
|
|
| |
|
|
| |
|
|
| |
|
|
| |
|
|
|
Library
Method | Path | Scope | Summary |
|---|---|---|---|
|
|
| |
|
|
|
Sign-in
Method | Path | Scope | Summary |
|---|---|---|---|
|
| None | |
|
| None | |
|
| Any key |
Not yet available
Method | Path | Scope | Summary |
|---|---|---|---|
|
|
| Webhooks, |
Objects
Instrument, Script, File, Interface, Dependency, Version, Diagnostic, Event, Run, Failure, Summary and Library script.
