Automate client reporting
Pull the same numbers for every client website with one credential, into a spreadsheet, a BI tool, or a scheduled agent.
An agency key can cover every website in the workspace, which means one credential and one loop instead of a login per client. This guide builds the monthly numbers for all of them.
1. Find out what the key covers
curl -s "https://app.sourceloop.ai/api/v1/websites" \
-H "Authorization: Bearer $SOURCELOOP_API_KEY"
Do not hardcode the list. Reading it each run means a newly onboarded client appears in the report without anyone editing a script, and a departed one drops out.
2. Ask for the same numbers per website
const websites = await get("/v1/websites");
const rows = [];
for (const site of websites.websites) {
const m = await get("/v1/metrics", {
website: site.domain,
period: "last month",
metrics: "conversions,revenue,ad_spend,cost_per_lead",
group_by: "channel",
});
rows.push({ client: site.domain, ...m });
}
Two things worth doing here rather than later:
Use period rather than computing dates. period=last month resolves in the workspace’s own timezone, which is what makes a client’s month match what they see in the app. Computing from and to in your server’s timezone is how a report ends up one day off for exactly the clients in another region.
Read meta.window back and print it on the report. It is the range the API actually used. A report that states its own window is one you can defend in a client meeting.
3. Pace the loop
Rate limits are counted per workspace, so a hundred clients in a tight loop will hit the limit even though each client is small. Watch RateLimit-Remaining and pause when it gets low, rather than firing everything and handling 429s afterwards.
if (Number(res.headers.get("ratelimit-remaining")) < 5) {
await sleep(Number(res.headers.get("ratelimit-reset")) * 1000);
}
Analytics reads are also capped at four concurrent queries per workspace, so a sequential loop is genuinely fine here. Parallelising to 50 will not make the report arrive sooner.
4. Land it somewhere
A spreadsheet is usually the right answer, because it is where the client conversation already happens. Write the rows with the Sheets API from the same script, one row per client per month, and let the sheet own the formatting.
A BI tool wants the same data appended to a table rather than overwritten, so the history survives. Key on client plus period so a re-run replaces a month instead of duplicating it.
A scheduled agent can skip the report entirely and answer questions instead. If that is where you are heading, the MCP server is a better fit than this API: it exposes the same data as tools an assistant can call, so “which clients got worse this month and why” is one question rather than a script.
Which numbers to put in front of a client
Start with conversions, revenue, spend and cost per lead, broken down by channel. Then add the one thing most reports lack: attribution coverage, the share of converted traffic that carried a usable source.
Coverage is what makes the rest trustworthy. A cost per lead computed over 60% coverage is a different claim from one computed over 95%, and stating it up front is far better than having a client discover the gap later.
Keys and access
Give the reporting job its own key with metrics:read only, and leave pii:read off. A monthly summary needs no email addresses, and a key that cannot read them cannot leak them. See Authentication » and Data provenance and PII ».