API Reference

All endpoints below use this prefix:

/api/public/v1

Auth header for every call:

Authorization: Bearer <token>

Example environment used across commands:

export BASE_URL="https://highlights-api.rankacy.com"
export TOKEN="rk_live_..."

Pagination pattern

List endpoints use:

  • limit (bounded per endpoint)
  • offset

Response format:

{
  "items": [...],
  "pagination": {
    "total": 120,
    "limit": 20,
    "offset": 40
  }
}

Demos endpoints

Method Path Description
POST /demos/upload Upload .dem file
GET /demos List demos
GET /demos/{demo_id} Demo detail
GET /demos/{demo_id}/kills Parsed kill feed
GET /demos/{demo_id}/players Parsed players (SteamID64 strings)

POST /demos/upload

Request: multipart form-data

  • file (required): .dem
  • generate_auto_highlight (optional, boolean, default true): controls whether automatic TOP5 highlight generation is enabled for this user-demo link
  • resolution_id (optional, integer): must be sent together with fps_id to enable upload-time auto highlight settings
  • fps_id (optional, integer): must be sent together with resolution_id to enable upload-time auto highlight settings
  • Automatic TOP5 highlights created by upload processing always persist show_tick=false
curl -X POST "$BASE_URL/api/public/v1/demos/upload" \
  -H "Authorization: Bearer $TOKEN" \
  -F "file=@$DEMO_FILE" \
  -F "generate_auto_highlight=true" \
  -F "resolution_id=1" \
  -F "fps_id=1"

Response 202:

{
  "demo_id": 52,
  "status": "queued",
  "auto_highlight_requested": false,
  "was_already_processed_successfully": false,
  "user_demo_assignment": "newly_assigned",
  "auto_highlight_skip_reason": null,
  "auto_highlight_skip_message": null,
  "auto_highlight_estimated_credit": 0.1,
  "auto_highlight_current_credit": 20.0
}

Upload response fields:

  • status: queued for newly created demo; otherwise existing demo status (new|processing|success|failed)
  • auto_highlight_requested: true when this request queued an automatic TOP5 highlight (requires generate_auto_highlight=true, resolution_id + fps_id, enough credit, demo already SUCCESS, and no existing highlight for this user+demo)
  • was_already_processed_successfully: true when this upload matched an existing demo that was already in SUCCESS
  • user_demo_assignment: newly_assigned when this call created the user<->demo link, already_assigned when link already existed
  • auto_highlight_skip_reason / auto_highlight_skip_message: populated when upload-time auto highlight is skipped (auto_highlight_disabled, missing_generation_settings, partial_generation_settings, insufficient_credit, existing_highlight, no_kills)
  • auto_highlight_estimated_credit / auto_highlight_current_credit: returned when render options were resolved so the client can compare cost vs balance

GET /demos

Query params:

  • created_from (ISO datetime)
  • created_to (ISO datetime)
  • map
  • status (NEW|PROCESSING|SUCCESS|FAILED)
  • upload_type (MANUAL|API)
  • search
  • limit (1..100)
  • offset (>=0)
curl -G "$BASE_URL/api/public/v1/demos" \
  -H "Authorization: Bearer $TOKEN" \
  --data-urlencode "map=mirage" \
  --data-urlencode "status=SUCCESS" \
  --data-urlencode "limit=20" \
  --data-urlencode "offset=0"

GET /demos/{demo_id}

curl "$BASE_URL/api/public/v1/demos/$DEMO_ID" \
  -H "Authorization: Bearer $TOKEN"

GET /demos/{demo_id}/kills

Query params:

  • round
  • attacker_steam_id (SteamID64 string)
  • victim_steam_id (SteamID64 string)
  • limit (1..500)
  • offset

Response note:

  • attacker_steam_id and victim_steam_id are returned as strings (BIGINT-safe serialization).
curl -G "$BASE_URL/api/public/v1/demos/$DEMO_ID/kills" \
  -H "Authorization: Bearer $TOKEN" \
  --data-urlencode "attacker_steam_id=76561198000000000" \
  --data-urlencode "round=8" \
  --data-urlencode "limit=50"

Response shape:

