Recipes Last updated

Export to a spreadsheet

Page through every item and write a CSV that opens in Excel or Google Sheets. A read key is enough.

If you only need a file now and then, Exports → Export items in the app (press ⌘K and type “export”) does this without code. Use the API when a script should build the sheet on a schedule, or feed it into something else. A read only key is enough.

1. Page through the items

GET /items returns up to 200 items a page. Ask for the next page until you’ve seen total:

curl "https://api.itemtally.com/v1/items?page=1&pageSize=200" \
  -H "Authorization: Bearer $ITEMTALLY_KEY"
{
  "items": [
    { "name": "Aputure LED panel", "qr": "IT-000005", "mode": "unique", "quantity": null, "folderPath": "Studio / Shelf A", "currentHolder": null, "value": 545 },
    { "name": "Cable ties 300 mm bag of 100", "qr": "IT-000009", "mode": "quantity", "quantity": 4, "minQuantity": 5, "folderPath": "Van 1", "value": 3.2 }
  ],
  "page": 1,
  "pageSize": 200,
  "total": 17,
  "totals": { "count": 17, "lowCount": 1, "checkedOutCount": 3, "totalValue": 6218.5 }
}

Narrow it with the same filters as the app: folderId, tag, mode, flag=low or q.

2. Write the CSV

import { writeFileSync } from "node:fs";

const API = "https://api.itemtally.com/v1";
const headers = { Authorization: `Bearer ${process.env.ITEMTALLY_KEY}` };

const rows = [];
for (let page = 1; ; page++) {
  const res = await fetch(`${API}/items?page=${page}&pageSize=200`, { headers });
  const body = await res.json();
  if (!res.ok) throw new Error(`${res.status} ${body.message}`);
  rows.push(...body.items);
  if (rows.length >= body.total || body.items.length === 0) break;
}

const cell = (v) => {
  const s = v == null ? "" : String(v);
  return /[",\n]/.test(s) ? `"${s.replaceAll('"', '""')}"` : s;
};
const columns = ["Code", "Name", "Type", "Quantity", "Min", "Place", "With", "Due back", "Value"];
const lines = rows.map((i) =>
  [
    i.qr,
    i.name,
    i.mode,
    i.quantity,
    i.minQuantity,
    i.folderPath ?? "Unfiled",
    i.currentHolder?.name,
    i.currentDueAt?.slice(0, 10),
    i.value,
  ]
    .map(cell)
    .join(",")
);
writeFileSync("inventory.csv", [columns.join(","), ...lines].join("\n"));
console.log(`Wrote ${rows.length} items to inventory.csv`);

Open inventory.csv in Excel, or use File → Import in Google Sheets.

Variations

  • The logbook instead of the register. Page through GET /movements the same way for every stock change and hand-over, newest first. Filter with itemId, personId, folderId or type.
  • Only what’s out. GET /movements/out-now returns everything a person holds right now, soonest due first, in one call.
  • Keep the sheet fresh. Run the script on a schedule, or update rows as webhooks arrive.

Stuck on an integration? Email [email protected] with the request you sent and the response you got. The person who built the API answers, usually within a business day.