Documentation
Everything you need to resolve Reel URLs and download the media programmatically. The API is public and requires no authentication.
Endpoints
- POST
/api/reels/resolveResolve a Reel URL and return its metadata and download options.
- GET
/api/reels/download/:resolutionId/:qualityStream a resolved media variant. Supports Range requests.
- GET
/api/healthLiveness and version information.
- GET
/api/health/readyReports whether the optional database and Redis dependencies are in use.
Resolve a Reel
Send the URL as JSON. The resolver accepts the formats people actually paste: a trailing slash, a missing scheme, shared ?igsh= query strings and #media fragments are all normalised.
curl -X POST /api/reels/resolve \
-H "Content-Type: application/json" \
-d '{"url": "https://www.instagram.com/reel/ABC123/"}'A successful response returns the reel metadata. The direct media URL located on the public Instagram page stays server-side, so downloadUrl always points back at this API.
{
"success": true,
"data": {
"id": "ig_ABC123",
"title": "Amazing Reel Title",
"thumbnail": "https://...fbcdn.net/...",
"duration": 30,
"shortCode": "ABC123",
"media": [
{
"quality": "hd",
"format": "mp4",
"downloadUrl": "/api/reels/download/ig_ABC123/hd",
"width": 1080,
"height": 1920
}
]
}
}Download
Request the downloadUrl from the resolve response. Range headers are forwarded upstream, so partial requests return 206 with a Content-Range. Instagram signs the underlying media URLs, so a link that has expired returns 410 and the Reel should be resolved again.
curl -L -o reel.mp4 "/api/reels/download/ig_ABC123/hd"Media variants
| Quality | Format | Typical size |
|---|---|---|
| original | mp4 | Source quality |
| hd | mp4 | 720p-1080p |
| sd | mp4 | Lower bandwidth |
media[] only lists the renditions that were actually located on the public page, so the set can vary per Reel.
Rate limits
Resolutions are limited to 30 requests per minute per IP and downloads to 10 per hour. Every response carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset; a limited response adds Retry-After.
Errors
Failures return a machine-readable code alongside a human-readable message.
{
"success": false,
"error": {
"code": "PRIVATE_CONTENT",
"message": "This content is from a private account"
}
}| Status | Code | Meaning |
|---|---|---|
| 400 | INVALID_URL | The URL format is invalid. |
| 400 | UNSUPPORTED_URL | Not a supported Instagram Reel or Post URL. |
| 403 | PRIVATE_CONTENT | The content is from a private account. |
| 404 | NOT_FOUND | The Reel was not found or has been deleted. |
| 401 | AUTH_REQUIRED | The Reel exists, but Instagram only serves its video file to a signed-in session. |
| 404 | MEDIA_UNAVAILABLE | The media could not be located or accessed. |
| 429 | RATE_LIMITED | Too many requests. |
| 451 | NOT_PERMITTED | The content cannot be legally or technically accessed. |
| 500 | INTERNAL_ERROR | An unexpected server error occurred. |
Using it from JavaScript
const response = await fetch('/api/reels/resolve', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ url }),
});
const result = await response.json();
if (!result.success) throw new Error(result.error.message);
const downloadUrl = result.data.media.find((m) => m.quality === 'hd')?.downloadUrl;
if (downloadUrl) {
const video = await fetch(downloadUrl);
// Handle the streamed response.
}