curl -X POST "https://api.hooked.so/v1/image/expand" \
-H "x-api-key: your_api_key_here" \
-H "Content-Type: application/json" \
-d '{ "media": "cm1a2b3c4d5e6f7g8h9i0j1k2", "aspectRatio": "ratio_16_9", "prompt": "the same beach, more sand and sky" }'
const response = await fetch('https://api.hooked.so/v1/image/expand', {
method: 'POST',
headers: { 'x-api-key': 'your_api_key_here', 'Content-Type': 'application/json' },
body: JSON.stringify({ imageUrl: 'https://example.com/product.png', aspectRatio: 'ratio_9_16' })
});
const { data } = await response.json();
// The new image; poll GET /v1/image/{mediaId} until status is COMPLETED
console.log(data.mediaId, data.status);
import requests
response = requests.post(
'https://api.hooked.so/v1/image/expand',
headers={'x-api-key': 'your_api_key_here'},
json={'media': 'cm1a2b3c4d5e6f7g8h9i0j1k2', 'aspectRatio': 'ratio_1_1'}
)
data = response.json()['data']
print(data['mediaId'], data['status'])
{
"success": true,
"message": "Image expansion started",
"data": {
"mediaId": "cm9z8y7x6w5v4u3t2s1r0q9p8",
"sourceMediaId": "cm1a2b3c4d5e6f7g8h9i0j1k2",
"status": "PROCESSING",
"aspectRatio": "ratio_16_9",
"usedCredits": 7
}
}
{
"success": false,
"message": "aspectRatio: Invalid enum value. Expected 'ratio_9_16' | 'ratio_1_1' | 'ratio_16_9', received 'ratio_4_5'"
}
{
"success": false,
"message": "The image already has that ratio"
}
{
"success": false,
"errorCode": "INSUFFICIENT_CREDITS",
"creditsNeeded": 7,
"creditsAvailable": 2,
"message": "Not enough credits to perform this action"
}
Expand Image
Grow an image to another aspect ratio, with AI painting the new area
curl -X POST "https://api.hooked.so/v1/image/expand" \
-H "x-api-key: your_api_key_here" \
-H "Content-Type: application/json" \
-d '{ "media": "cm1a2b3c4d5e6f7g8h9i0j1k2", "aspectRatio": "ratio_16_9", "prompt": "the same beach, more sand and sky" }'
const response = await fetch('https://api.hooked.so/v1/image/expand', {
method: 'POST',
headers: { 'x-api-key': 'your_api_key_here', 'Content-Type': 'application/json' },
body: JSON.stringify({ imageUrl: 'https://example.com/product.png', aspectRatio: 'ratio_9_16' })
});
const { data } = await response.json();
// The new image; poll GET /v1/image/{mediaId} until status is COMPLETED
console.log(data.mediaId, data.status);
import requests
response = requests.post(
'https://api.hooked.so/v1/image/expand',
headers={'x-api-key': 'your_api_key_here'},
json={'media': 'cm1a2b3c4d5e6f7g8h9i0j1k2', 'aspectRatio': 'ratio_1_1'}
)
data = response.json()['data']
print(data['mediaId'], data['status'])
{
"success": true,
"message": "Image expansion started",
"data": {
"mediaId": "cm9z8y7x6w5v4u3t2s1r0q9p8",
"sourceMediaId": "cm1a2b3c4d5e6f7g8h9i0j1k2",
"status": "PROCESSING",
"aspectRatio": "ratio_16_9",
"usedCredits": 7
}
}
{
"success": false,
"message": "aspectRatio: Invalid enum value. Expected 'ratio_9_16' | 'ratio_1_1' | 'ratio_16_9', received 'ratio_4_5'"
}
{
"success": false,
"message": "The image already has that ratio"
}
{
"success": false,
"errorCode": "INSUFFICIENT_CREDITS",
"creditsNeeded": 7,
"creditsAvailable": 2,
"message": "Not enough credits to perform this action"
}
Overview
Starts growing an image to the aspect ratio you ask for. The original is centred on the larger canvas at its own size and the new area is generated, optionally guided by a prompt. The result is a new image in your library; the original is not modified. The call answers202 with the id of the pending image. Poll Get Image on that mediaId until its status is COMPLETED; its url is the expanded image.
media (an image of your library) or imageUrl (imported into your library first); when both are sent, media is used. Accepted formats: JPEG, PNG and WEBP, up to 25 MB. An image that already has the requested ratio is rejected with 400.Credits
| Team mode | Cost |
|---|---|
| Managed (Hooked’s keys) | 7 credits per expansion, charged when the job starts and refunded if it fails |
| BYOK (your own keys) | No credits (usedCredits is 0). The team needs its own Bria key in Settings → AI keys |
curl -X POST "https://api.hooked.so/v1/image/expand" \
-H "x-api-key: your_api_key_here" \
-H "Content-Type: application/json" \
-d '{ "media": "cm1a2b3c4d5e6f7g8h9i0j1k2", "aspectRatio": "ratio_16_9", "prompt": "the same beach, more sand and sky" }'
const response = await fetch('https://api.hooked.so/v1/image/expand', {
method: 'POST',
headers: { 'x-api-key': 'your_api_key_here', 'Content-Type': 'application/json' },
body: JSON.stringify({ imageUrl: 'https://example.com/product.png', aspectRatio: 'ratio_9_16' })
});
const { data } = await response.json();
// The new image; poll GET /v1/image/{mediaId} until status is COMPLETED
console.log(data.mediaId, data.status);
import requests
response = requests.post(
'https://api.hooked.so/v1/image/expand',
headers={'x-api-key': 'your_api_key_here'},
json={'media': 'cm1a2b3c4d5e6f7g8h9i0j1k2', 'aspectRatio': 'ratio_1_1'}
)
data = response.json()['data']
print(data['mediaId'], data['status'])
{
"success": true,
"message": "Image expansion started",
"data": {
"mediaId": "cm9z8y7x6w5v4u3t2s1r0q9p8",
"sourceMediaId": "cm1a2b3c4d5e6f7g8h9i0j1k2",
"status": "PROCESSING",
"aspectRatio": "ratio_16_9",
"usedCredits": 7
}
}
{
"success": false,
"message": "aspectRatio: Invalid enum value. Expected 'ratio_9_16' | 'ratio_1_1' | 'ratio_16_9', received 'ratio_4_5'"
}
{
"success": false,
"message": "The image already has that ratio"
}
{
"success": false,
"errorCode": "INSUFFICIENT_CREDITS",
"creditsNeeded": 7,
"creditsAvailable": 2,
"message": "Not enough credits to perform this action"
}
Errors
| Status | When | Body |
|---|---|---|
| 400 | Body is not JSON | Invalid JSON body |
| 400 | Neither media nor imageUrl, or bad aspectRatio / prompt | media or imageUrl is required, aspectRatio: ..., prompt: ... |
| 400 | media is not an image of your library | media: Image not found, media: Media must be an image |
| 400 | imageUrl could not be imported | imageUrl: The image URL could not be fetched, imageUrl: The URL must point to a JPEG, PNG or WEBP image, imageUrl: The image is larger than 25 MB |
| 400 | The image cannot be expanded | The image already has that ratio, The image could not be read |
| 401 | Missing or invalid API key | { "code": "not_authenticated", ... } |
| 402 | Managed team without enough credits | errorCode: "INSUFFICIENT_CREDITS" |
| 402 | Managed team without a live subscription | code: "subscription_required" |
| 402 | BYOK team without its Bria key | code: "missing_credentials", missingProviders: ["bria"] |
| 403 | The team does not have the Hooked app product | code: "entitlement_required" |
| 502 | Bria did not take the request (credits are refunded) | { "success": false, "message": "..." } |
| 500 | Unexpected error | { "code": "internal_server_error", "message": "Failed to expand the image", ... } |
status: "FAILED" in Get Image and the credits are refunded.
Related Endpoints
Get Image
Remove Image Background
Authorizations
Headers
Makes the request safe to retry. 1-255 printable ASCII characters, one per operation (your job id, or a UUID you store), reused on every retry. Within 24 hours the same key with the same body answers with the first response and the header Idempotent-Replayed: true, without running or charging again. Keys are scoped to your team and the endpoint. 5xx answers and refusals before anything ran (401, 402, 403, 409, 429) are not kept. See Idempotency.
1 - 255^[\x20-\x7E]+$Body
Target aspect ratio. An image that already has it is rejected with 400.
ratio_9_16, ratio_1_1, ratio_16_9 ID of an image in your media library. Required unless imageUrl is given. When both are sent, media is used. A media that is not COMPLETED yet (an import or upload in progress) answers 400 media: Media "<id>" is not ready (status PROCESSING).
Public URL of a JPEG, PNG or WEBP image (up to 25 MB). It is imported into your library first, so the result is attached to a media you own. Required unless media is given.
What the new area should show (up to 500 characters). Without it the scene is continued from the image itself.
500