domainnotallowed

The credentials are valid, but the request uses a domain that is not on the allowlist that applies to them. The API refuses the request before any browser starts. Nothing about timing will change that, so the same request fails until the allowlist or the request changes. This page is part of the error code reference.

HTTP status 403 Forbidden
error.type authorization_error
error.retryable false
Credits used 0

Example response

{
  "error": {
    "type": "authorization_error",
    "code": "domain_not_allowed",
    "message": "Domain 'blocked.example' is not in the allowlist",
    "retryable": false,
    "docs": "https://docs.renderscreenshot.com/errors/domain_not_allowed"
  },
  "request_id": "req_abc123"
}

Why an allowlist exists

An allowlist limits what a set of credentials can capture. It matters most for credentials that can be seen by other people, such as publishable keys and signed URLs in public HTML. If someone copies such a URL and changes the target, the allowlist stops your account from rendering their pages on your credits.

Common causes

  • A hostname variant. www.example.com, example.com, blog.example.com and a staging host are different hostnames. Check which form you sent and which form is on the list.
  • User-supplied URLs. Your product passes a URL that a user typed, and it points to a site you never meant to support.
  • The wrong key or environment. A key meant for one site is used by another project, for example after copying a .env file.
  • A copied or edited public URL. Someone changed the url parameter of a public URL that uses your credentials.

How to fix it

  1. Read the hostname in error.message and the url you sent. Log both with the request_id.
  2. If the domain is yours and should work, add it to the allowlist for those credentials, or use the credentials that belong to that site.
  3. If the domain is not one you support, the error is correct. Reject the URL in your own app before you call the API, with a message the user can act on.
  4. Never retry. Mark the job as failed for good, so a queue does not send it again every few minutes.

If you are sure the domain is on the list and the error continues, contact [email protected] with the request_id.

Handle it as a permanent failure

This Python function turns the error into its own exception type, so a job runner can skip the item instead of re-queuing it:

# domain_check.py (Python 3.10+, requests)
import os
import sys
from urllib.parse import urlsplit

import requests

API = os.environ.get("RS_BASE_URL", "https://api.renderscreenshot.com")
HEADERS = {"Authorization": f"Bearer {os.environ['RS_API_KEY']}"}


class DomainNotAllowed(Exception):
    """Permanent failure: do not retry, do not requeue."""


def capture(url: str) -> bytes:
    res = requests.post(f"{API}/v1/screenshot", json={"url": url}, headers=HEADERS, timeout=(10, 90))
    if res.ok:
        return res.content

    payload = res.json() if "json" in res.headers.get("Content-Type", "") else {}
    error = payload.get("error", {})
    if error.get("code") == "domain_not_allowed":
        host = urlsplit(url).hostname
        raise DomainNotAllowed(f"{host}: {error.get('message')} (request {payload.get('request_id')})")
    res.raise_for_status()
    raise RuntimeError(f"unexpected status {res.status_code}")


if __name__ == "__main__":
    try:
        image = capture(sys.argv[1])
    except DomainNotAllowed as e:
        print(f"skipped, will not retry: {e}", file=sys.stderr)
        sys.exit(2)
    with open("shot.png", "wb") as f:
        f.write(image)
    print("saved shot.png")

With the Ruby SDK, the error arrives as RenderScreenshot::AuthorizationError. That class also covers insufficient_credits and feature_not_available, so check e.code:

require 'renderscreenshot'

client = RenderScreenshot.client(ENV.fetch('RS_API_KEY'))

begin
  client.take(RenderScreenshot::TakeOptions.url('https://blocked.example/pricing'))
rescue RenderScreenshot::AuthorizationError => e
  raise unless e.code == 'domain_not_allowed'

  # Permanent for this URL: record it and move on. Do not requeue.
  warn "not allowed: #{e.message} (#{e.request_id})"
end

Tested on 2026-10-15 with Python 3.14 plus requests 2.34.2, and with Ruby 4.0.7 plus the renderscreenshot gem 1.0.0, against a local mock that returns the envelope above. The blocked URL exited with code 2 and no retry; an allowed URL saved the image.

Related in this topic

Was this page helpful?