Exports
Pour une longue période (une journée de footprint, des mois de données pour un backtest), un export remplace la pagination : une seule requête, et les résultats arrivent au fil du calcul.
Les exports sont inclus dans l’offre Historique et demandent une clé avec le scope export.
Quand utiliser un export
| Besoin | Utilisez |
|---|---|
| Quelques milliers de résultats, pour un affichage | Une requête historique |
| Une journée entière ou plus, un backtest, une base de données | Un export |
| Charger le passé puis suivre le marché | from: {"time": …} sur le WebSocket |
Lancer un export
La requête est celle d’une requête historique, sans limit, cursor ni untilCursor. Ici, une journée complète de footprint :
curl -sS https://api.fathomcharts.com/v1/exports \
-H "Authorization: Bearer $FATHOM_CHARTS_API_KEY" \
-H "Content-Type: application/json" \
-d '{"instrument":"NQZ6","indicator":"footprint","params":{"timeframe":60,"groupTicks":4},"from":"1790208000000000000","to":"1790294400000000000"}' \
| zstd -d > footprint-NQZ6-2026-09-24.ndjsonLa réponse est compressée en zstd et curl ne la décompresse pas lui-même, d’où le zstd -d. Pour garder le fichier compressé, remplacez | zstd -d > par -o footprint-NQZ6-2026-09-24.ndjson.zst.
Le coût en compute units de toute la période est décompté dès le début de l’export ; estimez-le avant si besoin. Le volume téléchargé est décompté de votre volume d’export mensuel. Si l’un des deux est épuisé, l’export est refusé avec 429 QUOTA_EXCEEDED.
Format du fichier
Une ligne JSON par mise à jour, dans l’ordre, sous la même forme qu’un résultat de requête historique. Par exemple, la barre de 13:33 UTC à sa clôture :
{"cursor":"20720.124235.0","t":"upsert","id":"1790256780000000000","final":true,"ts":"1790256840033077853","data":{"levels":[[122008,14,3],[122012,44,31],[122016,31,37],[122020,16,32],[122024,48,24],[122028,26,24],[122032,40,28],[122036,24,75],[122040,35,46],[122044,14,32],[122048,12,14],[122052,12,9],[122056,20,9],[122060,36,38],[122064,61,58],[122068,29,68],[122072,28,37],[122076,19,20],[122080,30,46],[122084,32,29],[122088,39,42],[122092,87,67],[122096,154,125],[122100,151,163],[122104,149,192],[122108,66,99],[122112,50,81],[122116,22,38],[122120,27,42],[122124,39,72],[122128,10,25],[122132,0,8]],"poc":122104}}Comme l’historique, le fichier contient aussi les versions intermédiaires (final: false) : filtrez sur final: true si seul le résultat terminé vous intéresse.
Les lignes arrivent dès qu’elles sont calculées. Traitez-les au fil de l’eau plutôt que de charger tout le fichier en mémoire.
Avec le SDK
FathomChartsRest.exportNdjson() décompresse, découpe en lignes et, si la connexion est coupée, reprend automatiquement après la dernière ligne reçue :
import { createWriteStream } from 'node:fs';
import { FathomChartsRest } from '@fathom-charts/sdk';
const rest = new FathomChartsRest({ baseUrl: 'https://api.fathomcharts.com', apiKey: process.env.FATHOM_CHARTS_API_KEY! });
const out = createWriteStream('footprint-NQZ6-2026-09-24.ndjson');
for await (const lines of rest.exportNdjson({
instrument: 'NQZ6',
indicator: 'footprint',
params: { timeframe: 60, groupTicks: 4 },
from: '1790208000000000000',
to: '1790294400000000000',
})) {
if (!out.write(lines)) await new Promise((resolve) => out.once('drain', resolve));
}
out.end();En Node, la décompression demande Node 22.15 ou plus récent. Pour enregistrer le fichier compressé tel quel, sans reprise automatique, utilisez FathomChartsRest.export() : il renvoie le byte stream (body), le nom de fichier proposé (filename) et le token de reprise.
Sans SDK, en Node
import { request } from 'node:https';
import { createZstdDecompress } from 'node:zlib';
import { createInterface } from 'node:readline';
const body = JSON.stringify({
instrument: 'NQZ6',
indicator: 'footprint',
params: { timeframe: 60, groupTicks: 4 },
from: '1790208000000000000',
to: '1790294400000000000',
});
const req = request('https://api.fathomcharts.com/v1/exports', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.FATHOM_CHARTS_API_KEY}`,
'Content-Type': 'application/json',
},
});
req.on('response', async (res) => {
if (res.statusCode !== 200) {
// Les erreurs sont en application/problem+json, non compressées.
let problem = '';
for await (const chunk of res) problem += chunk;
throw new Error(problem);
}
const lines = createInterface({ input: res.pipe(createZstdDecompress()), crlfDelay: Infinity });
for await (const line of lines) {
const m = JSON.parse(line);
if (m.t === 'upsert' && m.final) console.log(m.cursor, m.id);
}
});
req.end(body);Reprendre après une coupure
Un long export peut être interrompu (coupure réseau, maintenance) : vous le voyez à une erreur de lecture ou à un fichier compressé incomplet. Inutile de tout recommencer :
- conservez le header
X-Export-Tokende la réponse (valable 24 heures) ; - notez le
cursorde la dernière ligne complète reçue, et ignorez une éventuelle ligne tronquée à la fin ; - renvoyez exactement la même requête, en ajoutant
resume:
{
"instrument": "NQZ6",
"indicator": "footprint",
"params": { "timeframe": 60, "groupTicks": 4 },
"from": "1790208000000000000",
"to": "1790294400000000000",
"resume": { "token": "<valeur de X-Export-Token>", "after": "20720.124235.0" }
}L’export reprend juste après cette ligne. La nouvelle réponse est un nouveau fichier compressé : décompressez chaque réponse séparément, puis mettez les lignes bout à bout. Le résultat est identique à celui d’un export sans coupure. Une reprise ne consomme aucune unité de calcul supplémentaire ; seuls les octets téléchargés sont décomptés de votre volume d’export.