Skip to content

API Reference

Overview

The platform exposes two independent APIs:

APIBase PathAuthConsumers
Public API/api/v1Anonymous (fingerprint/session)Player-facing frontend
Admin API/api/adminJWT / session tokenAdmin dashboard

All endpoints return JSON. Errors use standard HTTP status codes with a { "error": "message" } body.


Public API

Games

GET /api/v1/games

List games for the Home page, sorted by rank.

Query Parameters

ParamTypeDefaultDescription
pagenumber1Page number
limitnumber40Items per page (max 100)
platformstringauto-detecteddesktop or mobile

Response

{
"games": [
{
"id": "abc123",
"title": "Game Title",
"slug": "game-title",
"thumbnailUrl": "https://...",
"authorName": "Studio Name",
"rank": 1,
"tags": ["new", "trendy"],
"isExploration": false
}
],
"pagination": {
"page": 1,
"limit": 40,
"total": 1200,
"hasMore": true
}
}

Notes:

  • Response includes both Ranked and Exploration games (90/10 split handled server-side)
  • Exploration games are interspersed at fixed intervals

GET /api/v1/games/:slug

Get full game details and player page data.

Response

{
"game": {
"id": "abc123",
"title": "Game Title",
"slug": "game-title",
"description": "Full description...",
"instructions": "How to play...",
"thumbnailUrl": "https://...",
"iframeUrl": "https://...",
"authorId": "author456",
"authorName": "Studio Name",
"authorSlug": "studio-name",
"categories": [
{ "id": "cat1", "name": "Action", "slug": "action" }
],
"tags": ["new"],
"rating": { "score": 42, "likes": 58, "dislikes": 16 },
"rank": 15
},
"recommendations": {
"side": [
{ "id": "...", "title": "...", "slug": "...", "thumbnailUrl": "..." }
],
"bottom": [
{ "id": "...", "title": "...", "slug": "...", "thumbnailUrl": "..." }
],
"categories": [
{ "id": "...", "name": "Puzzle", "slug": "puzzle", "iconUrl": "..." }
]
},
"authorGames": [
{ "id": "...", "title": "...", "slug": "...", "thumbnailUrl": "..." }
]
}

Events triggered: game_page_view (server-side)


Categories

GET /api/v1/categories

List all categories.

Response

{
"categories": [
{
"id": "cat1",
"name": "Action",
"slug": "action",
"iconUrl": "https://...",
"gameCount": 245
}
]
}

GET /api/v1/categories/:slug/games

List games in a category, sorted by rank.

Query Parameters

ParamTypeDefaultDescription
pagenumber1Page number
limitnumber40Items per page
platformstringautodesktop or mobile

Response: Same shape as GET /api/v1/games with category context.


Authors

GET /api/v1/authors/:slug/games

List all games by an author, sorted by rank.

Query Parameters: Same as games list (page, limit, platform).

Response

{
"author": {
"id": "author456",
"name": "Studio Name",
"slug": "studio-name"
},
"games": [...],
"pagination": {...}
}

Note: No exploration slots on author pages.


GET /api/v1/search

Search across games, categories, and authors.

Query Parameters

ParamTypeRequiredDescription
qstringYesQuery (min 2 characters)
typestringNoFilter: games, categories, authors. Default: all
limitnumberNoMax results per type (default 5)

Response

{
"games": [
{ "id": "...", "title": "...", "slug": "...", "thumbnailUrl": "..." }
],
"categories": [
{ "id": "...", "name": "...", "slug": "...", "iconUrl": "..." }
],
"authors": [
{ "id": "...", "name": "..." }
]
}

Events

POST /api/v1/events

Submit one or more tracking events.

Request

{
"events": [
{
"type": "game_click",
"gameId": "abc123",
"timestamp": "2026-02-10T14:30:00.000Z",
"context": {
"surface": "home",
"position": 5,
"platform": "desktop"
}
},
{
"type": "gameplay_start",
"gameId": "abc123",
"timestamp": "2026-02-10T14:30:05.000Z"
}
],
"sessionId": "sess_xyz"
}

Supported Event Types

See Data Model → Event Catalog for the full event list with descriptions. The following types are accepted by this endpoint:

game_click, game_loading_start, game_loading_end, game_focused_start, game_focused_stop, gameplay_start, gameplay_stop, category_click, show_ad

Note: game_like/game_dislike and game_impression/game_page_view are not sent through this endpoint. Likes and dislikes use the Feedback endpoints. Impressions and page views are generated server-side.

Response

{ "accepted": 2 }

Notes:

  • Events can be batched (recommended: flush every 5s or on page unload)
  • game_impression and game_page_view are generated server-side, not submitted by the client
  • Invalid events are silently dropped; accepted count reflects valid events only

Feedback

POST /api/v1/games/:id/like

Like a game. Replaces a previous dislike if any.

Response

{ "rating": { "score": 43, "likes": 59, "dislikes": 16 } }

POST /api/v1/games/:id/dislike

Dislike a game. Replaces a previous like if any.

DELETE /api/v1/games/:id/like

Remove a like or dislike.


Admin API

