> ## Documentation Index
> Fetch the complete documentation index at: https://docs.top.gg/llms.txt
> Use this file to discover all available pages before exploring further.

# REST API Endpoints for Projects

> Use the /projects/:project_id endpoints to retrieve project details, update headline and page content, create announcements, and update Discord slash commands. Requires a valid Bearer token.

The projects endpoints let you retrieve a project's details, update its headline and page content, create announcements, and push updated command definitions to Top.gg. `:project_id` is the Top.gg project ID of a project your [OAuth authorization](/oauth/overview) covers, and each endpoint states its required [scope](/oauth/scopes).

A project that is not part of the authorization returns `404`. A missing scope returns `403`.

## Project tokens

With a legacy project token, use `@me` in place of `:project_id`. The token itself identifies the project. Project tokens only work with `@me`, and OAuth access tokens only work with explicit project IDs. Request bodies and responses are identical.

| OAuth access token                         | Project token                      |
| ------------------------------------------ | ---------------------------------- |
| `GET /projects/:project_id`                | `GET /projects/@me`                |
| `PATCH /projects/:project_id`              | `PATCH /projects/@me`              |
| `POST /projects/:project_id/announcements` | `POST /projects/@me/announcements` |
| `PATCH /projects/:project_id/metrics`      | `PATCH /projects/@me/metrics`      |
| `POST /projects/:project_id/metrics/batch` | `POST /projects/@me/metrics/batch` |
| `PUT /projects/:project_id/commands`       | `PUT /projects/@me/commands`       |

<Info>
  Bot endpoints are subject to stricter rate limits than other v1 endpoints. Stay within the published limits to avoid `429 Too Many Requests` responses.
</Info>

## GET /projects

**OAuth scope:** none, any valid access token or [application token](/oauth/application-token)

Lists the projects the current credential covers. With an access token that is the single project of the authorization. With an application token it is every project granted to your application, 100 per page.

Not available with project tokens.

```bash theme={null}
curl https://top.gg/api/v1/projects \
  -H "Authorization: Bearer $ACCESS_TOKEN"
```

### Query parameters

<ParamField query="cursor" type="string">
  Cursor from a previous response to fetch the next page. Only meaningful with an application token.
</ParamField>

### Response fields

<ResponseField name="projects" type="object[]" required>
  The covered projects.

  <Expandable title="Project object properties">
    <ResponseField name="id" type="string" required>
      The Top.gg project ID.
    </ResponseField>

    <ResponseField name="platform_id" type="string" required>
      The project's ID on its platform, for example the Discord application ID.
    </ResponseField>

    <ResponseField name="name" type="string" required>
      The project name.
    </ResponseField>

    <ResponseField name="platform" type="string" required>
      The platform the project belongs to. One of `discord` or `roblox`.
    </ResponseField>

    <ResponseField name="type" type="string" required>
      The project type. One of `bot`, `server`, or `game`.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="cursor" type="string">
  Present while more pages exist. Pass it back as the `cursor` query parameter. Keep paging until the cursor is absent. A page can be empty while later pages still hold projects.
</ResponseField>

### Example response

```json theme={null}
{
  "projects": [
    {
      "id": "218109768489992192",
      "platform_id": "1234567890",
      "name": "Example Bot",
      "platform": "discord",
      "type": "bot"
    }
  ]
}
```

## GET /projects/:project\_id

**OAuth scope:** `project.information.read`

Returns the project associated with your current token.

```bash theme={null}
curl https://top.gg/api/v1/projects/218109768489992192 \
  -H "Authorization: Bearer $TOPGG_TOKEN"
```

### Response fields

<ResponseField name="id" type="string" required>
  The unique Top.gg-sourced Snowflake identifier for the project.
</ResponseField>

<ResponseField name="name" type="string" required>
  The project name as it appears on the external platform.
</ResponseField>

<ResponseField name="platform" type="string" required>
  The platform the project belongs to. One of `discord` or `roblox`.
</ResponseField>

<ResponseField name="type" type="string" required>
  The project type. One of `bot`, `server`, or `game`.
</ResponseField>

<ResponseField name="headline" type="string" required>
  The short description set during project creation.
</ResponseField>

<ResponseField name="tags" type="string[]" required>
  List of tag IDs associated with the project.
</ResponseField>

<ResponseField name="votes" type="number" required>
  Current vote count used for ranking calculations.
</ResponseField>

<ResponseField name="votes_total" type="number" required>
  Total all-time votes received by the project.
</ResponseField>

