---
layout: page
title: Signed Image URLs
permalink: /getting-started/create-and-render/
parent: Getting started
nav_order: 6
description: >-
Generate signed image URLs from HTML, public webpages, or template values with a single authenticated GET request.
---
# Single-Request Image Generation
{: .no_toc }
{: .fs-9 }
Generate images from HTML/CSS, URLs, or templates in a single request with signed URLs.
{: .fs-6 .fw-300 }
{% include hint.md title="Recommendation" text="We recommend using the [standard API endpoint](/getting-started/using-the-api/) for most use cases. Only use this endpoint if you specifically need to generate image URLs from client-side code or need to avoid the two-step create-then-fetch process." %}
{% include hint.md title="Publishing social cards?" text="If each image corresponds to a public page on your website or CMS, an [OG Image Config](/getting-started/og-images/) is usually simpler. It maps page paths to stable image URLs and does not require 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 image URLs client-side without making API calls
- **Client-side friendly**: Use signed URLs to keep your API Key secure
- **Simplified workflow**: Skip the image creation step and go straight to image rendering
- **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.
{% include hint.md title="Security note" text="Never expose your API Key in client-side code. The API ID and generated token are safe to use client-side." %}
## 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 \(`Your content
`\) 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. |
{% include hint.md title="Required params" text="† Either `url` OR `html` is required, but not both. `css` is optional." %}
### Additional parameters
Optional parameters for greater control over your image.
{% include additional_parameters.md create_and_render=true %}
## 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:
```javascript
{
"title": "Launch",
"author": {
"name": "Jeff"
}
}
```
Could be encoded as:
```
author=%7B%22name%22%3A%22Jeff%22%7D&title=%22Launch%22
```
The `author` value decodes to `{"name":"Jeff"}`. The `title` value decodes to `"Launch"`.
{% include hint.md title="Use an official client" text="The [TypeScript](/example-code/typescript/) and [.NET](/example-code/c/) clients include signed URL helpers (`generateTemplatedImageUrl` and `CreateTemplatedImageUrl`) 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:
```
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" }
});
```
### 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: 'Hello World
',
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
tag or as a link
//
```
### PHP example
```php
Hello from PHP';
$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 '
';
```
### Example response
When you access a valid signed URL, the API will return the generated image directly with the appropriate content type header (`image/png`, `image/jpeg`, or `image/webp` depending on the format).
If there's an error, you'll receive a JSON response:
```
STATUS: 400 BAD REQUEST
```
```javascript
{
"error": "Bad Request",
"statusCode": 400,
"message": "HTML is Required"
}
```
```
STATUS: 401 UNAUTHORIZED
```
```javascript
{
"error": "Unauthorized",
"statusCode": 401,
"message": "Invalid token"
}
```
{% include code_footer.md version=1 %}