Get started

Authentication

Your server authenticates with an API key. A web page never holds a key: your server hands it a short-lived token to open the stream.

API keys

Create your keys in your account. A key is shown only once, when it is created: copy it right away. We keep no readable copy, so a lost key cannot be recovered, only rotated.

KeyDataUse
fc_test_…Sandbox rights: delayed by 10 minutes, the last 7 days of historyBuilding and testing your integration, for free
fc_live_…The rights of your plans: real time with Live (no history), full history with Historical. Without a plan, the Sandbox rightsProduction

A test key always has the Sandbox rights, whatever your plan. Apart from the delay, it behaves exactly like a live key: same messages, same cursors, same responses.

Send the key in the Authorization header, on every REST request and when opening the WebSocket:

BASH
curl https://api.fathomcharts.com/v1/usage -H "Authorization: Bearer $FATHOM_CHARTS_API_KEY"

The word Bearer is case-insensitive, but required: a bare key without Bearer is refused (401 over REST, code 4001 on the WebSocket).

A missing, invalid, expired (API key expired) or revoked key is rejected with 401 UNAUTHORIZED over REST, with the header WWW-Authenticate: Bearer realm="fathomcharts" (plus error="invalid_token" when a key was sent). On the WebSocket, the connection opens first (a browser cannot read why an opening was refused), then it is closed right away with code 4001. A valid key that lacks the required scope is rejected with 403 FORBIDDEN, or code 4003 on the WebSocket.

Scopes and restrictions

A key only grants what you allow it to. If it leaks, the damage stays contained: a key that only reads the stream has no business starting exports.

Scope (scopes)Allows
streamThe real-time stream, and creating stream tokens for browsers
historyHistorical queries and their estimates
exportExports
keysManaging keys through the API

You can also restrict a key to specific instruments or IP addresses, and give it an expiry date. Through the API, with a key that has the keys scope:

BASH
curl https://api.fathomcharts.com/v1/keys \
  -H "Authorization: Bearer $FATHOM_CHARTS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"collector","environment":"live","scopes":["stream","history"],"instruments":["NQ.front"],"allowedIps":["203.0.113.0/24"]}'
FieldContent
nameKey name, 1 to 64 characters
environmentlive or test
scopesAt least one of stream, history, export, keys
instrumentsOptional: allowed instruments (up to 32), each a symbol or an alias of GET /v1/instruments, case-sensitive. Omitted: all
allowedIpsOptional: allowed IP addresses or CIDR ranges (up to 32). Omitted: any
expiresAtOptional: expiry date, in the future, as a timestamp in nanoseconds since January 1, 1970 (UTC), as a string

The response contains the full key in the key field, once, and its id in id. An invalid field is rejected with 400 INVALID_PARAMETERS, and errors points at the faulty entry (/instruments/0 for an unknown instrument, /allowedIps/1 for a malformed address, /expiresAt).

GET /v1/keys lists every key of the account, without the secret: the active keys, and those revoked or expired in the last 30 days. The state field of each key is:

stateMeaning
activeA valid key, with no expiry date.
expiringA key valid until its expiresAt, in the future.
expiredexpiresAt has passed: the key is rejected with 401 (API key expired), and the WebSocket connections opened with it are closed (code 4003, reason KEY_REVOKED).
revokedA revoked key, through DELETE or a rotation without grace period.

Instrument restriction

Each instruments entry allows the instrument it names in GET /v1/instruments, with the same rule over REST and on the WebSocket:

  • NQ.front allows NQ.front and the contract it designates today (NQZ6);
  • NQZ6 allows NQZ6, and NQ.front as long as it designates NQZ6. Over history, a query on NQ.front reads the contract designated at from: for a range when NQ.front designated NQU6, a key restricted to NQZ6 is rejected with 403 FORBIDDEN;
  • NQ.front stays allowed over history for any range, even when it designated another contract (NQU6); that contract, requested by its symbol, is not;
  • an expired contract is only allowed by an identical entry (NQU6).

Managing keys with a key

A key with the keys scope can only create, rotate or revoke keys that have no more rights than its own, on every criterion:

  • the same kind (test or live);
  • scopes included in its own;
  • if it is restricted to instruments, a key restricted too, whose every instruments entry is literally one of its own (a key restricted to NQZ6 cannot manage a key restricted to NQ.front, nor the reverse);
  • if it is restricted to IP addresses, a key restricted too, whose every range lies within one of its own;
  • if it expires, a key that expires no later than it does.