<ResponseField name="review_score" type="number" required>
  Average review score across all reviews.
</ResponseField>

<ResponseField name="review_count" type="number" required>
  Total number of reviews submitted for the project.
</ResponseField>

### Example response

```json theme={null}
{
  "id": "218109768489992192",
  "name": "Miki",
  "type": "bot",
  "platform": "discord",
  "headline": "A great bot with tons of features!",
  "tags": ["anime", "economy", "fun", "leveling"],
  "votes": 1120,
  "votes_total": 313389,
  "review_score": 4.38,
  "review_count": 62245
}
```

***

## PATCH /projects/:project\_id

**OAuth scope:** `project.information.write`

Updates the headline and/or page content for your project. Both fields are locale-keyed, so you can set content for multiple languages in a single request.

```bash theme={null}
curl -X PATCH https://top.gg/api/v1/projects/218109768489992192 \
  -H "Authorization: Bearer $TOPGG_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "headline": {
      "en": "A great bot with tons of features!"
    },
    "page_content": {
      "en": "# Welcome\nThis is the full page description for your project..."
    }
  }'
```

### Request body

At least one of `headline` or `page_content` must be provided.

<ParamField body="headline" type="object">
  A map of locale codes to headline strings. Each headline must be between 3 and 140 characters.
</ParamField>

<ParamField body="page_content" type="object">
  A map of locale codes to page content strings (Markdown supported). Each entry must be between 300 and 50,000 characters.
</ParamField>

### Supported locales

`en`, `de`, `fr`, `pt`, `tr`, `hi`, `ja`, `ar`, `nl`, `ko`, `it`, `es`, `ru`, `uk`, `vi`, `zh`

### Response

Returns `204 No Content` on success. No response body is returned.

<Info>
  Changes may take up to 1 hour to appear on the website due to caching.
</Info>

### Error responses

| Status | Description                                                                 |
| ------ | --------------------------------------------------------------------------- |
| `400`  | Invalid project token                                                       |
| `404`  | Project not found                                                           |
| `422`  | Validation error (unsupported locale, length violations, or missing fields) |

***

## POST /projects/:project\_id/announcements

**OAuth scope:** `project.announcements.write`

Creates a new announcement for your project. Announcements appear on your project's page and can be used to notify users about updates, new features, or other news.

<Note>
  You can create at most one announcement every **4 hours**. Attempts during the cooldown period receive a `429 Too Many Requests` response with a `Retry-After` header indicating how many seconds to wait.
</Note>

```bash theme={null}
curl -X POST https://top.gg/api/v1/projects/218109768489992192/announcements \
  -H "Authorization: Bearer $TOPGG_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Version 2.0 Released!",
    "content": "We just released version 2.0 with a bunch of new features and improvements.",
    "category": "new_feature"
  }'
```

### Request body

<ParamField body="title" type="string" required>
  The announcement title. Must be between 3 and 100 characters.
</ParamField>

<ParamField body="content" type="string" required>
  The announcement body text. Must be between 10 and 2,000 characters.
</ParamField>

<ParamField body="category" type="string">
  The category to publish the announcement under. One of `announcement`, `event`, or `new_feature`. Defaults to `announcement` when omitted.
</ParamField>

### Response fields

<ResponseField name="title" type="string" required>
  The title of the created announcement.
</ResponseField>

<ResponseField name="content" type="string" required>
  The body content of the created announcement.
</ResponseField>

<ResponseField name="created_at" type="string" required>
  ISO 8601 timestamp of when the announcement was created.
</ResponseField>

### Example response

```json theme={null}
{
  "title": "Version 2.0 Released!",
  "content": "We just released version 2.0 with a bunch of new features and improvements.",
  "created_at": "2026-03-14T15:09:26Z"
}
```

### Error responses

| Status | Description                                                                    |
| ------ | ------------------------------------------------------------------------------ |
| `400`  | Invalid project token                                                          |
| `404`  | Project not found                                                              |
| `422`  | Validation error (title or content length violations, or unsupported category) |
| `429`  | Rate limited - only one announcement every 4 hours                             |

***

## PATCH /projects/:project\_id/metrics

**OAuth scope:** `project.metrics.write`

Submits a single metrics payload for your project. Use this to push fresh numbers after an event such as joining or leaving a guild or a player connecting.

```bash theme={null}
curl -X PATCH https://top.gg/api/v1/projects/218109768489992192/metrics \
  -H "Authorization: Bearer $TOPGG_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "server_count": 420,
    "shard_count": 67
  }'
```

### Request body

