invalid_url

The API returns invalid_url when the url value cannot be parsed as an absolute http:// or https:// URL. The API does not open a browser. Correct the value and send the request again. The same text will fail on every retry.

Field Value
HTTP status 400 Bad Request
error.type validation_error
error.code invalid_url
error.param url (batch: urls)
retryable false
Credit used No

See the error code reference for the other codes.

Example response

{
  "error": {
    "type": "validation_error",
    "code": "invalid_url",
    "message": "The URL 'example.com/pricing' is not a valid HTTP(S) URL",
    "param": "url",
    "retryable": false,
    "docs": "https://docs.renderscreenshot.com/errors/invalid_url"
  },
  "request_id": "req_5d0e93b7a41c2f86"
}

In a batch request, the message gives the position of the bad entry: Invalid URL at position 3: '...'. The whole batch is rejected, so remove or correct that entry and send the batch again.

What passes and what fails

We ran 23 inputs through the same check that the API uses (a strict RFC 3986 parser, then a test for the http or https scheme):

Input Result Note
https://example.com passes
HTTPS://EXAMPLE.COM passes Scheme and host are not case-sensitive
https://example.com/#/route passes Fragments are fine
https://example.com/%C3%BCber passes Encoded non-ASCII path
https://xn--exmple-cua.com passes Punycode host
example.com, www.example.com/page invalid_url No scheme
//example.com invalid_url Protocol-relative
ftp://example.com, javascript:alert(1) invalid_url Wrong scheme
https://example.com/a b invalid_url Raw space
" https://example.com", "https://example.com\n" invalid_url Whitespace or newline around the value
https://exämple.com, https://example.com/über invalid_url Raw non-ASCII characters
https://example.com/a[1] invalid_url Square brackets, the pipe character and ^ must be percent-encoded

Three inputs pass this check but still fail later, because the renderer refuses them for security reasons: https://example.com:8080/ (only ports 80 and 443), https://user:[email protected]/ (credentials in the URL) and private addresses such as https://[::1]/. Those come back as a target error, not invalid_url. The security guide lists the rules, and blocked covers the error.

Common causes in real code

  • The scheme is missing. Users type example.com. Add https:// before you send.
  • The value has a newline. Lines read from a CSV or text file keep \n unless you strip them.
  • The URL was encoded twice. In a GET request, url=https%253A%252F%252F... decodes to https%3A%2F%2F..., which has no scheme. Encode the target URL once.
  • The URL comes straight from a browser address bar. Browsers show über and exämple.com, but they send the encoded form. Copy the encoded form, or normalize it as shown below.

In a GET request, also encode & inside the target URL. Otherwise the API sees a shorter url plus extra parameters. That request does not return invalid_url. It captures the wrong page.

Normalize user input before you send it

This function accepts what people paste and returns a URL that passes the check, or a clear reason why it cannot:

// normalize-url.mjs  (Node 22): turn user input into a URL the API accepts, or explain why not.
export function normalizeTargetUrl(input) {
  let text = String(input ?? '').trim();
  if (!text) throw new Error('empty URL');
  if (/%3A%2F%2F/i.test(text)) text = decodeURIComponent(text); // pasted already encoded
  if (!/^[a-z][a-z0-9+.-]*:/i.test(text)) text = `https://${text.replace(/^\/\//, '')}`; // no scheme

  const url = new URL(text); // throws TypeError on input that cannot be a URL
  if (url.protocol !== 'http:' && url.protocol !== 'https:') {
    throw new Error(`unsupported scheme ${url.protocol}`);
  }
  if (url.username || url.password) throw new Error('credentials in the URL are not allowed');
  if (url.port && url.port !== '80' && url.port !== '443') {
    throw new Error(`port ${url.port}: only ports 80 and 443 are reachable`);
  }
  // WHATWG leaves | ^ [ ] unencoded in the path and query; stricter parsers reject them.
  const enc = (s) => s.replace(/[|^[\]]/g, (c) => '%' + c.charCodeAt(0).toString(16).toUpperCase());
  return url.origin + enc(url.pathname + url.search) + url.hash; // punycode host, encoded path
}

For example, https://exämple.com/über uns becomes https://xn--exmple-cua.com/%C3%BCber%20uns. Every URL the function returned in our test also passed the API's check. Without the last step, https://example.com/a|b would still fail.

Handle it in a batch job

When URLs come from a file or a database, invalid_url is a data problem, not an outage. With the Ruby SDK, collect the bad rows and continue:

require 'renderscreenshot'

client = RenderScreenshot.client(ENV.fetch('RS_API_KEY'))
rejected = []

File.readlines(ARGV.fetch(0), chomp: true).each_with_index do |url, line|
  image = client.take(RenderScreenshot::TakeOptions.url(url).preset('og_card'))
  File.binwrite("shot-#{line + 1}.png", image)
rescue RenderScreenshot::Error => e
  raise unless e.code == 'invalid_url' # anything else is not a data problem

  rejected << [line + 1, url, e.message]
end

rejected.each { |line, url, message| warn "line #{line}: #{url.inspect}: #{message}" }
exit(rejected.empty? ? 0 : 1)

Tested on 2026-10-15. The input table and the normalizer output were checked with Ruby 4.0.7's URI parser, which is the parser the API uses. The normalizer ran on Node 22.18. The Ruby job ran with the renderscreenshot gem 1.0.0 against a local mock that returns the API's error envelope.

Related in this topic

Was this page helpful?