Otherwise the request is rejected with 403 FORBIDDEN, and the message names the criterion, for example Cannot create a key with scopes beyond this key's own: stream or Cannot revoke a key without instrument restriction: this key is restricted to NQ.front. From your account, you manage every key of the account, without these limits.

An account can have up to 10 valid keys (state active or expiring). All your keys share the same limits: creating more does not raise your quotas.

Rotating a key

To change a key without interrupting your service, rotate it (<id>: the key’s id field, from GET /v1/keys):

BASH
curl -X POST https://api.fathomcharts.com/v1/keys/<id>/rotate \
  -H "Authorization: Bearer $FATHOM_CHARTS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"gracePeriodSeconds":172800}'

gracePeriodSeconds is required, from 0 to 604800 (7 days). The response contains the new key, with the same scopes, restrictions and expiry date, and, in previous, the state of the old one. The old key keeps working during the grace period (2 days here), giving you time to deploy the new key everywhere (state expiring, with expiresAt at the end of the grace period), then it expires (state expired): it is rejected over REST, and the WebSocket connections still open with it are closed within seconds (code 4003, reason KEY_REVOKED). With 0, it is revoked immediately (state revoked). A key can only be rotated once: a second rotation is rejected with 409 CONFLICT.

Revoking a key

DELETE /v1/keys/<id>, or from your account. Revocation is immediate: WebSocket connections opened with the key are closed within a second (code 4003, reason KEY_REVOKED).

Browser access

An API key must never appear in a web page: any visitor could read it. To show the stream in a browser:

  1. your server requests a stream token with its key;
  2. it passes the token to the page;
  3. the page opens the WebSocket and sends the token.

On the server:

BASH
curl -X POST https://api.fathomcharts.com/v1/stream-tokens -H "Authorization: Bearer $FATHOM_CHARTS_API_KEY"

The response contains the token (token) and its expiry (expiresAt). With the SDK: rest.streamToken(), rest being an instance of FathomChartsRest.

In the page, give the SDK a function that fetches a token from your server (here a /api/fathom-charts-token route of your backend that makes the call above). The SDK calls it on every connection and handles the rest:

TS
import { FathomChartsStream } from '@fathom-charts/sdk';

const stream = new FathomChartsStream({
  url: 'wss://stream.fathomcharts.com/v1',
  streamToken: async () => {
    const response = await fetch('/api/fathom-charts-token', { method: 'POST' });
    const { token } = (await response.json()) as { token: string };
    return token;
  },
});

for await (const event of stream.subscribe({ instrument: 'NQ.front', indicator: 'big-trades', params: { minimum: 30 } })) {
  console.log(event.type, event);
}

Without the SDK, send the token in an auth message, first, within 5 seconds of opening the connection:

TS
const { token } = (await fetch('/api/fathom-charts-token', { method: 'POST' }).then((r) => r.json())) as { token: string };
const ws = new WebSocket('wss://stream.fathomcharts.com/v1');
ws.onopen = () => {
  ws.send(JSON.stringify({ t: 'auth', token }));
  ws.send(JSON.stringify({
    t: 'subscribe', sub: 'bt', instrument: 'NQ.front', indicator: 'big-trades',
    params: { minimum: 30 }, mode: 'live', from: 'live',
  }));
};
ws.onmessage = (e) => console.log(JSON.parse(e.data));

Good to know:

  • a token is valid for 60 seconds and works only once. It opens the connection, which then stays open as long as you need. Every reconnection needs a new token;
  • the token has the same scopes and limits as the key that created it, and stops working if that key is revoked;
  • any website can use a token: only hand tokens to your own users, once they are authenticated;
  • the key’s allowed IP addresses (allowedIps) apply to the token request, made by your server, not to the browser that opens the connection with the token;
  • an accepted auth gets no answer: send your subscribe messages right away. Any later auth on the same connection is ignored.

If the first message is not a valid auth, or does not arrive within 5 seconds, the connection is closed with code 4001.

Signing in to the portal

Your account (keys, plans, billing, usage) has no password. On the Sign in page, enter your email address: you receive a sign-in link valid for 15 minutes, usable once. You can request up to 5 links per hour.

Link rejected? It has expired or was already used: request a new one. Once signed in, your session stays open for 30 days in that browser.