# Asset transformations

API reference for asset transformations in Hygraph, including image, document, and file type transformations, transformation safeguards, and error responses.

When fetching assets, you can pass an optional `transformation` argument to the `url` field. Hygraph enforces [transformation safeguards](#transformation-safeguards) on requests and capacity. Breaches return `400 Bad Request` or `429 Too Many Requests`, depending on the safeguard.

**Note:**

If you want to find out which asset system your project uses and which section of this document applies to you, [click here](https://hygraph.com/docs/api-reference/assets/assets-overview#which-asset-system-does-my-project-use).

## Hygraph Asset Management

### Auto image

This document transformation uses the `auto_image` parameter to determine which [mimetype](https://hygraph.com/docs/api-reference/assets/transformations#file-type-conversion) to serve based on the browsers preference.

| Arg          | Description                                                                                                                                                                                                         |
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `auto_image` | Determines which mimetype to serve based on the browsers preference. We either send back a `webp` or an `avif`, depending on the **Accept** request header. Without an **Accept** header, we use `jpg` as fallback. |

For example:

  **Query**

```graphql
{
  assets {
    url(
      transformation: {
        document: {output: {format: autoImage}}
        }
      )
  }
}
```

  
  **Response**

```graphql
{
  "data": {
    "assets": [
      {
        "url": "https://REGION.graphassets.com/ENV_ID/auto_image/HANDLE"
      }
    ]
  }
}
```


### Blur

This transformation uses the `blur` parameter to blur your image.

| Arg      | Description                                                        |
| -------- | ------------------------------------------------------------------ |
| `amount` | Blur effect amount. The value must be an integer from `0` to `20`. |

For example, we can query all assets, and blur images to an amount of 4:

  **Query**

```graphql
{
  assets {
    url(
      transformation: {
        image: { blur: {amount: 4} }
      }
    )
  }
}
```

  
  **Response**

```graphql
{
  "data": {
    "assets": [
      {
        "url": "https://REGION.graphassets.com/ENV_ID/blur=amount:4/HANDLE"
      }
    ]
  }
}
```


### Border

This transformation uses the `border` parameter to control the width, color and background of your image borders.

| Arg          | Description                                                                                                                    |
| ------------ | ------------------------------------------------------------------------------------------------------------------------------ |
| `width`      | Border width. The value must be an integer from `1` to `1000`.                                                                 |
| `color`      | Border color. The value must be a hexadecimal or shortened hexadecimal color code.                                             |
| `background` | Border color. The value must be a hexadecimal or shortened hexadecimal color code, or a supported color name. [Here's a list of color names and their corresponding HEX values](https://hygraph.com/docs/api-reference/assets/border-hex-values) that you can use for your image borders. |

For example, we can query all assets, and set the image borders using our chosen characteristics:

  **Query**

```graphql
{
  assets {
    url(
      transformation: {
        image: { border: {width: 2, color: "gray15", background: "azure"} }
      }
    )
  }
}
```

  
  **Response**

```graphql
{
  "data": {
    "assets": [
      {
        "url": "https://REGION.graphassets.com/ENV_ID/border=width:2,color:gray15,background:azure/HANDLE"
      }
    ]
  }
}
```


### Compress

This transformation compresses PNG & JPG files.

| Arg        | Description                                                                                      |
| ---------- | ------------------------------------------------------------------------------------------------ |
| `compress` | Image compression. You can choose to compress the metadata as well by passing `true` or `false`. |

**Warning:**
- `compress` will only work on `jpg` and `png` file formats, otherwise it returns unchanged.
- Make compress the last transformation in your chain for better results.

For example, we can query all assets, and compress images along with their metadata:

  **Query**

```graphql
{
  assets {
    url(
      transformation: {
        image:{compress:{metadata:true}}
      }
    )
  }
}
```

  
  **Response**

```graphql
{
  "data": {
    "assets": [
      {
        "url": "https://REGION.graphassets.com/ENV_ID/compress=metadata:true/HANDLE"
      }
    ]
  }
}
```


### Crop

This transformation uses the `crop` parameter to crop images by entering coordinates and crop dimensions:

- The starting points for `X` and `Y` coordinates are [0,0], aligning with the top-left corner of the image.
- The `width` and `height` parameters determine the size in pixels of the cropping rectangle. The output will include only the portion of the image within the designated crop area.

| Arg      | Description                                                                                    |
| -------- | ---------------------------------------------------------------------------------------------- |
| `x`      | The `x` coordinate of the image. The value must be an integer from `0` to `10000`.             |
| `y`      | The `y` coordinate of the image. The value must be an integer from `0` to `10000`.             |
| `width`  | The width in pixels to resize the image to. The value must be an integer from `1` to `10000`.  |
| `height` | The height in pixels to resize the image to. The value must be an integer from `1` to `10000`. |

For example, we can query all assets, and crop images:

  **Query**

```graphql
{
  assets {
    url(
      transformation: {
        image:{crop:{ x: 100, y: 200, width:300, height: 400 }}
      }
    )
  }
}
```

  
  **Response**

```graphql
{
  "data": {
    "assets": [
      {
        "createdAt": "2024-01-19T13:56:45.723049+00:00",
        "url": "https://REGION.graphassets.com/ENV_ID/crop=dim:[100,200,300,400]/HANDLE"
      }
    ]
  }
}
```


### Quality

Use the `quality` parameter to set the quality of your image without risking generating a larger file.

| Arg     | Description                                                                     |
| ------- | ------------------------------------------------------------------------------- |
| `value` | The quality value of the image. The value must be an integer from `1` to `100`. |

**Note:**
- Only supported for the following formats: `jpeg`, `jpg`, `webp`, `gif`, `heif`, `tiff`, `avif`
- You can use this transformation to reduce the file size of your image before a compress task.

For example, we can query all assets, and set the image quality to 50:

  **Query**

```graphql
{
  assets {
    url(
      transformation: {
        image: { quality: {value: 50} }
      }
    )
  }
}
```

  
  **Response**

```graphql
{
  "data": {
    "assets": [
      {
        "url": "https://REGION.graphassets.com/ENV_ID/quality=value:50/HANDLE"
      }
    ]
  }
}
```


### Resize

This transformation uses the `resize` parameter to adjust the image `height`, `width` and `fit`.

The `image` takes the following arguments:

| Arg      | Type       | Description                                                                                                   |
| -------- | ---------- | ------------------------------------------------------------------------------------------------------------- |
| `width`  | `Int`      | The width in pixels to resize the image to. The value must be an integer from `1` to `10000`.                 |
| `height` | `Int`      | The height in pixels to resize the image to. The value must be an integer from `1` to `10000`.                |
| `fit`    | `ImageFit` | The default value for this parameter is `clip`. Check the following table for all possible values. |

The `ImageFit` takes one of the following values:

| Value   | Description                                                                                                                                                                                          |
| ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `clip`  | Resizes the image to fit within the specified parameters without distorting, cropping, or changing the aspect ratio.                                                                                 |
| `crop`  | Resizes the image to fit the specified parameters exactly by removing any parts of the image that don't fit within the boundaries.                                                                   |
| `scale` | Resizes the image to fit the specified parameters exactly by scaling the image to the desired size. The aspect ratio of the image is not respected and the image can be distorted using this method. |
| `max`   | Resizes the image to fit within the parameters, but as opposed to `fit:clip` will not scale the image if the image is smaller than the output size.                                                  |

**Note:**
Only supported for the following formats: `jpeg`, `jpg`, `png`, `gif`, `bmp` , `tiff`, `webp`, `avif`

For example, we can query all assets, and resize images:

  **Query**

```graphql
{
  assets {
    url(
      transformation: {
        image: { resize: { width: 50, height: 50, fit: clip } }
      }
    )
  }
}
```

  
  **Response**

```graphql
{
  "data": {
    "assets": [
      {
        "url": "https://REGION.graphassets.com/ENV_ID/resize=fit:clip,height:50,width:50/HANDLE"
      }
    ]
  }
}
```


### Sharpen

This transformation uses the `sharpen` parameter to sharpen your image.

| Arg      | Description                                                           |
| -------- | --------------------------------------------------------------------- |
| `amount` | Sharpen effect amount. The value must be an integer from `0` to `20`. |

For example, we can query all assets, and sharpen images to an amount of 2:

  **Query**

```graphql
{
  assets {
    url(
      transformation: {
        image: { sharpen: {amount: 2} }
      }
    )
  }
}
```

  
  **Response**

```graphql
{
  "data": {
    "assets": [
      {
        "url": "https://REGION.graphassets.com/ENV_ID/sharpen=amount:2/HANDLE"
      }
    ]
  }
}
```


### File type conversion

The following table shows the original file type you'd be changing from, and the possible formats you'd change it into.

| Input type | Available output formats                           | Mimetype          |
| ---------- | -------------------------------------------------- | ----------------- |
| `AVIF`     | `JPG`, `PNG`, `WEBP`, `GIF`, `TIFF`                | `image/avif`      |
| `BMP`      | `JPG`, `PNG`, `SVG`, `WEBP`, `GIF`, `TIFF`, `AVIF` | `image/bmp`       |
| `GIF`      | `JPG`, `PNG`, `SVG`, `WEBP`, `TIFF`, `AVIF`        | `image/gif`       |
| `JPG`      | `PNG`, `SVG`, `WEBP`, `GIF`, `TIFF`, `AVIF`        | `image/jpg`       |
| `PDF`      | `JPG`, `GIF`, `WEBP`, `TIFF`, `AVIF`               | `application/pdf` |
| `PNG`      | `JPG`, `SVG`, `WEBP`, `GIF`, `TIFF`, `AVIF`        | `image/png`       |
| `SVG`      | `JPG`, `PNG`, `WEBP`, `GIF`, `TIFF`, `AVIF`        | `image/svg+xml`   |
| `TIFF`     | `JPG`, `PNG`, `SVG`, `WEBP`, `GIF`, `AVIF`         | `image/tiff`      |
| `WEBP`     | `JPG`, `PNG`, `SVG`, `GIF`, `TIFF`, `AVIF`         | `image/webp`      |

For example, we can query all assets, and convert the images to JPG:

  **Query**

```graphql
query Assets {
  assets {
    createdAt
    url(transformation: {document: {output: {format: jpg}}})
  }
}
```

  
  **Response**

```graphql
{
  "data": {
    "assets": [
      {
        "createdAt": "2024-01-19T13:56:45.723049+00:00",
        "url": "https://REGION.graphassets.com/ENV_ID/output=format:jpg/HANDLE"
      }
    ]
  }
}
```


### URL transformations

It is possible to do URL transformations without using the API. You can do this by using the regular URL of an asset and then placing the transformation syntax in it.

You would place the transformation here:

```
https://REGION.graphassets.dev/ENV_ID/<Transformations go here>/HANDLE
```

The following example uses `resize`:

```
https://REGION.graphassets.dev/ENV_ID/resize=width:400,height:400/HANDLE
```

You can also chain the transformations. The following example uses `resize` and `sharpen`:

```
https://REGION.graphassets.dev/ENV_ID/resize=width:400,height:400/sharpen=amount:2/HANDLE
```

## Transformation safeguards

Hygraph enforces safeguards on asset transformations to keep image processing fast and reliable for every project. Safeguards fall into two groups:

- **Request safeguards** validate what a transformation URL asks for: output dimensions, multi-page output, and how many times an operation may repeat. Breaches return `400 Bad Request` with a body beginning `"Asset transformation safeguard exceeded:"`.
- **Capacity safeguards** bound the work a transformation costs to produce: the resolution of the source asset, and how many *new* transformations a project may generate per second.

| Safeguard | Limit | Applies to | Response |
| --- | --- | --- | --- |
| Resize width / height | `10000` px each | Requested output | `400` |
| Multi-page output width / height | `2000` px each | Requested output | `400` |
| Multi-page upscaling factor | `3.0x` | Requested output | `400` |
| Repetitions per transformation kind | `3` | Transformation URL | `400` |
| Source resolution | `100` MP (`100,000,000` px) | Source asset | `400` |
| New transformations per second | `40` RPS shared / `80` RPS dedicated | Uncached transformations, per environment | `429` |

Safeguards apply to both GraphQL `url(transformation: {...})` queries and direct URL transformations. Transformations already generated and cached are unaffected by capacity safeguards.

### Resize safeguards

`resize.width` and `resize.height` are each capped at `10000` px. The value `10000` is accepted, any value above `10000` is rejected.

For example, `resize=width:15000` returns `Asset transformation safeguard exceeded: resize width 15000 must not exceed 10000`

### Multi-page asset safeguards

Multi-page assets include animated image formats and multi-page documents such as animated GIF, animated WebP, APNG, animated AVIF/HEIC/HEIF, multi-page TIFF, and PDF.

The following safeguards apply to all multi-page assets:

| Safeguard          | Value    |
| ------------------ | -------- |
| Maximum output width  | `2000` px |
| Maximum output height | `2000` px |
| Maximum upscaling factor | `3.0x` |

A request must satisfy all three safeguards. A request within the `2000` px dimension safeguards can still be rejected if it upscales the original by more than `3.0x`.

Example response bodies:
- `Asset transformation safeguard exceeded: multi-page width 3000 must not exceed 2000 for gif`
- `Asset transformation safeguard exceeded: multi-page upscaling factor 15.00 exceeds 3.00 for avif`

### Per-transformation repetition cap

Each transformation kind can appear at most `3` times in a single transformation URL. This safeguard is per kind, not per total transformation count.

The counted kinds are: `resize`, `output`, `blur`, `sharpen`, `border`, `quality`, `crop`, `compress`, and `cache`. 

A URL containing four `resize` transformations returns `Asset transformation safeguard exceeded: transformation kind - resize with value 4 exceeded its default limit: 3`

### Source resolution

A transformation decodes the entire source image into memory before touching it. File size is a poor predictor of that cost: a few-megabyte file can decode into a gigapixel image, which is not always intuitive the first time a small-looking upload trips this limit. Bounding resolution, rather than file size, is what reliably bounds the memory a single request can consume.

So, Hygraph caps the resolution of the **source image** a transformation may be applied to at **100 megapixels** (100,000,000 pixels, for example 10,000 × 10,000). Resolution is measured as `width × height` of the decoded image, per page. 

Your plan's asset upload limit governs how large a file you can **store**. The source resolution cap governs what the image pipeline will transform.

| Action | Asset above the source resolution cap |
| --- | --- |
| Uploading the asset | Allowed, up to your plan's upload limit |
| Serving or downloading the original | Works normally |
| Requesting a transformation of it | Returns `400 Bad Request` |

If your plan allows, you can still store, version, publish, and download assets above the source resolution cap at their original URL. But you cannot transform these assets.

For example, requesting a transformation of a 15,000 × 12,000 source image (180 MP) returns `Asset transformation safeguard exceeded: source image too large to transform: 180000000 pixels exceeds the 100000000 pixel limit`

Animated and multi-page assets are measured one page at a time, so frame count doesn't count against the cap. Such assets are bounded separately by the [multi-page safeguards](#multi-page-asset-safeguards).

#### Working with large source assets
 
- **Resize once, at ingest.** Store a web-scale master and keep the full-resolution file in your DAM or archive.
- **Serve the original where the full resolution matters.** The unmodified asset URL has no resolution cap. You can use it for download links, print assets, and archival access.
- **Re-upload a downscaled version** if you need transformations of an existing over-limit asset.

### Transformation rate limit

Generating a transformation that does not exist yet is a compute-intensive operation. To protect throughput across all projects, Hygraph applies a **requests-per-second (RPS) limit on new transformations**, counted per project environment.

| Deployment | New transformations per second, per environment |
| --- | --- |
| Shared clusters | 40 RPS |
| Dedicated clusters | 80 RPS |

The limit applies **only** when Hygraph has to generate a transformation it has not produced before. It does not apply to:

- **Cached transformations**: Any transformation already generated is served from the CDN with no limit and no rate-limit accounting.
- **Original asset requests**: Fetching the unmodified asset is never rate-limited.

In practice, this means production traffic is effectively unlimited: a page serving the same set of image variants to thousands of visitors generates one transformation per variant, then serves cached results indefinitely.

Unlike the request safeguards, a rate-limited response body does not begin with `"Asset transformation safeguard exceeded:"`. Detect it by the `429` status code.

For example, when an environment exceeds its allowance for new transformations, it returns `429 Too Many Requests` with a `Retry-After` header.

The `Retry-After` header gives the number of seconds to wait before retrying. The limit is measured over a one-second window, so allowances recover immediately. A short backoff and retry is the correct client behavior.

#### Staying within the limit

The limit counts *distinct new* transformations. Increase your cache hit rate to avoid being rate-limited.

- **Reuse a fixed set of variants.** Pick the sizes your layout needs (thumbnail, card, hero, and their 2× versions) and reuse those exact URLs everywhere. Repeat requests hit the cache.
- **Don't size images from runtime values.** Viewport width, device pixel ratio, or container size produce a near-unique URL per visitor, so each one triggers a new transformation. Use a fixed set of breakpoints instead.
- **Warm variants before a launch.** Request each new variant once, before a campaign, migration, or bulk import goes live. Don't let peak traffic generate them for the first time.
- **Throttle bulk jobs.** When transforming a whole asset library, limit concurrency and retry on `429` instead of firing every request at once.

## Validate transforms

We provide a validation field you can enable to check the combination of transform arguments are valid.

For example, you may query a video, and request to change the file type to PDF. `validateOptions: true` will warn you that this is not allowed.

```graphql
{
  assets {
    url(
      transformation: {
        document: { output: { format: pdf } }
        validateOptions: true
      }
    )
  }
}
```

## Combine transforms

It is possible to combine both transformation arguments:

```graphql
{
  assets {
    url(
      transformation: {
        image: { resize: { width: 50, height: 50, fit: clip } }
        document: { output: { format: png } }
        validateOptions: true
      }
    )
  }
}
```

## Alias transforms

[GraphQL aliases](https://hygraph.com/docs/api-reference/content-api/queries#combining-queries) are great for querying the same asset URL with multiple transformations.

For example, you could transform product images to include a `thumbnail`.

```graphql
{
  products {
    images {
      thumbnail: url(
        transformation: {
          image: { resize: { width: 50, height: 50, fit: clip } }
          document: { output: { format: png } }
        }
      )
      url(transformation: { document: { output: { format: png } } })
    }
  }
}
```

## Legacy asset system

### Resize images

The `image` takes the following arguments:

| Arg      | Type       | Description                                                                                    |
| -------- | ---------- | ---------------------------------------------------------------------------------------------- |
| `width`  | `Int`      | The width in pixels to resize the image to. The value must be an integer from `1` to `10000`.  |
| `height` | `Int`      | The height in pixels to resize the image to. The value must be an integer from `1` to `10000`. |
| `fit`    | `ImageFit` | The default value for the fit parameter is `clip`.                                             |

The `ImageFit` takes one of the following values:

| Value   | Description                                                                                                                                                                                          |
| ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `clip`  | Resizes the image to fit within the specified parameters without distorting, cropping, or changing the aspect ratio.                                                                                 |
| `crop`  | Resizes the image to fit the specified parameters exactly by removing any parts of the image that don't fit within the boundaries.                                                                   |
| `scale` | Resizes the image to fit the specified parameters exactly by scaling the image to the desired size. The aspect ratio of the image is not respected and the image can be distorted using this method. |
| `max`   | Resizes the image to fit within the parameters, but as opposed to `fit:clip` will not scale the image if the image is smaller than the output size.                                                  |

For example, we can query all assets, and resize images:

```graphql
{
  assets {
    url(
      transformation: {
        image: { resize: { width: 50, height: 50, fit: clip } }
      }
    )
  }
}
```

### Convert file type

Depending on the asset type you're dealing with, it's possible to transform the `output` to another file type by passing a `format` value.

| Current file type | Available output formats                                         |
| ----------------- | ---------------------------------------------------------------- |
| PDF               | `jpg`, `odp`, `ods`, `odt`, `png`, `svg`, `txt`, `webp`          |
| DOC               | `docx`, `html`, `jpg`, `odt`, `pdf`, `png`, `svg`, `txt`, `webp` |
| DOCX              | `doc`, `html`, `jpg`, `odt`, `pdf`, `png`, `svg`, `txt`, `webp`  |
| ODT               | `doc`, `docx`, `html`, `jpg`, `pdf`, `png`, `svg`, `txt`, `webp` |
| XLS               | `jpg`, `pdf`, `ods`, `png`, `svg`, `xlsx`, `webp`                |
| XLSX              | `jpg`, `pdf`, `ods`, `png`, `svg`, `xls`, `webp`                 |
| ODS               | `jpg`, `pdf`, `png`, `xls`, `svg`, `xlsx`, `webp`                |
| PPT               | `jpg`, `odp`, `pdf`, `png`, `svg`, `pptx`, `webp`                |
| PPTX              | `jpg`, `odp`, `pdf`, `png`, `svg`, `ppt`, `webp`                 |
| ODP               | `jpg`, `pdf`, `png`, `ppt`, `svg`, `pptx`, `webp`                |
| BMP               | `jpg`, `odp`, `ods`, `odt`, `pdf`, `png`, `svg`, `webp`          |
| GIF               | `jpg`, `odp`, `ods`, `odt`, `pdf`, `png`, `svg`, `webp`          |
| JPG               | `jpg`, `odp`, `ods`, `odt`, `pdf`, `png`, `svg`, `webp`          |
| PNG               | `jpg`, `odp`, `ods`, `odt`, `pdf`, `png`, `svg`, `webp`          |
| WEBP              | `jpg`, `odp`, `ods`, `odt`, `pdf`, `png`, `svg`, `webp`          |
| TIFF              | `jpg`, `odp`, `ods`, `odt`, `pdf`, `png`, `svg`, `webp`          |
| AI                | `jpg`, `odp`, `ods`, `odt`, `pdf`, `png`, `svg`, `webp`          |
| PSD               | `jpg`, `odp`, `ods`, `odt`, `pdf`, `png`, `svg`, `webp`          |
| SVG               | `jpg`, `odp`, `ods`, `odt`, `pdf`, `png`, `webp`                 |
| HTML              | `jpg`, `odt`, `pdf`, `svg`, `txt`, `webp`                        |
| TXT               | `jpg`, `html`, `odt`, `pdf`, `svg`, `webp`                       |

For example, let's transform all assets to PDFs:

```graphql
{
  assets {
    url(transformation: { document: { output: { format: pdf } } })
  }
}
```
