Export Instagram Story Insights to CSV Every Day
The Graph API gives story insights only while a story is live. This guide shows a small script that saves the numbers of each story to a CSV file. It also shows how to run the script on a schedule and open the file in Google Sheets.
Why you must save the numbers
A story is live for 24 hours. The Graph API gives the insights of a story only during that time. After the story expires, you cannot ask Meta for its numbers again.
The numbers also grow while the story is live. A good export reads each story more than once and keeps the last reading. The script in this guide does that: it writes one row for each story, and it replaces the row when it reads the story again.
Before you start, get an access token and the Instagram account id. For the steps, read Get an Instagram Graph API access token.
Try it first from the command line
The free @storylinkpro/instagram-stories-api package has a CLI. With --json, it prints JSON that other tools can read. These commands need no install:
export INSTAGRAM_ACCESS_TOKEN=…
export INSTAGRAM_USER_ID=…
npx @storylinkpro/instagram-stories-api stories --json
npx @storylinkpro/instagram-stories-api insights 18012345678901234 --jsonThe first command lists the live stories. The second command prints the views, reach, replies and shares of one story. A number that Meta does not give is null, not 0. Meta keeps back some numbers when fewer than 5 people saw the story.
The export script
Make a folder, install the client, and save this file as export-stories.mjs:
npm init -y
npm install @storylinkpro/instagram-stories-apiimport { existsSync, readFileSync, writeFileSync } from "node:fs";
import { createClient } from "@storylinkpro/instagram-stories-api";
const FILE = "story-insights.csv";
const COLUMNS = ["id", "posted", "type", "views", "reach", "replies", "shares", "read_at", "permalink"];
const ig = createClient({
accessToken: process.env.INSTAGRAM_ACCESS_TOKEN,
igUserId: process.env.INSTAGRAM_USER_ID,
});
const cell = (v) => (v === null || v === undefined ? "" : `"${String(v).replaceAll('"', '""')}"`);
const unquote = (s) => s.replace(/^"|"$/g, "").replaceAll('""', '"');
// Read the rows that the last runs saved, by story id. No field has a comma.
const rows = new Map();
if (existsSync(FILE)) {
for (const line of readFileSync(FILE, "utf8").trim().split("\n").slice(1)) {
const values = line.split(",").map(unquote);
rows.set(values[0], line);
}
}
// Read every live story, one after the other, and replace its row.
const readAt = new Date().toISOString();
for (const story of await ig.stories()) {
const n = await ig.storyInsights(story.id);
const values = [story.id, story.timestamp, story.media_type, n.views, n.reach, n.replies, n.shares, readAt, story.permalink];
rows.set(story.id, values.map(cell).join(","));
}
const sorted = [...rows.values()].sort();
writeFileSync(FILE, [COLUMNS.join(","), ...sorted].join("\n") + "\n");
console.log(`Saved ${rows.size} stories to ${FILE}`);Run it once to test it:
node export-stories.mjsThe rows stay in the order of the story id. A story that expired keeps its last row, because the script never removes a row.
Run it on a schedule
Run the script at least every 12 hours. Then each story gets at least one reading before it expires. To get readings that are nearer to the final numbers, run it more often, for example every 6 hours.
On macOS or Linux, use cron. Put the token and the account id in a file that only you can read, for example .env, and open the crontab with crontab -e:
0 */6 * * * cd /path/to/export && node --env-file=.env export-stories.mjs >> export.log 2>&1The --env-file option needs Node.js 20.6 or later. The computer must be on when the job runs. A small server or a scheduled cloud job also works. Each run makes one call for the story list and one call for each live story, so the job stays far below Meta's limits. For the limits, read Instagram Graph API rate limits explained.
Open the file in Google Sheets
- Open a new spreadsheet in Google Sheets.
- Select File, then Import, then Upload, and select
story-insights.csv. - Select Replace current sheet, and then Import data.
Do the import again when you want the new rows. In Microsoft Excel and in Apple Numbers, you can open the CSV file directly.
Some useful columns to add in the spreadsheet: reach divided by views, which shows repeat views, and the hour of posted, which helps you compare publish times.
How Storylink does it
Storylink saves the insights of each story while the story is live, so you do not need a script or a schedule. It reads every slide during the 24 hours, and it keeps the numbers after the story expires.
On the Pro and Business plans, the Analytics page exports your results to Excel. To read the saved numbers from code, the Storylink API has GET /v1/stories/{id}/insights. It works after the story expires.
To learn what each number means, read Instagram Story insights explained.
Put this into practice with Storylink
Free forever to start. No card required.