BIO.RE
Creator Analytics

Export Creator Analytics (CSV)

Download a CSV of the overview, traffic, or links endpoint. CSV-injection-safe via formula-character escaping. Returns text/csv with attachment Content-Disposition.

GET /api/v1/creators/analytics/export โ€” ๐Ÿ”‘ Bearer ยท Rate limit: 60 req / minute ยท Content-Type: text/csv

Returns a CSV file derived from one of three other endpoints โ€” pick via ?type=overview / ?type=traffic / ?type=links. The body is the raw CSV (not JSON-wrapped) and the response carries Content-Disposition: attachment; filename=biore-analytics-<type>-<YYYY-MM-DD>.csv so a browser will download it.

type is one of three. Only overview, traffic, links are supported. Anything else returns a CSV body of error\nInvalid export type. Use: overview, traffic, or links. (still 200, still text/csv). Validate client-side before calling.

Internally re-uses the same JSON endpoints. The export handler calls this.overview(userId, days), this.traffic(userId, days), or this.links(userId, days) and serializes the result. So the data is the same as the JSON endpoints โ€” only the wire format and the field flattening differ.

CSV injection is mitigated server-side. Every value runs through sanitizeCSVValue: leading =, +, -, @, \t, \r get prefixed with ' (single quote) so spreadsheet apps don't parse the cell as a formula. Double quotes are escaped (""); cells containing commas, quotes, or newlines are wrapped in double quotes. Do not double-sanitize on the client.

Filename has the current UTC date, not the window range. The suffix is new Date().toISOString().slice(0, 10) โ€” today's date, not "from-X-to-Y." If you save several exports the same day they'll collide; rename client-side if you need to disambiguate.

Request

Query parameters

ParamTypeRequiredNotes
typeenumโœ“overview / traffic / links. Anything else โ†’ error CSV body.
daysintegeroptional (default 30)Lookback window. Capped at 365.

Headers

HeaderRequiredNotes
Authorization: Bearer <accessToken>โœ“JwtAuthGuard.

Response

200 OK โ€” text/csv

Important: this endpoint does NOT return the JSON envelope. The body is raw CSV bytes. Use res.text() (not res.json()).

HeaderValue
Content-Typetext/csv
Content-Dispositionattachment; filename=biore-analytics-<type>-<YYYY-MM-DD>.csv

CSV shapes

?type=overview โ€” 5 rows, two columns:

metric,value
total_views,4200
unique_visitors,1800
total_clicks,320
ctr,7.62
days,30

?type=traffic โ€” header + one row per traffic source:

source,visits,percentage
instagram.com,600,35
direct,280,16
youtube.com,150,9

?type=links โ€” header + one row per link with clicks:

id,title,url,clicks,ctr
link-uuid-1,My Portfolio,https://example.com,120,4.8
link-uuid-deleted,Unknown,,6,0.24

Invalid type โ€” single error row, still 200 OK:

error
Invalid export type. Use: overview, traffic, or links.

Errors

HTTPReason
401Missing / invalid bearer token.
404error.creator.not_found / error.creator.bio_not_found (raised inside the underlying overview/traffic/links calls).
429Throttle.

There is no 400 for invalid type โ€” instead the body becomes the error CSV described above.

Side effects

  1. JwtAuthGuard + ThrottleGuard.
  2. Compute d = Math.min(parseInt(days ?? '30'), 365).
  3. Switch on type:
    • overview โ†’ call this.overview(userId, String(d)) โ†’ 5-row metric/value CSV.
    • traffic โ†’ call this.traffic(userId, String(d)) โ†’ header + per-source rows.
    • links โ†’ call this.links(userId, String(d)) โ†’ header + per-link rows.
    • Anything else โ†’ static error\nInvalid export type. ...\n body.
  4. Run every value through sanitizeCSVValue (formula-prefix, quote-escape, comma/newline-wrap).
  5. res.setHeader('Content-Disposition', ...); res.send(csv) (raw text body).

Code samples

# Saves the file using the server-suggested filename
curl -OJ \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  'https://api.bio.re/api/v1/creators/analytics/export?type=overview&days=30'
async function exportCsv(
  accessToken: string,
  type: 'overview' | 'traffic' | 'links',
  days = 30,
): Promise<{ csv: string; filename: string }> {
  const res = await fetch(
    `https://api.bio.re/api/v1/creators/analytics/export?type=${type}&days=${days}`,
    { headers: { Authorization: `Bearer ${accessToken}` } },
  );
  if (!res.ok) throw new Error(`Export failed: ${res.status}`);

  const csv = await res.text(); // NOT .json() โ€” body is raw CSV
  const dispo = res.headers.get('Content-Disposition') ?? '';
  const match = /filename=([^;]+)/.exec(dispo);
  const filename = match?.[1]?.trim() ?? `biore-analytics-${type}.csv`;
  return { csv, filename };
}
async function downloadExport(
  type: 'overview' | 'traffic' | 'links',
  days = 30,
) {
  const res = await fetch(
    `/api/v1/creators/analytics/export?type=${type}&days=${days}`,
  );
  if (!res.ok) return;

  const blob = await res.blob();
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = `biore-analytics-${type}-${new Date().toISOString().slice(0, 10)}.csv`;
  a.click();
  URL.revokeObjectURL(url);
}

Try it

GET
/api/v1/creators/analytics/export
AuthorizationBearer <token>

In: header

Query Parameters

type*string
Value in"overview" | "traffic" | "links"
days?number

Response Body

text/csv

text/csv

curl -X GET "https://loading/api/v1/creators/analytics/export?type=overview"
Empty
{
  "success": false,
  "error": {
    "code": "AUTH_UNAUTHORIZED",
    "message": "Invalid credentials",
    "i18nKey": "auth.login.invalid_credentials",
    "i18nVars": {
      "field": "email"
    },
    "details": [
      {
        "message": "email must be an email"
      }
    ],
    "correlationId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
  }
}
{
  "success": false,
  "error": {
    "code": "AUTH_UNAUTHORIZED",
    "message": "Invalid credentials",
    "i18nKey": "auth.login.invalid_credentials",
    "i18nVars": {
      "field": "email"
    },
    "details": [
      {
        "message": "email must be an email"
      }
    ],
    "correlationId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
  }
}

Source

SourcePathLines
Controllerapps/api-core/src/modules/analytics/creator-analytics.controller.ts206โ€“246 (exportAnalytics โ€” type switch, sanitization, attachment header)
CSV sanitizerapps/api-core/src/common/utils/sanitize-csv.ts6โ€“25 (sanitizeCSVValue โ€” formula prefix ', double-quote escaping, comma/newline wrap)
Re-used handlersapps/api-core/src/modules/analytics/creator-analytics.controller.ts45โ€“62 (overview), 64โ€“80 (traffic), 165โ€“204 (links)

On this page