Chart pictures (PNG)
Strata charts as ready-made PNG pictures for bots - liquidation levels, the liquidation heatmap and the position heatmaps - with a Discord example.
GET /v1/charts/<kind>.png answers a PNG picture (1200 x 675, dark by default) of a Strata chart, ready for a bot to post. It needs the charts:read scope. The picture is drawn from exactly the data the matching /v1/strata route returns for the same query, and carries the coin, the chart, the window, the time of the data, the price axis, the current price and the attribution lines.
| kind | drawn from | weight (7d / 30d) |
|---|---|---|
liquidation-levels | /v1/strata/levels: the modelled liquidation book per price, longs below the price, shorts above | 10 / 10 |
liquidation-heatmap | /v1/strata/heatmap: the modelled liquidation field over the window, with the price path | 15 / 20 |
positions-heatmap | /v1/strata/netpos: open interest parked by price (new positions park at the price they opened at, closes shrink it) | 15 / 20 |
net-longs-heatmap | /v1/strata/netpos: the long share | 15 / 20 |
net-shorts-heatmap | /v1/strata/netpos: the short share | 15 / 20 |
long-short-delta-heatmap | /v1/strata/netpos: longs minus shorts per price and time | 15 / 20 |
Parameters: coin (BTC; or symbol=BTCUSDT), venue (default every venue of the model), window (7d; 30d from Developer), theme (dark or light).
- The three split pictures need the long/short split some exchanges publish. Where the coin's venues (or the pinned venue) publish none, the answer is
404 split_unavailable, never an invented split: usepositions-heatmapthere. - Sandbox keys get a picture exactly where the sandbox has a recorded answer for the matching
/v1/strataroute, and the same404 sandbox_not_recordedwhere it does not. - Errors are the usual JSON error body. A picture is drawn at most once per 45 seconds for the same kind, coin, venue, window and theme:
Cache-Control: private, max-age=<seconds left>tells you how long it stays the same, and a repeated call still costs its weight. Under load the answer is503 render_busyorrender_timeoutwithRetry-After. - Attribution: the picture prints it, and the
X-Attributionheader carries the same lines (joined by|). Keep it with the picture when you post it.
#A Discord bot posting a picture
The key comes from the bot's environment (CHARTDECK_API_KEY), never from code or a URL. Node 18+ with discord.js:
import { Client, GatewayIntentBits, AttachmentBuilder } from 'discord.js';
const client = new Client({ intents: [GatewayIntentBits.Guilds] });
async function chartPicture(kind, coin) {
const url = `https://api.chartdeck.trade/v1/charts/${kind}.png?coin=${encodeURIComponent(coin)}`;
const res = await fetch(url, { headers: { Authorization: `Bearer ${process.env.CHARTDECK_API_KEY}` } });
if (!res.ok) {
const { error } = await res.json(); // errors are JSON: { error: { code, message } }
throw new Error(`${error.code}: ${error.message}`);
}
return { png: Buffer.from(await res.arrayBuffer()), attribution: res.headers.get('x-attribution') ?? '' };
}
client.on('interactionCreate', async (i) => {
if (!i.isChatInputCommand() || i.commandName !== 'heatmap') return;
await i.deferReply();
try {
const { png, attribution } = await chartPicture('liquidation-heatmap', i.options.getString('coin') ?? 'BTC');
await i.editReply({ content: attribution.replaceAll(' | ', ' ยท '), files: [new AttachmentBuilder(png, { name: 'heatmap.png' })] });
} catch (e) {
await i.editReply(`No picture: ${e.message}`);
}
});
client.login(process.env.DISCORD_TOKEN);With the SDKs: await cd.charts.picture('liquidation-heatmap', { coin: 'BTC' }) (JavaScript) and client.charts.picture('liquidation-heatmap', coin='BTC') (Python) return the image bytes as data, and the attribution lines in meta.attribution.