# Resize an ad into other sizes

Send one finished, flat ad and up to ten target sizes; get back one finished image per size, with the layout redone for each shape rather than cropped or stretched. Trained exclusively on licensed data for safe commercial use.
**Input assumptions**
The input is assumed to be a flat ad. PSD and Figma files are not inputs. The job is asynchronous only; `"sync": true` is rejected with `400`. On plans other than Enterprise, a source over 1350 px on either side is downscaled to fit before resizing, the outputs still come back at the requested sizes, and `result.text` says so. Submissions are not idempotent: a retried POST is a new job. Failed jobs are never billed.
**Reporting a problem**
The `request_id` in an error body identifies that response, not your job. The durable handle is the id inside the `status_url` you received at submit; quote that.
Ad Resize takes one finished, flat ad and returns it at other sizes, with the layout redone for each shape rather than cropped or stretched. Send the image and a list of target sizes; get one finished image per size back. Use it to turn a single creative into every placement a campaign needs, from a square feed post to a leaderboard, without a design file.
| You send | You get back |
|  --- | --- |
| One flat ad image (PNG or JPEG) and up to 10 target sizes, each with a name and a width and height in pixels | One hosted image per target size, at exactly the pixels requested, laid out again from the ad's layers |

### How a resize job runs
1. **Submit** the ad and its target sizes. The API returns a `request_id` and a `status_url` immediately.
2. **Wait** for completion by polling the `status_url` (the shared [status endpoint](/status)) every few seconds, or pass a `webhook_url` and let Bria POST the result to you ([webhooks](/getting-started/async-requests#webhooks)). Both deliver the same body.
3. **Download the images.** The completed status body carries `result.results`, one entry per target size, each with a hosted `url`.

Every size is laid out again from the ad's layers, so a call takes several minutes and finishes when its slowest size does. Set a poll timeout of at least 20 minutes.
**Python SDK**

```python
from bria_client import BriaSyncClient

client = BriaSyncClient()  # reads BRIA_API_TOKEN

job = client.submit(
    endpoint="ads/resize",
    payload={
        "attachments": ["https://example.com/ads/spring-sale.jpg"],
        "formats": [
            {"name": "feed", "width": 1080, "height": 1080},
            {"name": "story", "width": 1080, "height": 1920},
            {"name": "leaderboard", "width": 970, "height": 90},
        ],
    },
)
status = client.poll(job, interval=10, timeout=1200)

for target in status.result.results:
    print(target["name"], target["status"], target["strategy"], target["url"])
```
**TypeScript SDK**

```typescript
import { BriaClient } from "@bria-ai/client";

const client = new BriaClient(); // reads BRIA_API_TOKEN

// 1 + 2. Submit and wait
const job = await client.submit("ads/resize", {
  attachments: ["https://example.com/ads/spring-sale.jpg"],
  formats: [
    { name: "feed", width: 1080, height: 1080 },
    { name: "story", width: 1080, height: 1920 },
    { name: "leaderboard", width: 970, height: 90 },
  ],
});
const status = await client.poll(job, { interval: 10, timeout: 1200 });

// 3. One entry per target size
type Target = { name: string; status: string; strategy: string | null; url: string | null; error: string | null };
for (const target of (status.result?.results ?? []) as Target[]) {
  console.log(target.name, target.status, target.strategy, target.url);
}
```
**cURL**

```bash
curl -s -X POST https://engine.prod.bria-api.com/v2/ads/resize \
  -H "api_token: $BRIA_API_TOKEN" -H "Content-Type: application/json" \
  -d '{
    "attachments": ["https://example.com/ads/spring-sale.jpg"],
    "formats": [
      {"name": "feed", "width": 1080, "height": 1080},
      {"name": "story", "width": 1080, "height": 1920},
      {"name": "leaderboard", "width": 970, "height": 90}
    ]
  }'

curl -s "https://engine.prod.bria-api.com/v2/status/<request_id>" -H "api_token: $BRIA_API_TOKEN"

curl -s "https://engine.prod.bria-api.com/v2/status/<request_id>" -H "api_token: $BRIA_API_TOKEN" \
  | jq '.result.results[] | {name, status, strategy, url}'
```
The completed status body looks like this:

```json
{
  "request_id": "340780795e1b4530a5c7ee4563989f6f",
  "status": "COMPLETED",
  "result": {
    "status": "completed",
    "results": [
      {
        "name": "feed",
        "width": 1080,
        "height": 1080,
        "status": "ok",
        "strategy": "delayer_dispatch",
        "url": "https://editor-media.bria.ai/inventory/.../assets/7b4549c24686472bac04bcfd023a1f0e.png",
        "error": null
      },
      {
        "name": "leaderboard",
        "width": 970,
        "height": 90,
        "status": "ok",
        "strategy": "delayer_dispatch",
        "url": "https://editor-media.bria.ai/inventory/.../assets/860865425a3f4d349dc79d8826e0c2b4.png",
        "error": null
      }
    ]
  }
}
```
Each target is independent: one can be `"status": "failed"` with an `error` while the others carry a `url`. Check every entry rather than the job as a whole.
### How a size is made
Every size is produced the same way: the [Ad Delayer](/ads) separates the ad into layers, Bria's layout engine composes them for the new shape, and the text stays text. The `strategy` field on each result names that route, `delayer_dispatch`. It takes several minutes per call, and every image is Bria-made.
An optional `prompt` steers the layout, for example `"keep the logo in the top-left corner"`.
### Resize request essentials
| Parameter | Description |
|  --- | --- |
| `attachments` | Exactly one image: a public URL or a `data:image/…;base64,…` URI. PNG or JPEG. Required. |
| `formats` | One to ten target sizes. Each has a `name` (echoed back on its result), and a `width` and `height` in pixels, both greater than zero. Required. |
| `prompt` | Optional guidance for how the ad should adapt to each size. |
| `webhook_url` | Optional. Receive the completion body by POST instead of polling. |

The job is asynchronous only. A request with `"sync": true` is rejected with `400`, because a layered target can run for minutes. Inputs are assumed to be flat ads; PSD and Figma files are not inputs. Submissions are not idempotent: a retried POST is a new job. Failed jobs are never billed.
### Source resolution
On plans other than Enterprise, a source over 1350 px on either side is downscaled to fit 1350 px before resizing. The outputs still come back at the exact sizes requested, and the completed body carries a `result.text` note saying the source was downscaled. Enterprise accounts resize at full source resolution and receive no note. [Contact us](https://bria.ai/contact-us) about Enterprise.
### Limits
|  |  |
|  --- | --- |
| Target sizes | 1 to 10 per call, any width and height in pixels |
| Input format | PNG or JPEG, one image per call |
| Source resolution | Full resolution on Enterprise; downscaled to 1350 px per side on other plans, with a note in the response |
| Billing | Failed jobs are not billed |

### Use resize from an agent
The hosted and local [MCP server](/mcp-authentication) exposes this capability as the `ad_resize` tool; agents that write code can use the [Python SDK](/integration-methods/python-sdk) as shown above. To make the ad editable first, or to change its copy before resizing, run the [Ad Delayer](/ads).

Endpoint: POST /ads/resize
Security: api_token

## Request fields (application/json):

  - `attachments` (array, required)
    Exactly one image: a publicly reachable URL, or a `data:image/…;base64,…` URI. PNG or JPEG. More than one entry, or a value that is neither a URL nor base64, is rejected with 422. This parameter is required.

  - `formats` (array, required)
    One to ten target sizes. One result comes back per entry, carrying the same `name`. This parameter is required.

  - `formats.name` (string, required)
    Label for this target, echoed back on its result.
    Example: feed

  - `formats.width` (integer, required)
    Target width in pixels.
    Example: 1080

  - `formats.height` (integer, required)
    Target height in pixels.
    Example: 1080

  - `prompt` (string)
    Optional guidance for how the ad should adapt to each size, for example "keep the logo in the top-left corner".

  - `webhook_url` (string)
    Optional URL that receives the completion payload by POST when the job reaches a terminal state, with the same body as a status poll. See [webhooks](/getting-started/async-requests#webhooks).

## Response 202 fields (application/json):

  - `request_id` (string, required)
    Example: 860f1a2b73f847e59f284d6f860f2ddb

  - `status_url` (string, required)
    Example: https://engine.prod.bria-api.com/v2/status/860f1a2b73f847e59f284d6f860f2ddb

## Response 400 fields (application/json):

  - `error` (object, required)

  - `error.code` (integer, required)
    Example: 422

  - `error.message` (string, required)
    Example: Unprocessable Entity

  - `error.details` (string, required)
    Example: ['Invalid Input -> only one image URL is supported per request']

  - `request_id` (string, required)
    Identifies this response, not your job; the durable handle is the id inside the `status_url` you received at submit.
    Example: 3a51f1c2f0de4b6c9a3e2b41a7c9d0e8

## Response 401 fields (application/json):

  - `error` (object, required)

  - `error.code` (integer, required)
    Example: 422

  - `error.message` (string, required)
    Example: Unprocessable Entity

  - `error.details` (string, required)
    Example: ['Invalid Input -> only one image URL is supported per request']

  - `request_id` (string, required)
    Identifies this response, not your job; the durable handle is the id inside the `status_url` you received at submit.
    Example: 3a51f1c2f0de4b6c9a3e2b41a7c9d0e8

## Response 413 fields (application/json):

  - `error` (object, required)

  - `error.code` (integer, required)
    Example: 422

  - `error.message` (string, required)
    Example: Unprocessable Entity

  - `error.details` (string, required)
    Example: ['Invalid Input -> only one image URL is supported per request']

  - `request_id` (string, required)
    Identifies this response, not your job; the durable handle is the id inside the `status_url` you received at submit.
    Example: 3a51f1c2f0de4b6c9a3e2b41a7c9d0e8

## Response 422 fields (application/json):

  - `error` (object, required)

  - `error.code` (integer, required)
    Example: 422

  - `error.message` (string, required)
    Example: Unprocessable Entity

  - `error.details` (string, required)
    Example: ['Invalid Input -> only one image URL is supported per request']

  - `request_id` (string, required)
    Identifies this response, not your job; the durable handle is the id inside the `status_url` you received at submit.
    Example: 3a51f1c2f0de4b6c9a3e2b41a7c9d0e8

## Response 429 fields (application/json):

  - `error` (object, required)

  - `error.code` (integer, required)
    Example: 422

  - `error.message` (string, required)
    Example: Unprocessable Entity

  - `error.details` (string, required)
    Example: ['Invalid Input -> only one image URL is supported per request']

  - `request_id` (string, required)
    Identifies this response, not your job; the durable handle is the id inside the `status_url` you received at submit.
    Example: 3a51f1c2f0de4b6c9a3e2b41a7c9d0e8

## Response 5XX fields (application/json):

  - `error` (object, required)

  - `error.code` (integer, required)
    Example: 422

  - `error.message` (string, required)
    Example: Unprocessable Entity

  - `error.details` (string, required)
    Example: ['Invalid Input -> only one image URL is supported per request']

  - `request_id` (string, required)
    Identifies this response, not your job; the durable handle is the id inside the `status_url` you received at submit.
    Example: 3a51f1c2f0de4b6c9a3e2b41a7c9d0e8

