---
name: "Thumbnail Personalization"
last_updated: 2026-07-23T22:19:14Z
type: feature
api_base_url: "https://apis.roblox.com"
endpoints: 7
auth: [api-key, oauth2, cookie]
description: "Upload and manage creator-provided homepage thumbnail variants for an experience, then choose which player groups see each variant"
---

# Thumbnail Personalization

Upload and manage creator-provided homepage thumbnail variants for an experience, then choose which player groups see each variant. Unlike the Thumbnails feature, these APIs manage homepage images and targeting rules instead of generating or retrieving standard Roblox thumbnails.

**Base URL:** `https://apis.roblox.com`

    ## Authentication

    Each endpoint requires one of the following authentication methods:

    - **API Key**: Pass your key in the `x-api-key` HTTP header. Create keys at [Creator Dashboard](https://create.roblox.com/dashboard/credentials).
- **OAuth 2.0**: Use Bearer token in the `Authorization` header. Authorization URL: `https://apis.roblox.com/oauth/v1/authorize`, Token URL: `https://apis.roblox.com/oauth/v1/token`
- **Cookie** *(not recommended)*: `.ROBLOSECURITY` cookie. Do not use in production.

    ```
    # API Key example
    curl -H "x-api-key: YOUR_API_KEY" https://apis.roblox.com/...

    # OAuth 2.0 example
    curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" https://apis.roblox.com/...
    ```

## Endpoints

### GET `/thumbnail-personalization-api/v1/universes/{universeId}/personalization` [EXPERIMENTAL]

**Auth:** Cookie (`.ROBLOSECURITY`) or API Key (`x-api-key` header) or OAuth 2.0 Bearer token

**Scopes:** `universe.thumbnail:read`

**Parameters:**

| Name | In | Type | Required | Description |
|------|-----|------|----------|-------------|
| `universeId` | path | `integer` | Yes |  |
| `status` | query | `any` | No |  |
| `limit` | query | `integer` | No |  |
| `cursor` | query | `string` | No |  |

**Responses:**

- `200`: Success → `ActionResult`
- `400`: Bad Request → `ActionResult`
- `401`: Unauthorized → `ActionResult`
- `403`: Forbidden → `ActionResult`
- `500`: Server Error → `ActionResult`

**Response fields** (`ActionResult`)

See [ActionResult](#actionresult) in Models.

**Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. 

**Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute

**Example:**
```bash
curl -H "x-api-key: $ROBLOX_API_KEY" \
  "https://apis.roblox.com/thumbnail-personalization-api/v1/universes/{UNIVERSEID}/personalization"
```

### POST `/thumbnail-personalization-api/v1/universes/{universeId}/personalization/create` [EXPERIMENTAL]

**Auth:** Cookie (`.ROBLOSECURITY`) or API Key (`x-api-key` header) or OAuth 2.0 Bearer token

**Scopes:** `universe.thumbnail:write`

**Parameters:**

| Name | In | Type | Required | Description |
|------|-----|------|----------|-------------|
| `universeId` | path | `integer` | Yes |  |

**Request Body:** `application/json-patch+json` — Type: `any`

> **Verify mutations:** If your API key lacks the required scope (`universe.thumbnail:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource.

**Responses:**

- `200`: Success → `ActionResult`
- `400`: Bad Request → `ActionResult`
- `401`: Unauthorized → `ActionResult`
- `403`: Forbidden → `ActionResult`
- `500`: Server Error → `ActionResult`

**Response fields** (`ActionResult`)

See [ActionResult](#actionresult) in Models.

**Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. 

**Rate Limits:** perApiKeyOwner: 50/minute, perOauth2Authorization: 50/minute

**Example:**
```bash
curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \
  "https://apis.roblox.com/thumbnail-personalization-api/v1/universes/{UNIVERSEID}/personalization/create" \
  -H "Content-Type: application/json" \
  -d '"..."'
```

### POST `/thumbnail-personalization-api/v1/universes/{universeId}/personalization/update` [EXPERIMENTAL]

**Auth:** Cookie (`.ROBLOSECURITY`) or API Key (`x-api-key` header) or OAuth 2.0 Bearer token

**Scopes:** `universe.thumbnail:write`

**Parameters:**

| Name | In | Type | Required | Description |
|------|-----|------|----------|-------------|
| `universeId` | path | `integer` | Yes |  |

**Request Body:** `application/json-patch+json` — Type: `any`

> **Verify mutations:** If your API key lacks the required scope (`universe.thumbnail:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource.

**Responses:**

- `200`: Success → `ActionResult`
- `400`: Bad Request → `ActionResult`
- `401`: Unauthorized → `ActionResult`
- `403`: Forbidden → `ActionResult`
- `500`: Server Error → `ActionResult`

**Response fields** (`ActionResult`)

See [ActionResult](#actionresult) in Models.

**Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. 

**Rate Limits:** perApiKeyOwner: 50/minute, perOauth2Authorization: 50/minute

**Example:**
```bash
curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \
  "https://apis.roblox.com/thumbnail-personalization-api/v1/universes/{UNIVERSEID}/personalization/update" \
  -H "Content-Type: application/json" \
  -d '"..."'
```

### GET `/thumbnail-personalization-api/v1/universes/{universeId}/thumbnails` [EXPERIMENTAL]

**Auth:** Cookie (`.ROBLOSECURITY`) or API Key (`x-api-key` header) or OAuth 2.0 Bearer token

**Scopes:** `universe.thumbnail:read`

**Parameters:**

| Name | In | Type | Required | Description |
|------|-----|------|----------|-------------|
| `universeId` | path | `integer` | Yes |  |
| `limit` | query | `integer` | No |  |
| `nextCursor` | query | `string` | No |  |

**Responses:**

- `200`: Success → `FindThumbnailsResponse`
- `400`: Bad Request → `ActionResult`
- `401`: Unauthorized → `ActionResult`
- `403`: Forbidden → `ActionResult`
- `500`: Server Error → `ActionResult`

**Response fields** (`FindThumbnailsResponse`)

See [FindThumbnailsResponse](#findthumbnailsresponse) in Models.

**Response example:**
```json
{
  "homepageThumbnails": [
    {
      "homepageThumbnailId": "...",
      "assetId": "...",
      "moderationStatus": "...",
      "personalizedConfigStatus": "...",
      "homepageThumbnailStatus": "..."
    }
  ],
  "nextCursor": "string"
}
```

**Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. 

**Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute

**Example:**
```bash
curl -H "x-api-key: $ROBLOX_API_KEY" \
  "https://apis.roblox.com/thumbnail-personalization-api/v1/universes/{UNIVERSEID}/thumbnails"
```

### DELETE `/thumbnail-personalization-api/v1/universes/{universeId}/thumbnails` [EXPERIMENTAL]

**Auth:** Cookie (`.ROBLOSECURITY`) or API Key (`x-api-key` header) or OAuth 2.0 Bearer token

**Scopes:** `universe.thumbnail:write`

**Parameters:**

| Name | In | Type | Required | Description |
|------|-----|------|----------|-------------|
| `universeId` | path | `integer` | Yes |  |
| `homepageThumbnailIds` | query | `string[]` | No |  |

> **Verify mutations:** If your API key lacks the required scope (`universe.thumbnail:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource.

**Responses:**

- `200`: Success → `ActionResult`
- `400`: Bad Request → `ActionResult`
- `401`: Unauthorized → `ActionResult`
- `403`: Forbidden → `ActionResult`
- `500`: Server Error → `ActionResult`

**Response fields** (`ActionResult`)

See [ActionResult](#actionresult) in Models.

**Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. 

**Rate Limits:** perApiKeyOwner: 50/minute, perOauth2Authorization: 50/minute

**Example:**
```bash
curl -X DELETE -H "x-api-key: $ROBLOX_API_KEY" \
  "https://apis.roblox.com/thumbnail-personalization-api/v1/universes/{UNIVERSEID}/thumbnails"
```

### POST `/thumbnail-personalization-api/v1/universes/{universeId}/thumbnails/uploads` [EXPERIMENTAL]

**Auth:** Cookie (`.ROBLOSECURITY`) or API Key (`x-api-key` header) or OAuth 2.0 Bearer token

**Scopes:** `universe.thumbnail:write`

**Parameters:**

| Name | In | Type | Required | Description |
|------|-----|------|----------|-------------|
| `universeId` | path | `integer` | Yes |  |

**Request Body:** `multipart/form-data` — Type: `object`

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `files` | `string[]` | No |  |

**Request example:**
```json
{
  "files": [
    "string"
  ]
}
```

> **Verify mutations:** If your API key lacks the required scope (`universe.thumbnail:write`), this endpoint may return successfully without applying changes. Always verify mutations by re-reading the resource.

**Responses:**

- `200`: Success → `UploadThumbnailsResponse`
- `400`: Bad Request → `ActionResult`
- `401`: Unauthorized → `ActionResult`
- `403`: Forbidden → `ActionResult`
- `500`: Server Error → `ActionResult`

**Response fields** (`UploadThumbnailsResponse`)

See [UploadThumbnailsResponse](#uploadthumbnailsresponse) in Models.

**Response example:**
```json
{
  "fileToOperationIdDict": "..."
}
```

**Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. 

**Rate Limits:** perApiKeyOwner: 50/minute, perOauth2Authorization: 50/minute

**Example:**
```bash
curl -X POST -H "x-api-key: $ROBLOX_API_KEY" \
  "https://apis.roblox.com/thumbnail-personalization-api/v1/universes/{UNIVERSEID}/thumbnails/uploads" \
  -F "files=["string"]"
```

### GET `/thumbnail-personalization-api/v1/universes/{universeId}/thumbnails/uploads/status` [EXPERIMENTAL]

**Auth:** Cookie (`.ROBLOSECURITY`) or API Key (`x-api-key` header) or OAuth 2.0 Bearer token

**Scopes:** `universe.thumbnail:read`

**Parameters:**

| Name | In | Type | Required | Description |
|------|-----|------|----------|-------------|
| `universeId` | path | `integer` | Yes |  |
| `operationIds` | query | `string[]` | No |  |

**Responses:**

- `200`: Success → `UploadThumbnailsStatusResponse`
- `400`: Bad Request → `ActionResult`
- `401`: Unauthorized → `ActionResult`
- `403`: Forbidden → `ActionResult`
- `500`: Server Error → `ActionResult`

**Response fields** (`UploadThumbnailsStatusResponse`)

See [UploadThumbnailsStatusResponse](#uploadthumbnailsstatusresponse) in Models.

**Response example:**
```json
{
  "uploadStatus": "...",
  "uploadThumbnailStatusDict": "..."
}
```

**Error handling:** `401`: Check that your API key/token is valid and not expired. `403`: Verify your API key has the required scopes listed above. 

**Rate Limits:** perApiKeyOwner: 100/minute, perOauth2Authorization: 100/minute

**Example:**
```bash
curl -H "x-api-key: $ROBLOX_API_KEY" \
  "https://apis.roblox.com/thumbnail-personalization-api/v1/universes/{UNIVERSEID}/thumbnails/uploads/status"
```

## Models

### FindThumbnailsResponse

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `homepageThumbnails` | `HomepageThumbnail[]` | Yes |  |
| `nextCursor` | `string` | No |  |

### UploadThumbnailsResponse

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `fileToOperationIdDict` | `object` | Yes |  |

### UploadThumbnailsStatusResponse

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `uploadStatus` | `any` | Yes |  |
| `uploadThumbnailStatusDict` | `object` | Yes |  |