BIO.RE
Creator Analytics

Creator Geo Distribution

Top 20 visitor countries for the authenticated creator's bio page. Country names from GeoIP at session-create; sessions with unknown country fall under "Unknown".

GET /api/v1/creators/analytics/geo โ€” ๐Ÿ”‘ Bearer ยท Rate limit: 60 req / minute

Top 20 countries by session count for the creator's bio page. The country value was set at session-create time from a GeoIP lookup on the request IP (the IP itself is never stored โ€” see Create Analytics Session).

"Unknown" for sessions with no country. GeoIP can fail (private IPs in dev, blocked geo db, etc.) โ€” those sessions have country = null, and the SQL COALESCE(country, 'Unknown') collapses them into a single "Unknown" bucket. There's no separate null-vs-empty distinction in the response.

Top 20 only, percentage rounded to whole numbers. Same ranking + rounding contract as traffic sources โ€” LIMIT 20, ORDER BY visits DESC, percentage = Math.round((visits / total) * 100).

Request

Query parameters

ParamTypeDefaultNotes
daysinteger30Lookback window. Capped at 365.

Headers

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

Response

200 OK โ€” ApiResponseOf<CreatorGeoDto>

{
  "success": true,
  "data": {
    "locations": [
      { "country": "Turkey",  "visits": 400, "percentage": 22 },
      { "country": "United States", "visits": 250, "percentage": 14 },
      { "country": "Unknown", "visits": 50,  "percentage": 3 }
    ]
  }
}

Item fields (locations[])

FieldTypeNotes
countrystringCountry name from GeoIP (e.g. "Turkey", "United States") or "Unknown".
visitsnumberSession count.
percentagenumberWhole-number percentage of total.

Errors

Same as overview.

Side effects

  1. JwtAuthGuard + ThrottleGuard.
  2. getBioPageId(userId).
  3. analyticsDb.$queryRaw:
    SELECT COALESCE(country, 'Unknown') as country,
           COUNT(*)::int as visits
    FROM "AnalyticsSession"
    WHERE "bioPageId" = $bioPageId::uuid
      AND "startedAt" >= $since
    GROUP BY country
    ORDER BY visits DESC
    LIMIT 20
  4. Compute percentage per row; return.

Code samples

curl 'https://api.bio.re/api/v1/creators/analytics/geo?days=30' \
  -H "Authorization: Bearer $ACCESS_TOKEN"
type GeoLocation = { country: string; visits: number; percentage: number };

async function getGeo(accessToken: string, days = 30): Promise<{ locations: GeoLocation[] }> {
  const res = await fetch(
    `https://api.bio.re/api/v1/creators/analytics/geo?days=${days}`,
    { headers: { Authorization: `Bearer ${accessToken}` } },
  );
  const json = await res.json();
  if (!res.ok || !json.success) throw new Error(json?.error?.message ?? 'Failed');
  return json.data;
}

Try it

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

In: header

Query Parameters

days?string

Response Body

application/json

application/json

application/json

curl -X GET "https://loading/api/v1/creators/analytics/geo"
{
  "success": true,
  "data": {
    "locations": [
      {
        "country": "Turkey",
        "visits": 400,
        "percentage": 22.2
      }
    ]
  }
}
{
  "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.ts82โ€“98 (geo)
DTO (response)apps/api-core/src/modules/analytics/dto/analytics-client-response.dto.ts109โ€“112 (CreatorGeoDto), 96โ€“105 (item shape)
Prisma modelpackages/prisma-analytics/prisma/schema.prismaAnalyticsSession.country (line 67), index @@index([country, startedAt]) (89)

On this page