{
  "items": [
    {
      "demo_kill_id": 801,
      "demo_id": 52,
      "tick": 15423,
      "round": 8,
      "attacker_steam_id": "76561198000000000",
      "victim_steam_id": "76561198000000001",
      "weapon": "ak47",
      "item_id": 7,
      "is_headshot": true,
      "score": 9.5
    }
  ],
  "pagination": {"total": 1, "limit": 50, "offset": 0}
}

GET /demos/{demo_id}/players

Response note:

  • steam_id is returned as a string (BIGINT-safe serialization).
curl "$BASE_URL/api/public/v1/demos/$DEMO_ID/players" \
  -H "Authorization: Bearer $TOKEN"

Response shape:

{
  "items": [
    {
      "id": 901,
      "player_name": "rankacy_pro",
      "steam_id": "76561198000000000"
    },
    {
      "id": 902,
      "player_name": "opponent_one",
      "steam_id": "76561198000000001"
    }
  ]
}

Highlights endpoints

Method Path Description
GET /highlights List highlights
GET /highlights/{highlight_id} Highlight detail
DELETE /highlights/{highlight_id} Delete highlight
GET /highlights/resolutions Resolution options
GET /highlights/fps FPS options
GET /highlights/cost Cost estimate for resolution + FPS
POST /highlights Generate highlight (auto)
POST /highlights/by-ticks Generate by tick ranges
POST /highlights/by-kill Generate by kill IDs

GET /highlights

Query params:

  • demo_id
  • status (NEW|PROCESSING|SUCCESS|FAILED)
  • created_from
  • created_to
  • type (auto|ticks|kill)
  • limit (1..100)
  • offset
curl -G "$BASE_URL/api/public/v1/highlights" \
  -H "Authorization: Bearer $TOKEN" \
  --data-urlencode "type=auto" \
  --data-urlencode "limit=20"

GET /highlights/{highlight_id}

curl "$BASE_URL/api/public/v1/highlights/$HIGHLIGHT_ID" \
  -H "Authorization: Bearer $TOKEN"

Response shape:

{
  "id": 311,
  "demo_id": 52,
  "status": "SUCCESS",
  "title": "Auto highlight for demo 52",
  "type": "AUTO",
  "resolution_id": 1,
  "fps_id": 1,
  "cost": 0.1,
  "score": 18.75,
  "length": 34.2,
  "size": 26391542,
  "use_transition": false,
  "show_tick": false,
  "intro": "NONE",
  "created_at": "12-02-2026 12:20:19",
  "image_url": "https://cdn.example.com/highlights/311.jpg",
  "video_url": "https://cdn.example.com/highlights/311.mp4",
  "containers": [
    {
      "id": 901,
      "start_tick": 15423,
      "end_tick": 15610,
      "speed": 1,
      "steam_id": "76561198000000000",
      "player_name": "rankacy_pro",
      "kills": [
        {
          "demo_kill_id": 801,
          "tick": 15440,
          "round": 8,
          "attacker_steam_id": "76561198000000000",
          "attacker_name": "rankacy_pro",
          "victim_steam_id": "76561198000000001",
          "victim_name": "opponent_one",
          "weapon": "ak47",
          "item_id": 7,
          "is_headshot": true,
          "score": 9.5
        }
      ]
    }
  ]
}

DELETE /highlights/{highlight_id}

curl -X DELETE "$BASE_URL/api/public/v1/highlights/$HIGHLIGHT_ID" \
  -H "Authorization: Bearer $TOKEN"

Response shape:

{
  "highlight_id": 311,
  "status": "deleted"
}

GET /highlights/resolutions

curl "$BASE_URL/api/public/v1/highlights/resolutions" \
  -H "Authorization: Bearer $TOKEN"

Response shape:

{
  "items": [
    {"id": 1, "name": "1280x720", "width": 1280, "height": 720}
  ]
}

GET /highlights/fps

curl "$BASE_URL/api/public/v1/highlights/fps" \
  -H "Authorization: Bearer $TOKEN"

Response shape:

{
  "items": [
    {"id": 1, "name": "60 FPS", "fps": 60}
  ]
}

GET /highlights/cost

Query params:

  • resolution_id (required)
  • fps_id (required)
curl -G "$BASE_URL/api/public/v1/highlights/cost" \
  -H "Authorization: Bearer $TOKEN" \
  --data-urlencode "resolution_id=1" \
  --data-urlencode "fps_id=1"