All admin endpoints require authentication via Authorization: Bearer <token> header.

Catalog

GET /api/admin/games

List games with full admin context. Supports all dashboard filters.

Query Parameters

ParamTypeDescription
statestringexploration, ranked
visibilitystringdraft, hidden, visible
tagsstringComma-separated: new,trendy
surfacestringhome, category
categorystringCategory slug
platformstringdesktop, mobile
timeRangestring24h, 7d, 30d, 6m, all, or custom
fromISO dateStart date (when timeRange=custom)
toISO dateEnd date (when timeRange=custom)
searchstringSearch by title or author
pagenumberPage number
limitnumberItems per page

Response

{
"games": [
{
"id": "abc123",
"title": "Game Title",
"thumbnailUrl": "https://...",
"authorName": "Studio",
"visibility": "visible",
"state": "ranked",
"tags": ["trendy"],
"rank": 15,
"delta": { "metric": "dau", "value": "+15%", "direction": "up" },
"quickMetrics": {
"dau": 1240,
"playtime": "4m32s",
"ctr": 0.12,
"retentionD1": 0.35
}
}
],
"pagination": {...}
}

POST /api/admin/games

Create a new game (Draft state).

Request

{
"title": "Game Title",
"description": "...",
"categories": ["cat1", "cat2"],
"authorName": "Studio",
"iframeUrl": "https://...",
"iconSource": "https://..."
}

Response

{
"game": { "id": "abc123", "visibility": "draft", ... },
"requirementsChecklist": {
"title": true,
"description": true,
"categories": true,
"icon": true,
"iframeSource": true,
"allMet": true
}
}

PATCH /api/admin/games/:id

Update game content, visibility, or settings.

Request (partial update)

{
"title": "New Title",
"visibility": "visible",
"categories": ["cat1", "cat3"]
}

Errors

StatusMessage
400Cannot set visible: requirements not met
404Game not found

DELETE /api/admin/games/:id

Permanently delete a game and its data.


Bulk Operations

POST /api/admin/games/import

Bulk import games from a broker.

Request

{
"broker": "gamedistribution",
"options": {
"initialVisibility": "draft",
"autoMapCategories": true
}
}

Response

{
"imported": 342,
"skipped": 12,
"duplicates": 8,
"errors": []
}

POST /api/admin/games/bulk

Apply actions to multiple games.

Request

{
"gameIds": ["abc123", "def456", "ghi789"],
"action": "setVisibility",
"value": "visible"
}

Supported Actions: setVisibility, disable, delete


Metrics

GET /api/admin/games/:id/metrics

Get full metrics for a single game.

Query Parameters

ParamTypeDescription
timeRangestring24h, 7d, 30d, 6m, all, custom
from / toISO dateCustom range
platformstringdesktop, mobile, all
surfacestringhome, category, all

Response

{
"engagement": {
"plays": { "total": 15420, "dailyAvg": 2203 },
"dau": { "total": 8930, "dailyAvg": 1276 },
"impressions": { "total": 125000, "dailyAvg": 17857 },
"ctr": 0.123
},
"performance": {
"playtime": { "avgPerPlayer": "4m32s" },
"engagedPlayers": 0.68,
"conversion": 0.82,
"retentionD1": 0.35,
"retentionD7": 0.18,
"returningUsers": 0.42,
"rating": { "score": 42, "likes": 58, "dislikes": 16 },
"loadingTime": { "avgMs": 2340 }
},
"monetization": {
"adsShown": { "total": 4200, "perSession": 0.27 }
}
}

Data Types

Game (Public)

interface GameCard {
id: string
title: string
slug: string
thumbnailUrl: string
authorName: string
rank: number | null
tags: ('new' | 'trendy' | 'updated')[]
isExploration: boolean
}

Game (Admin)

interface AdminGame extends GameCard {
visibility: 'draft' | 'hidden' | 'visible'
state: 'exploration' | 'ranked'
iframeUrl: string
description: string
categories: Category[]
broker: string | null
createdAt: string
updatedAt: string
}

TrackingEvent

interface TrackingEvent {
type: EventType
gameId?: string
categoryId?: string
timestamp: string
context?: {
surface?: 'home' | 'category' | 'game_page'
position?: number
platform?: 'desktop' | 'mobile'
categorySlug?: string
}
}

CORS Policy

OriginAllowed
https://playupi.comFull access (all methods)
https://admin.playupi.comAdmin API only
https://*.vercel.appPreview deploys (dev/staging only)
All othersBlocked
Access-Control-Allow-Origin: https://playupi.com
Access-Control-Allow-Methods: GET, POST, PATCH, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 86400

The Public API (/api/v1) allows GET and POST from the main domain. The Admin API (/api/admin) restricts to the admin subdomain. Preview deploy origins are allowed only in non-production environments.


Error Format

All errors follow a consistent format:

{
"error": "Human-readable error message",
"code": "GAME_NOT_FOUND",
"details": {}
}
StatusWhen
400Invalid request (missing fields, bad format)
401Missing or invalid auth token (admin API)
403Insufficient permissions
404Resource not found
409Conflict (duplicate, state violation)
429Rate limited
500Internal server error