The body must match the authenticated project's platform. See the [platform payloads](#platform-payloads) section below for the supported fields per platform.

### Response

Returns `204 No Content` on success. No response body is returned.

### Error responses

| Status | Description                                                           |
| ------ | --------------------------------------------------------------------- |
| `400`  | Invalid project token                                                 |
| `404`  | Project not found                                                     |
| `422`  | Payload was malformed, empty, or did not match the project's platform |

***

## POST /projects/:project\_id/metrics/batch

**OAuth scope:** `project.metrics.write`

Submits up to 100 metrics entries in a single request. Entries without a `timestamp` are applied first in the order they appear in the request. Entries with a `timestamp` are applied afterward in ascending timestamp order, so you can backfill gaps or push buffered samples after a network interruption.

```bash theme={null}
curl -X POST https://top.gg/api/v1/projects/218109768489992192/metrics/batch \
  -H "Authorization: Bearer $TOPGG_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "data": [
      {
        "timestamp": "2026-04-17T10:00:00Z",
        "metrics": { "server_count": 419 }
      },
      {
        "timestamp": "2026-04-17T10:05:00Z",
        "metrics": { "server_count": 420, "shard_count": 67 }
      }
    ]
  }'
```

### Request body

<ParamField body="data" type="object[]" required>
  The batch of metrics entries. Must contain between 1 and 100 entries.

  <Expandable title="Entry object properties">
    <ParamField name="metrics" type="object" required>
      The metrics payload for this entry. Must be a JSON object matching the project's platform shape. See [platform payloads](#platform-payloads).
    </ParamField>

    <ParamField name="timestamp" type="string">
      ISO 8601 timestamp of when the metrics were collected. Cannot be more than 5 minutes in the future. Entries without a timestamp are applied before dated entries, in the order they appear in the request.
    </ParamField>
  </Expandable>
</ParamField>

### Response

Returns `204 No Content` when every entry is accepted. If any entry fails validation, processing stops and the request returns `422`.

### Error responses

| Status | Description                                                                     |
| ------ | ------------------------------------------------------------------------------- |
| `400`  | Invalid project token                                                           |
| `404`  | Project not found                                                               |
| `422`  | Batch was empty, contained more than 100 entries, or contained an invalid entry |

***

## Platform payloads

The metrics payload is shaped per platform. For `PATCH /projects/:project_id/metrics`, send this payload as the request body. For the batch metrics endpoint, send the same payload under `data[].metrics`. Sending the wrong shape for your project returns `422`.

### Discord bot

Use for projects with `platform: "discord"` and `type: "bot"`. At least one field is required.

<ParamField body="server_count" type="integer">
  Total number of servers the bot is currently in. Must be zero or greater.
</ParamField>

<ParamField body="shard_count" type="integer">
  Total number of shards the bot is currently running. Must be zero or greater.
</ParamField>

```json theme={null}
{
  "server_count": 420,
  "shard_count": 67
}
```

### Discord server

Use for projects with `platform: "discord"` and `type: "server"`. At least one field is required.

<ParamField body="member_count" type="integer">
  Total number of members in the server. Must be zero or greater.
</ParamField>

<ParamField body="online_count" type="integer">
  Number of members currently online. Must be zero or greater and cannot exceed `member_count` when both are provided.
</ParamField>

```json theme={null}
{
  "member_count": 14820,
  "online_count": 3120
}
```

### Roblox game

Use for projects with `platform: "roblox"` and `type: "game"`.

<ParamField body="player_count" type="integer" required>
  Current number of players in the game. Must be zero or greater.
</ParamField>

```json theme={null}
{
  "player_count": 428
}
```

***

## PUT /projects/:project\_id/commands

**OAuth scope:** `project.information.write`

Overwrites the list of slash command definitions for your bot project on Top.gg. This endpoint is only applicable to `bot`-type projects on the `discord` platform.

```bash theme={null}
curl -X PUT https://top.gg/api/v1/projects/218109768489992192/commands \
  -H "Authorization: Bearer $TOPGG_TOKEN" \
  -H "Content-Type: application/json" \
  -d '[{ "name": "ping", "description": "Replies with Pong!" }]'
```

### Request body

The request body must be a JSON array of command objects following [Discord's application command structure](https://discord.com/developers/docs/interactions/application-commands#application-command-object). Pass an empty array to clear all commands.

<ParamField body="commands" type="object[]" required>
  Array of application command objects following Discord's application command structure.
</ParamField>

### Response

Returns `204 No Content` on success. No response body is returned.
