Skip to main content

Thumbnails

Thumbnails API

AntCDN supports two thumbnail approaches:

  • Dynamic thumbnails (fastest): request a thumbnail directly from the worker URL.
  • Generated thumbnails (API job): request a thumbnail to be generated and cached.

Overview

In most cases, dynamic thumbnails are enough. Use the job API if you want deterministic caching / tracking.

Dynamic thumbnails (worker URL)

Request a thumbnail directly from the worker:

https://worker.antcdn.net/v1/thumbnail/{orgID}/{envID}/{edgeKey}.{ext}?width=640&timeStamp=30

Common query params:

  • width: output width in pixels
  • timeStamp: seconds into the video

Generated thumbnails (API job)

1) Request generation

/{orgID}/edge-ids/{edgeId}/thumbnails

Body:

{
"width": 320,
"height": 180,
"timestampMs": 12000
}

Response:

{
"body": {
"jobId": "thumb_job_123456789abcdef",
"url": "https://worker.antcdn.net/v1/thumbnail/{orgID}/{envID}/{edgeKey}.webp?width=320&timeStamp=12",
"status": "pending"
}
}

2) Poll job status

/{orgID}/thumbnails/{jobID}

Monitor the progress of thumbnail generation jobs.

Path Parameters

ParameterTypeRequiredDescription
jobIDstringYesThe job ID returned from thumbnail generation

Response

{
"body": {
"status": "completed",
"url": "https://worker.antcdn.net/v1/thumbnail/{orgID}/{envID}/{edgeKey}.webp?width=320&timeStamp=12"
}
}

Job Status Values

StatusDescription
pendingThumbnail generation is queued
processingThumbnail is being generated
completedThumbnail is ready and URL is available
failedGeneration failed (check error logs)

Example Request

Tip

If you already have a thumbnailUrl from video details, you can use that directly.

Private Video Thumbnails

For private videos, the thumbnailUrl is returned clean (without embedded tokens). To access:

Using signing keys (recommended for storage):

const signedUrl = `${video.thumbnailUrl}&token=${generateJWT(signingKey, keyId)}`;

Using previewToken (quick access, expires ~1 hour):

const signedUrl = `${video.thumbnailUrl}&token=${video.previewToken}`;

See Private Videos for signing key setup and JWT examples.

Best Practices

Thumbnail Dimensions

  • Video Galleries: 320x180 (16:9) or 240x135 (16:9)
  • Social Media: 1200x630 (Facebook) or 1200x675 (Twitter)
  • Mobile Apps: 640x360 or 480x270
  • High Quality: 1920x1080 for detailed previews

Timestamp Selection

  • Video Start: Use 0 for opening frame
  • Key Moments: Extract frames at important scenes
  • Midpoint: Use videoDurationMs / 2 for middle frame
  • Custom: Choose specific timestamps for branded content

Error Handling

const generateThumbnailWithRetry = async (videoId, options, maxRetries = 3) => {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const response = await generateThumbnail(videoId, options);
// Poll for completion
let status = 'pending';
while (status === 'pending' || status === 'processing') {
await new Promise(resolve => setTimeout(resolve, 2000)); // Wait 2 seconds
const statusResponse = await checkThumbnailStatus(response.jobId);
status = statusResponse.status;
if (status === 'completed') {
return statusResponse.url;
} else if (status === 'failed') {
throw new Error('Thumbnail generation failed');
}
}
} catch (error) {
if (attempt === maxRetries) throw error;
await new Promise(resolve => setTimeout(resolve, 1000 * attempt)); // Exponential backoff
}
}
};

Common Use Cases

// Generate thumbnails for video gallery
const generateGalleryThumbnails = async (videos) => {
const thumbnails = await Promise.all(
videos.map(async (video) => {
const response = await generateThumbnail(video.publicId, {
width: 320,
height: 180,
timestampMs: 5000 // 5 seconds into video
});
return {
videoId: video.assetId,
thumbnailUrl: response.url,
jobId: response.jobId
};
})
);
return thumbnails;
};

Social Media Previews

// Generate social media optimized thumbnails
const generateSocialThumbnail = async (videoId) => {
const response = await generateThumbnail(videoId, {
width: 1200,
height: 630, // Facebook optimal size
timestampMs: 0 // Use opening frame
});
return response.url;
};

Mobile App Thumbnails

// Generate mobile-optimized thumbnails
const generateMobileThumbnail = async (videoId) => {
const response = await generateThumbnail(videoId, {
width: 640,
height: 360,
timestampMs: 10000 // 10 seconds into video
});
return response.url;
};

Error Handling

Common Errors

Status CodeErrorDescriptionSolution
400Bad RequestInvalid dimensions or timestampCheck width/height (1-4096) and timestamp (0 to video duration)
401UnauthorizedInvalid API keyVerify API key is correct and active
403ForbiddenVideo doesn’t belong to organizationEnsure video belongs to your organization
404Not FoundVideo not foundVerify video public ID is correct
422Unprocessable EntityTimestamp exceeds video durationUse timestamp within video length
429Too Many RequestsRate limit exceededWait before making more requests

Error Response Example

{
"title": "Bad Request",
"status": 400,
"detail": "Invalid thumbnail dimensions",
"errors": [
{
"message": "Width must be between 1 and 4096 pixels",
"location": "body.width",
"value": 5000
}
]
}

Rate Limits

OperationLimitWindow
Thumbnail generation50 requests1 hour
Status checks100 requests1 hour
Thumbnail retrieval1000 requests1 hour

Tip

Thumbnail generation is processed asynchronously. Use the job ID to poll for completion status rather than making repeated generation requests.