{"openapi":"3.1.0","info":{"title":"Limzo Public Data API","version":"1.0.0","summary":"Read-only JSON for public Telegram group stats pages.","description":"Every public Limzo group profile is available as machine-readable JSON — the same aggregate data the human page shows, built for agents, dashboards, and integrations.\n\n- **No authentication, no key.** All endpoints are public and read-only.\n- **CORS:** every endpoint sends `Access-Control-Allow-Origin: *`.\n- **Caching:** responses send `Cache-Control: public, max-age=1800`; stats move slowly, so respect the cache instead of polling.\n- **Rate limit:** 5 requests/second with a burst of 20 per client IP; excess requests get HTTP 429.\n- **Privacy:** payloads never include verbatim member messages. Private groups (no public t.me address) expose aggregate metrics and the member leaderboard only.\n- **Versioning:** the payload carries `schema: \"limzo.public_stats/v1\"`. Fields may be added without notice; breaking changes bump the schema version.\n\nTo find groups to query, use the public directory (https://limzo.com/groups/) or the group sitemap (https://limzo.com/sitemap-groups.xml). Human-readable docs: https://limzo.com/docs/","contact":{"name":"Limzo","url":"https://limzo.com/contact/","email":"info@limzo.com"},"termsOfService":"https://limzo.com/terms/"},"externalDocs":{"description":"Developer documentation","url":"https://limzo.com/docs/"},"servers":[{"url":"https://limzo.com"}],"paths":{"/s/{slug}.json":{"get":{"operationId":"getGroupStats","summary":"Public stats for one group","description":"Mirrors the human page URL: take any public profile like /s/hipo and append .json.","parameters":[{"name":"slug","in":"path","required":true,"description":"The group’s public slug — the last part of its limzo.com/s/<slug> URL.","schema":{"type":"string"},"example":"hipo"},{"name":"range","in":"query","required":false,"description":"Stats window. 30d and all require the group’s Pro/Community plan; on free groups the response silently falls back to 7d (check `range.key` in the payload).","schema":{"type":"string","enum":["7d","30d","all"],"default":"7d"}}],"responses":{"200":{"description":"Curated public stats for the group.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicGroupStats"}}}},"403":{"description":"The group exists but its public page is disabled or not public.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No public group with this slug.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (more than 5 requests/second sustained from one IP). Slow down and retry."}}}},"/api/public/{slug}":{"get":{"operationId":"getGroupStatsAlias","summary":"Alias of /s/{slug}.json","description":"Same payload and parameters as /s/{slug}.json.","parameters":[{"name":"slug","in":"path","required":true,"description":"The group’s public slug — the last part of its limzo.com/s/<slug> URL.","schema":{"type":"string"},"example":"hipo"},{"name":"range","in":"query","required":false,"description":"Stats window. 30d and all require the group’s Pro/Community plan; on free groups the response silently falls back to 7d (check `range.key` in the payload).","schema":{"type":"string","enum":["7d","30d","all"],"default":"7d"}}],"responses":{"200":{"description":"Curated public stats for the group.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicGroupStats"}}}},"403":{"description":"The group exists but its public page is disabled or not public.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No public group with this slug.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (more than 5 requests/second sustained from one IP). Slow down and retry."}}}},"/api/public/global-stats":{"get":{"operationId":"getGlobalStats","summary":"Network-wide totals across all public groups","responses":{"200":{"description":"Totals over the public directory plus the current top groups.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GlobalStats"}}}},"429":{"description":"Rate limited (more than 5 requests/second sustained from one IP). Slow down and retry."}}}}},"components":{"schemas":{"PublicGroupStats":{"type":"object","description":"Curated, versioned public stats payload. Never includes verbatim member messages.","required":["ok","schema","generated_at","group","range","stats"],"properties":{"ok":{"type":"boolean","const":true},"schema":{"type":"string","const":"limzo.public_stats/v1"},"generated_at":{"type":"string","format":"date-time"},"docs":{"type":"string","format":"uri","description":"Human-readable API documentation."},"openapi":{"type":"string","format":"uri","description":"This OpenAPI document."},"group":{"type":"object","properties":{"slug":{"type":"string"},"title":{"type":["string","null"]},"username":{"type":["string","null"],"description":"Telegram username without @, when the group is public on t.me."},"is_private":{"type":"boolean","description":"True when the group has no public t.me address. Private groups expose aggregates and the leaderboard only."},"plan":{"type":"string","enum":["free","pro","community"]},"member_count":{"type":["integer","null"]},"description":{"type":["string","null"]},"url":{"type":"string","format":"uri","description":"The human stats page for this group."},"telegram_url":{"type":["string","null"],"format":"uri"}}},"range":{"type":"object","description":"The range actually served — compare `key` with what you requested to detect the plan fallback.","properties":{"key":{"type":"string","enum":["7d","30d","all"]},"label":{"type":"string"},"days":{"type":"integer","description":"Length of the window in days."}}},"stats":{"type":"object","properties":{"messages":{"type":"integer","description":"Messages in the selected range."},"replies":{"type":"integer","description":"Replies in the selected range."},"stickers":{"type":"integer","description":"Stickers in the selected range."},"active_users":{"type":"integer","description":"Unique posting members in the selected range."},"messages_today":{"type":"integer","description":"Messages so far today (UTC)."},"active_users_today":{"type":"integer","description":"Unique posting members so far today (UTC)."},"lifetime_messages":{"type":"integer","description":"Messages since Limzo started tracking the group."},"tracked_since_days":{"type":"integer","description":"Days since tracking started."},"new_active_members":{"type":"integer","description":"Members who posted for the first time in the selected range."},"peak_hour_utc":{"type":["integer","null"],"minimum":0,"maximum":23,"description":"Busiest hour of day, UTC."},"mood":{"type":["object","null"],"description":"Group mood derived from reactions. Null when there is not enough data.","properties":{"label":{"type":"string"},"emoji":{"type":"string"},"positive_pct":{"type":"number","description":"Share of positive reactions, 0–100."}}},"daily":{"type":"array","description":"Per-day series over the selected range.","items":{"type":"object","properties":{"day":{"type":"string","format":"date"},"messages":{"type":"integer"},"replies":{"type":"integer"},"active_users":{"type":"integer"}}}},"top_members":{"type":"array","description":"Member leaderboard for the selected range.","items":{"type":"object","properties":{"rank":{"type":"integer","description":"1-based position."},"name":{"type":"string"},"username":{"type":["string","null"]},"messages":{"type":"integer"},"replies":{"type":"integer"}}}},"top_reactor":{"type":["object","null"],"description":"A member singled out by reaction activity. Null when the range has no reaction data.","properties":{"name":{"type":"string","description":"Display name as shown on the public page."},"username":{"type":["string","null"],"description":"Telegram username without @, when public."},"count":{"type":"integer","description":"Reactions in the selected range."}}},"reaction_magnet":{"type":["object","null"],"description":"The member whose messages attracted the most reactions. Null when the range has no reaction data.","properties":{"name":{"type":"string","description":"Display name as shown on the public page."},"username":{"type":["string","null"],"description":"Telegram username without @, when public."},"count":{"type":"integer","description":"Reactions in the selected range."}}}}}}},"GlobalStats":{"type":"object","description":"Network-wide totals across all listed public groups.","required":["ok","stats"],"properties":{"ok":{"type":"boolean","const":true},"docs":{"type":"string","format":"uri","description":"Human-readable API documentation."},"openapi":{"type":"string","format":"uri","description":"This OpenAPI document."},"stats":{"type":"object","properties":{"public_groups":{"type":"integer","description":"Public groups in the directory."},"messages_7d":{"type":"integer"},"messages_all":{"type":"integer"},"active_users_7d":{"type":"integer"},"replies_7d":{"type":"integer"},"reactions_7d":{"type":"integer"},"karma_7d":{"type":"integer"}}},"top_groups":{"type":"array","description":"Current top public groups by Limzo Score.","items":{"type":"object","properties":{"title":{"type":"string"},"slug":{"type":"string"},"plan":{"type":"string","enum":["free","pro","community"]},"score":{"type":"number"},"messages_7d":{"type":"integer"},"active_users_7d":{"type":"integer"}}}}}},"Error":{"type":"object","properties":{"ok":{"type":"boolean","const":false},"error":{"type":"string","description":"Human-readable reason."}},"required":["ok","error"]}}}}