# Signed image URLs

Generate images from HTML/CSS, URLs, or templates in a single request with signed URLs.

When to use signed URLs

Use signed URLs when an image should render on demand from a URL, without a separate create request. Generate the URL on your server or at build time, then use it in your page. For a create-then-fetch workflow, use the [standard API endpoint](/getting-started/using-the-api/).

Publishing social cards?

If each image corresponds to a public page on your website or CMS, an [OG Image Config](/getting-started/og-images/) maps page paths to stable image URLs without an HMAC token for every page.

## Key benefits

This endpoint allows you to generate image URLs that directly render images when accessed:

*   **No POST request needed**: Generate signed URLs locally on your server or during a build, without an API call
*   **Browser-ready URLs**: Embed the finished URL without exposing your API Key
*   **On-demand rendering**: The first image request triggers generation
*   **Template ready**: Create reusable signed image URLs from template values

Unlike the standard endpoint that requires a POST request followed by using the returned URL, this endpoint lets you construct a signed URL that will generate and return the image when accessed.

Each API key has an associated API ID (public) and API Key (secret). The token is generated by creating an HMAC SHA256 hash of the query string (without the `?`) using your API Key as the secret. For HTML/CSS and URL renders, the signed URL includes your API ID. For templated image URLs, the signed URL uses the `template_id` instead.

Keep signing on your server

Never expose your API Key in client-side code. The finished signed URL can be used in the browser; anyone with that URL can request the image it authorizes.

## Creating an image

To generate an image with a signed URL, construct a URL with your API ID and token:

`GET https://hcti.io/v1/image/create-and-render/:api_id/:token/:format`

### URL components

| Component | Description |
| --- | --- |
| `api_id` | Your public API ID from the dashboard |
| `token` | HMAC SHA256 hash of the query string using your API Key (see below for how to generate) |
| `format` | Optional file format: `png` (default), `jpg`, `webp`, or `pdf` |

### Parameters

