ScreenshotNeo

BlogHow-to

HTMLCSStoImage API Authentication Error: How to Fix a 401 Response

Fix an HTML/CSS to Image 401 by checking Basic Auth credentials or a signed URL token, then distinguish authentication failures from 403 permission errors.

By the ScreenshotNeo team4 October 20266 min read

A 401 from the HTML/CSS to Image API usually means the request did not authenticate. For a standard API call, send the matching API ID as the HTTP Basic username and API key as the password, and make sure the key is enabled. For a signed image URL, check the HMAC SHA-256 token against the exact query string. A 403 generally means credentials were accepted but the key lacks permission or the operation is not available on the plan.

1. Identify which authentication method your request uses

The troubleshooting steps depend on the request path:

Request Authentication First checks for a 401
Standard image creation: POST https://hcti.io/v1/image HTTP Basic; API ID is username, API key is password Credential pair, key status, and Authorization header
Signed create-and-render URL HMAC SHA-256 token over the query string Signing key, exact query string, encoding, parameter order

Do not troubleshoot a signed URL as though it were a Basic Auth request. First inspect the actual outgoing request and identify which path your code constructs.

2. Fix Basic Auth for a standard API request

The API ID and API key must belong together. Use the API ID as the username and the API key as the password. Confirm both values came from the intended organization and that the key is enabled in the account’s key controls. Disabled keys cannot authenticate.

cURL

curl -X POST https://hcti.io/v1/image \\
  -u "$HTMLCSS_API_ID:$HTMLCSS_API_KEY" \\
  -H "Content-Type: application/json" \\
  -d '{"html":"<h1>Hello</h1>"}'

Set HTMLCSS_API_ID and HTMLCSS_API_KEY in your server environment before running the command. Keep the secret out of shell history where possible, source control, browser code, logs, and shared troubleshooting messages.

Python

import os
import requests

api_id = os.environ["HTMLCSS_API_ID"]
api_key = os.environ["HTMLCSS_API_KEY"]

response = requests.post(
    "https://hcti.io/v1/image",
    auth=(api_id, api_key),
    json={"html": "<h1>Hello</h1>"},
    timeout=30,
)
print(response.status_code)
print(response.text)
response.raise_for_status()
print(response.json())

Node.js

const apiId = process.env.HTMLCSS_API_ID;
const apiKey = process.env.HTMLCSS_API_KEY;
if (!apiId || !apiKey) throw new Error('Set HTMLCSS_API_ID and HTMLCSS_API_KEY');

const authorization = Buffer.from(`${apiId}:${apiKey}`).toString('base64');
const response = await fetch('https://hcti.io/v1/image', {
  method: 'POST',
  headers: {
    Authorization: `Basic ${authorization}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ html: '<h1>Hello</h1>' }),
});
console.log(response.status, await response.text());

The Basic Authorization value is the Base64 encoding of API_ID:API_KEY. Base64 is an encoding, not encryption, so the header still contains a secret and must only be sent over HTTPS. Keep credentials on a server you control; do not embed them in frontend JavaScript or a public app bundle.

3. Fix authentication for a signed URL

Signed URL authentication uses a different calculation. The token is an HMAC SHA-256 hash of the query string without the leading ?, with the API key as the HMAC secret. The key must be enabled and grant the images:create permission.

For a 401 on this path, verify all of the following:

  1. The signing key is the API key for the intended account and is enabled.
  2. The key has the images:create permission.
  3. The HMAC input is exactly the query string, excluding the initial question mark.
  4. Parameter order, URL encoding, whitespace, and escaping match the final URL exactly.
  5. If you edit any parameter after signing, you recompute the token.

A URL can look equivalent to a human while having a different encoded query string. For example, changing spaces from one encoding form to another or reordering parameters changes the signed input and invalidates the token. Construct the final query string once, sign that exact string, and avoid normalizing or rewriting it afterward.

4. Read 401 and 403 as different failures

Status Likely meaning What to check
401 Unauthorized Credentials or signature were missing, incorrect, disabled, or invalid. Basic Auth pair and key status, or signed URL token and exact input string.
403 Forbidden Credentials were accepted, but the request is not allowed. Required permission, organization ownership of the resource, and plan eligibility.

Do not rotate credentials as the only response to a 403. Inspect the response body for the required permission, confirm the key belongs to the organization that owns the resource, and check whether the operation requires a particular plan.

5. Troubleshooting checklist

  1. Capture the status and response body. Record the HTTP status and safe diagnostic text. Redact Authorization headers, API keys, signed tokens, and any private URL parameters before sharing logs.
  2. Confirm the request path. Is it a POST to /v1/image using Basic Auth, or a signed create-and-render URL?
  3. Check credentials at runtime. Verify the application process received the expected API ID and key from its environment or secret store. Watch for empty values, stale deployment secrets, whitespace, or credentials copied from a different organization.
  4. Check key state. Confirm the key is enabled. For signed URLs, also verify images:create.
  5. Inspect the outgoing request safely. Confirm the Authorization scheme is Basic and represents API_ID:API_KEY. Never print the encoded header: it can be decoded to recover the secret.
  6. For signed URLs, compare bytes. Compare the exact query string used for signing with the exact query string sent, including order and percent-encoding.
  7. If it is a 403, check access. Verify permission, organization association, and plan eligibility separately from authentication.
  8. Escalate with redacted evidence. If the concrete checks pass, contact the vendor at support@htmlcsstoimage.com. Share status, timestamp, request type, and redacted response details, never the key.

6. Reliability, performance, and cost considerations

A 401 is an authentication failure, so changing image dimensions, HTML content, or render timing is unlikely to address it. Fix credential loading and request construction first. Keep secrets in server-side configuration or a secret manager, and avoid exposing them in client applications, error reports, and request traces.

For reliability, validate required environment variables at application startup, keep the API ID and key paired in one deployment configuration, and check status codes before treating a response as a successful image result. Avoid retrying a persistent 401 repeatedly: retries do not repair invalid credentials and can obscure the original response. After correcting a key or deployment setting, make one controlled request and inspect the response.

The research material does not specify a price or billing rule for failed authentication requests, so do not infer a cost from the 401 status alone. Check the vendor’s current account and billing information for the applicable policy.

7. Or skip the browser setup

If your goal is simply to get a website screenshot, ScreenshotNeo offers a screenshot API with a single GET request. See the ScreenshotNeo API documentation for request options.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

ScreenshotNeo accepts cookie or consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers report the page verdict and billing status. Its MCP server provides take_screenshot, get_page_info, and capture_pdf for AI agents. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots.

Sign up for ScreenshotNeo and get 1,000 free screenshots a month, with no card.

8. FAQ

Can I put the API key in browser JavaScript?

No. Keep it on a server because browser code and bundled assets are visible to users.

Does Base64 hide my Basic Auth credentials?

No. It encodes the credential pair; anyone who obtains the header can decode it.

What exact part of a signed URL is used for the HMAC?

The query string without its leading question mark, using the API key as the HMAC SHA-256 secret.

What if my request returns 401 only after deployment?

Check that the deployed process has the intended environment variables and key state. Local credentials do not automatically carry over to the production environment.

Sources