Response shape:

{
  "resolution_id": 1,
  "fps_id": 1,
  "resolution_credit": 0.1,
  "fps_multiplier": 1.0,
  "cost": 0.1
}

POST /highlights

Accepted optional fields:

  • title
  • mode (accepted compatibility field; currently ignored by the backend)
  • use_transition
  • show_tick
  • intro (NONE|GENERIC|SCENIC)
curl -X POST "$BASE_URL/api/public/v1/highlights" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "demo_id": 52,
    "resolution_id": 1,
    "fps_id": 1,
    "title": "Mirage recap",
    "show_tick": true,
    "use_transition": false,
    "intro": "NONE"
  }'

POST /highlights/by-ticks

Each tick segment must include steam_id (SteamID64 string) so the clip is rendered from that player perspective. Each segment may also include speed (0.25|0.5|1|2, default 1).

curl -X POST "$BASE_URL/api/public/v1/highlights/by-ticks" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "demo_id": 52,
    "ticks": [
      {"start_tick": 15423, "end_tick": 15610, "speed": 1, "steam_id": "76561198000000000"},
      {"start_tick": 30120, "end_tick": 30400, "speed": 0.5, "steam_id": "76561198000000005"}
    ],
    "resolution_id": 1,
    "fps_id": 1,
    "title": "Round pivots",
    "show_tick": true
  }'

POST /highlights/by-kill

Accepts kill identifiers in these shapes:

  • demo_kill_ids: [1,2,3] (recommended)
  • kill_ids: [1,2,3] (alias)
  • demo_kill_id: 1 (single-item alias)
  • speed (0.25|0.5|1|2, default 1) applies to every generated segment
curl -X POST "$BASE_URL/api/public/v1/highlights/by-kill" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "demo_id": 52,
    "demo_kill_ids": [801, 805],
    "pre_ticks": 192,
    "post_ticks": 192,
    "speed": 2,
    "resolution_id": 1,
    "fps_id": 1,
    "title": "Entry chain",
    "show_tick": true
  }'

Response for generation endpoints (202):

{
  "highlight_id": 311,
  "status": "queued"
}

Account endpoints

Method Path Description
GET /me/credit Current balance and profile credit totals
GET /me/transactions Highlight transaction history

GET /me/credit

curl "$BASE_URL/api/public/v1/me/credit" \
  -H "Authorization: Bearer $TOKEN"

Response shape:

{
  "user_id": 7,
  "email": "dev@example.com",
  "is_staff": false,
  "credit": 13.5,
  "credit_bought": 20.0,
  "credit_given": 3.5,
  "created_at": "12-02-2026 12:20:19"
}

GET /me/transactions

Query params:

  • from (ISO datetime)
  • to (ISO datetime)
  • limit (1..100)
  • offset (>=0)
curl -G "$BASE_URL/api/public/v1/me/transactions" \
  -H "Authorization: Bearer $TOKEN" \
  --data-urlencode "limit=20" \
  --data-urlencode "offset=0"

Response shape:

{
  "items": [
    {
      "id": 14,
      "highlight_id": 311,
      "demo_id": 52,
      "resolution_id": 1,
      "fps_id": 1,
      "highlight_status": "NEW",
      "credit": 0.1,
      "created_at": "12-02-2026 12:20:19"
    }
  ],
  "pagination": {"total": 1, "limit": 20, "offset": 0}
}

Error model

Standard errors:

  • 401 auth missing/invalid
  • 403 revoked token or forbidden resource
  • 404 resource missing
  • 422 request validation/domain validation

Examples:

{"detail": "API token missing"}
{"detail": "API token revoked"}
{"detail": "Demo not found"}
{"detail": "Tick range is outside demo bounds"}
{
  "detail": [
    {
      "loc": ["body", "resolution_id"],
      "msg": "Field required",
      "type": "missing"
    }
  ]
}

Rate limits

No hard per-token rate-limiting policy is currently enforced by these routes.

Recommended client behavior:

  • use retries with exponential backoff for 5xx and network errors
  • avoid aggressive polling (prefer webhooks where possible)
  • use reasonable pagination (limit <= 100)
  • deduplicate retries client-side by your own request IDs