The parameters are the same as the [standard API endpoint](/getting-started/using-the-api/#parameters), but they must be passed as query parameters in the URL.

| Name | Type | Description |
| --- | --- | --- |
| `html`† | `String` | This is the HTML you want to render. You can send an HTML snippet (`<div>Your content</div>`) or an entire webpage. |
| `css` | `String` | The CSS for your image. When using with `url` it will be injected into the page. |
| `url`† | `String` | The fully qualified URL to a public webpage. When passed this will override the html param and will generate a screenshot of the url. |

Required parameters

† Provide either `url` or `html`. `css` is optional.

### Additional parameters

Optional parameters for greater control over your image.

| Name | Type | Description |
| --- | --- | --- |
| [`additional_header_origins`](/parameters/headers/#additional-header-origins) | `Array` | Allow custom `headers` on requests to specific additional HTTP or HTTPS origins. |
| [`block_consent_banners`](/guides/advanced/blocking-cookie-banners/) | `Boolean` | When set to `true`, automatically blocks cookie consent banners and popups on websites. Most useful for URL screenshots. |
| [`color_scheme`](/parameters/color_scheme/) | `String` | Set Chrome to render in `light` or `dark` mode. Affects websites using `prefers-color-scheme`. |
| [`device_scale`](/parameters/device_scale/) | `Double` | Control resolution by adjusting the pixel ratio from `0.1` to `3`. Higher values increase image quality and file size. |
| [`disable_twemoji`](/guides/debugging/emoji/#disabling-twemoji) | `Boolean` | Set to `true` to use native emoji fonts instead of Twemoji. |
| [`full_screen`](/parameters/full_screen/) | `Boolean` | Generate an image of the entire height of a URL page. |
| [`google_fonts`](/parameters/google_fonts/) | `String` | Load one or more Google fonts, such as `Roboto\|Open Sans`. |
| [`headers`](/parameters/headers/) | `Object` | Add custom HTTP headers when screenshotting a URL. Headers are restricted to the requested URL's origin and any `additional_header_origins`. |
| [`identify_as_hcti`](/parameters/identify_as_hcti/) | `Boolean` | Add `X-HCTI-SCREENSHOT: 1` to the top-level request when screenshotting a URL. |
| [`include_headers_on_subrequests`](/parameters/headers/#include-headers-on-subrequests) | `Boolean` | Also add custom `headers` to same-origin subrequests and subrequests matching `additional_header_origins`. |
| [`jumbo_max_height`](/guides/advanced/jumbo-images/) | `Integer` | Maximum output height in jumbo mode, up to `80,000` pixels. Must be set with `jumbo_max_width` and consumes additional image credits. |
| [`jumbo_max_width`](/guides/advanced/jumbo-images/) | `Integer` | Maximum output width in jumbo mode, up to `80,000` pixels. Must be set with `jumbo_max_height` and consumes additional image credits. |
| [`max_wait_ms`](/parameters/max_wait_ms/) | `Integer` | Set a maximum time limit from `500` to `10000` milliseconds for waiting before taking the screenshot. |
| [`media_type`](/parameters/media_type/) | `String` | Set Chrome to render using `screen` or `print` CSS media styles. |
| [`ms_delay`](/parameters/ms_delay/) | `Integer` | Delay before generating the image. Useful when waiting for JavaScript; start with `500` milliseconds. |
| [`proxy_id`](/parameters/proxy_id/) | `String` | Route outbound traffic through one of your organization's configured HTTP proxies. Available on the 10,000 images/month plan or higher. |
| [`render_when_ready`](/parameters/render_when_ready/) | `Boolean` | Wait to generate the image until JavaScript calls `ScreenshotReady()`. |
| [`selector`](/parameters/selector/) | `String` | Crop the image to an element matching this CSS selector, such as `section#complete-toolkit.container-lg`. |
| [`storage_destination_id`](/parameters/storage_destination_id/) | `String` | Save rendered files to one of your organization's configured storage destinations. Available on the 10,000 images/month plan or higher. |
| [`timezone`](/parameters/timezone/) | `String` | Set Chrome's timezone with an IANA identifier such as `America/New_York`. |
| [`transparent_background`](/parameters/transparent_background/) | `Boolean` | Set to `true` to render with a transparent background. |
| [`viewport_height`](/parameters/viewport/#viewport-width-and-height) | `Integer` | Set the height of Chrome's viewport. Both dimensions must be set when using either. |
| [`viewport_landscape`](/parameters/viewport/#viewport-landscape) | `Boolean` | Set Chrome's viewport to landscape mode. |
| [`viewport_mobile`](/parameters/viewport/#viewport-mobile) | `Boolean` | Set Chrome's viewport to emulate a mobile device. |
| [`viewport_touch`](/parameters/viewport/#viewport-touch) | `Boolean` | Set Chrome's viewport to support touch events. |
| [`viewport_width`](/parameters/viewport/#viewport-width-and-height) | `Integer` | Set the width of Chrome's viewport. Both dimensions must be set when using either. |

## Creating a templated image URL

To generate an image from a template with a signed URL, construct a URL with your `template_id` and token. You do not need to include your API ID in the path.

`GET https://hcti.io/v1/image/:template_id/:token/:format`

### URL components

| Component | Description |
| --- | --- |
| `template_id` | The template ID returned by the [template API](/getting-started/templates/#creating-a-template) |
| `token` | HMAC SHA256 hash of the query string using your API Key |
| `format` | Optional file format: `png` (default), `jpg`, `webp`, or `pdf`. |

### Parameters

Template values are passed as query string parameters. Each query parameter name maps to a variable in your template.

| Name | Type | Description |
| --- | --- | --- |
| `template values` | `String`, `Number`, `Boolean`, or `JSON` | Values for the variables in your template. For editor templates, see the [Variables guide](/template-editor/variables/). |
| `template_version` | `Integer` | Optional. Render a specific version of the template. Include this in the query string before generating the token. |

Nested objects and arrays should be serialized as JSON and URL encoded. The token must be generated from the exact encoded query string you put after `?`.

For example, these template values:

```json
{
  "title": "Launch",
  "author": {
    "name": "Jeff"
  }
}
```

Could be encoded as:

```plaintext
author=%7B%22name%22%3A%22Jeff%22%7D&title=%22Launch%22
```

The `author` value decodes to `{"name":"Jeff"}`. The `title` value decodes to `"Launch"`.

Use an official client

The [TypeScript](/example-code/typescript/), [.NET](/example-code/c/), [Python](/example-code/python/), and [PHP](/example-code/php/) clients include signed URL helpers so you do not need to hand-build the query string or HMAC token.

## Understanding HMAC authentication

HMAC (Hash-based Message Authentication Code) is a mechanism for calculating a message authentication code involving a hash function in combination with a secret key. In this API:

1.  The **message** is your query string (without the leading `?`), e.g., `html=%3Cdiv%3EHello%3C%2Fdiv%3E`
2.  The **secret key** is your API Key
3.  The **hash function** used is SHA-256
4.  The resulting **token** is used in the URL path to authenticate the request

This allows you to create signed URLs without exposing your API Key. If any part of the query string is changed without updating the token, the URL will be invalid. Query parameter order, encoding style, and whitespace all matter because the token is based on the exact query string.

### Testing token generation online

Use our [HMAC SHA-256 Generator](/hmac-generator/) to test token generation. It runs entirely in your browser; your API key and query string are never sent to our servers.

1.  Enter your query string (e.g., `html=%3Cdiv%3EHello%3C%2Fdiv%3E`) as the **String**
2.  Enter your API Key as the **Secret Key**
3.  Copy the generated lowercase hexadecimal token

For example:

*   Input String: `html=%3Cdiv%3EHello%3C%2Fdiv%3E`
*   Secret Key: `your-api-key-here`
*   Computed MAC: `ac5553c5a9031e09f4580101080045e7e4cbd1734aa1b53a94f1006c3496ca21`

Your final URL would be:

```plaintext
https://hcti.io/v1/image/create-and-render/your-api-id-here/ac5553c5a9031e09f4580101080045e7e4cbd1734aa1b53a94f1006c3496ca21/png?html=%3Cdiv%3EHello%3C%2Fdiv%3E
```

## Examples

### Official client helpers

The official clients generate the signed URL for you and keep the API Key on your server.

#### [TypeScript client](/example-code/typescript/#official-npm-client)

```typescript
import { HtmlCssToImageClient } from '@html-css-to-image/client';


const client = HtmlCssToImageClient.fromEnv();


const imageUrl = client.generateTemplatedImageUrl('t-b0354248-e7f6-4cca-81c6-2b4a70a16388', {
  title: 'Launch',
  author: { name: 'Avery' }
});
```

#### [.NET client](/example-code/c/#official-net-package)

```csharp
using HtmlCssToImage;
using HtmlCssToImage.Models;


var client = new HtmlCssToImageClient(
    new HttpClient(),
    new HtmlCssToImageOptions
    {
        ApiId = "your-api-id",
        ApiKey = "your-api-key"
    });


var imageUrl = client.CreateTemplatedImageUrl(
    "t-b0354248-e7f6-4cca-81c6-2b4a70a16388",
    new
    {
        title = "Launch",
        author = new { name = "Avery" }
    });
```

#### [Python client](/example-code/python/#official-pypi-client)

```python
from html_css_to_image import HtmlCssToImageClient


with HtmlCssToImageClient.from_env() as client:
    image_url = client.generate_templated_image_url_from_values(
        "t-b0354248-e7f6-4cca-81c6-2b4a70a16388",
        {
            "title": "Launch",
            "author": {"name": "Avery"},
        },
    )
```

#### [PHP client](/example-code/php/#official-composer-client)

```php
use HtmlCssToImage\HtmlCssToImageClient;


$client = HtmlCssToImageClient::fromEnvironment();


$imageUrl = $client->generateTemplatedImageUrlFromValues(
    't-b0354248-e7f6-4cca-81c6-2b4a70a16388',
    [
        'title' => 'Launch',
        'author' => ['name' => 'Avery'],
    ],
);
```

### JavaScript example

```javascript
const crypto = require('crypto');


function generateToken(queryString, apiKey) {
  return crypto
    .createHmac('sha256', apiKey)
    .update(queryString)
    .digest('hex');
}


const apiId = 'your_api_id_here';
const apiKey = 'your_api_key_here';
const format = 'png';


// Example 1: Using HTML and CSS
const params = new URLSearchParams({
  html: '<div>Hello World</div>',
  css: 'div{color:red}'
});
const queryString = params.toString();
const token = generateToken(queryString, apiKey);


// Generate the URL
const imageUrl = `https://hcti.io/v1/image/create-and-render/${apiId}/${token}/${format}?${queryString}`;


// Example 2: Using a URL parameter
const urlParams = new URLSearchParams({
  url: 'https://example.com'
});
const urlQueryString = urlParams.toString();
const urlToken = generateToken(urlQueryString, apiKey);


// Generate the URL for website screenshot
const screenshotUrl = `https://hcti.io/v1/image/create-and-render/${apiId}/${urlToken}/${format}?${urlQueryString}`;


// Example 3: Using a template
const templateId = 't-b0354248-e7f6-4cca-81c6-2b4a70a16388';
const templateValues = {
  title: 'Launch',
  author: { name: 'Avery' }
};


const templateParams = new URLSearchParams();
Object.keys(templateValues)
  .sort()
  .forEach((key) => {
    templateParams.append(key, JSON.stringify(templateValues[key]));
  });


const templateQueryString = templateParams.toString();
const templateToken = generateToken(templateQueryString, apiKey);
const templatedImageUrl = `https://hcti.io/v1/image/${templateId}/${templateToken}/${format}?${templateQueryString}`;


// Now these URLs can be used directly in an <img> tag or as a link
// <img src="imageUrl" alt="Generated image" />
```

### PHP example

```php
<?php
$apiId = 'your_api_id_here';
$apiKey = 'your_api_key_here';
$format = 'png';


// Example 1: Using HTML and CSS
// Create query string
$html = '<div>Hello from PHP</div>';
$css = 'div{color:blue;font-family:Arial}';
$queryString = http_build_query([
  'html' => $html,
  'css' => $css,
], '', '&', PHP_QUERY_RFC3986);


// Generate token
$token = hash_hmac('sha256', $queryString, $apiKey);


// Generate the URL
$imageUrl = "https://hcti.io/v1/image/create-and-render/$apiId/$token/$format?$queryString";
echo "Generated image URL: $imageUrl\n";


// Example 2: Using a URL parameter
$websiteUrl = 'https://example.com';
$urlQueryString = http_build_query([
  'url' => $websiteUrl,
], '', '&', PHP_QUERY_RFC3986);
$urlToken = hash_hmac('sha256', $urlQueryString, $apiKey);


// Generate the URL for website screenshot
$screenshotUrl = "https://hcti.io/v1/image/create-and-render/$apiId/$urlToken/$format?$urlQueryString";
echo "Screenshot URL: $screenshotUrl\n";


// Example 3: Using a template
$templateId = 't-b0354248-e7f6-4cca-81c6-2b4a70a16388';
$templateValues = [
  'title' => 'Launch',
  'author' => ['name' => 'Avery'],
];
ksort($templateValues);


$templateParams = [];
foreach ($templateValues as $key => $value) {
  $templateParams[$key] = json_encode($value);
}


$templateQueryString = http_build_query($templateParams, '', '&', PHP_QUERY_RFC3986);
$templateToken = hash_hmac('sha256', $templateQueryString, $apiKey);
$templatedImageUrl = "https://hcti.io/v1/image/$templateId/$templateToken/$format?$templateQueryString";
echo "Templated image URL: $templatedImageUrl\n";


// Use the URLs directly in your HTML
// echo '<img src="' . htmlspecialchars($imageUrl) . '" alt="Generated image">';
```

### Example response

When you access a valid signed URL, the API returns the generated file directly with the appropriate content type: `image/png`, `image/jpeg`, `image/webp`, or `application/pdf`.

If there’s an error, you’ll receive a JSON response:

```plaintext
STATUS: 400 BAD REQUEST
```

```json
{
  "error": "Bad Request",
  "statusCode": 400,
  "message": "HTML is Required"
}
```

```plaintext
STATUS: 401 UNAUTHORIZED
```

```json
{
  "error": "Unauthorized",
  "statusCode": 401,
  "message": "Invalid token"
}
```

The signing key must be enabled and grant [`images:create`](/getting-started/using-the-api/permissions/). See [API key management](/management-api/api-keys/) when replacing or disabling signing credentials.

## Need help?

Talk to a human. Email [support@htmlcsstoimage.com](mailto:support@htmlcsstoimage.com) and we’ll help you get started.
