Node.js 18 and later ship with fetch, so pushing data to an Output Charts dashboard needs no packages at all. This guide writes a small push() function, sends a number and a bar chart, and handles the two errors you are likely to meet.
Before you start
You need a Solo plan (API push is part of Solo), a dashboard with a KPI and a bar chart, and an API key from Account → API keys. The key starts with oc_live_ and is shown once.
For each visual, open the editor, select it, and choose Copy push URL under Push from code. Keep the key and the URLs in environment variables:
export OC_KEY="oc_live_..."
export OC_TOTAL_URL="https://api.outputcharts.com/visuals/<kpi-visual-id>/data"
export OC_PLANS_URL="https://api.outputcharts.com/visuals/<bar-visual-id>/data"
The push function
// push.mjs
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
export async function push(url, document, { retry = true } = {}) {
const response = await fetch(url, {
method: "PUT",
headers: {
Authorization: `Bearer ${process.env.OC_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify(document),
});
const body = await response.json().catch(() => ({}));
// Each key gets 60 pushes per clock minute. Wait for the next minute and try once more.
if (response.status === 429 && retry) {
await sleep(60_000 - (Date.now() % 60_000));
return push(url, document, { retry: false });
}
if (!response.ok) {
const details = body.details ? ` ${JSON.stringify(body.details)}` : "";
throw new Error(`Push failed with ${response.status} ${body.code ?? ""}${details}`);
}
return body; // { visualId, updatedAt }
}
The error carries the API's own code, and for a rejected document the details list, which names each field that is wrong.
Push a number
A KPI takes a label, a number, and a format. delta and deltaLabel are optional and show the change beside it:
import { push } from "./push.mjs";
const ordersToday = 214; // from your database, queue or API
const ordersYesterday = 187;
await push(process.env.OC_TOTAL_URL, {
label: "Orders today",
value: ordersToday,
format: "number",
delta: ordersToday - ordersYesterday,
deltaFormat: "number",
deltaLabel: "vs yesterday",
});
Push a chart
A bar chart takes the key for the x axis, the series to draw, and one row per bar. Each row has exactly the x key and the series keys:
const ordersByPlan = [
{ plan: "Free", orders: 1260 },
{ plan: "Solo", orders: 418 },
{ plan: "Team", orders: 96 },
];
await push(process.env.OC_PLANS_URL, {
orientation: "vertical",
mode: "grouped",
xKey: "plan",
series: [{ key: "orders", label: "Orders" }],
data: ordersByPlan,
});
Run it with node --env-file=.env report.mjs (Node 20.6 and later read the file for you), or export the variables first.
Open copies of the dashboard check for new data every 15 seconds, so both visuals update on every screen soon after the script finishes.
Errors worth knowing
422 invalid_document: the document does not fit the visual's type. A common cause is a number that arrives as a string, such as acount(*)from Postgres, which many drivers return as text. Wrap it inNumber()before you push.429 rate_limited: more than 60 pushes in a minute from one key. The function above waits for the next minute once; if you see it often, push once per visual per run instead of in a loop.401 invalid_api_key: the key is wrong or revoked. A brand-new key can also need a few seconds.403 requires_solo: the account is on Free. Keys pause and work again after an upgrade.
Each push replaces the whole document, so send every row each time. Documents can be up to 100 KB.
Where to run it
The same function works wherever Node runs: a cron job on a server, a scheduled cloud function such as AWS Lambda with an EventBridge schedule, a step at the end of a CI pipeline, or inside your app right after the event you want to count. For a schedule without a server, see Schedule dashboard updates with cron or GitHub Actions.
To try document shapes before writing code, paste them into JSON to chart. Create a free account to build the dashboard; Solo turns on API keys.