Script.Util.HttpGet
→ HttpGetInstanceHTTP GET request constructor — creates an HttpGetInstance that caches content for mail sends and supports custom headers.
Syntax
new Script.Util.HttpGet(url)
Script.Util.HttpGet creates an HTTP GET request handler. Unlike Platform.Function.HTTPGet, it caches content for use in mail sends and supports custom headers via setHeader(). Only works with HTTP on port 80 and HTTPS on port 443.
For full control over HTTP method, timeouts, and all status codes, use Script.Util.HttpRequest instead.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
url |
string | Yes | Target URL (HTTP port 80 or HTTPS port 443 only) |
Return Value
Returns an HttpGetInstance. Call send() to execute the request.
Show test script
<script runat="server">
/*
* Chapter: Return Value
*
* Proves:
* 1. `new Script.Util.HttpGet(url)` returns an HttpGetInstance (a CLR proxy).
* 2. The instance exposes send() and the request is executed by calling it.
* 3. No Platform.Load is required — the constructor works without it.
*
* EXPECTED OUTPUT: every line starts with PASS.
*/
function assert(id, actual, expected) {
var got;
try { got = "" + actual; } catch (ex) { got = "THREW: " + ("" + ex.message); }
Platform.Response.Write((got === "" + expected ? "PASS " : "FAIL ") + id + " -> [" + got + "]\n");
}
/* 1. Constructor returns an instance without any Platform.Load call. */
var req = new Script.Util.HttpGet("https://ssjs.guide/robots.txt");
assert("constructor returns an object (CLR proxy)", typeof req, "clr");
/* 2. send() exists on the instance. */
assert("instance exposes send()", typeof req.send, "clrmethodinfo");
/* 3. Calling send() executes the request and returns a response object. */
var resp = req.send();
assert("send() returns a response object", typeof resp, "clr");
assert("send() performed the GET (statusCode 200)", resp.statusCode, "200");
</script>
HttpGetInstance Properties
| Property | Type | Default | Description |
|---|---|---|---|
retries |
number | 1 |
Number of retry attempts on failure |
continueOnError |
boolean | false |
If true, does not throw on HTTP error status |
emptyContentHandling |
number | 0 |
Indicates what to do if the GET request doesn’t return any content. 0 = continue, 1 = stop the request, 2 = continue to the next subscriber (only works in email sends) |
timeout |
number | 30 |
Request timeout in seconds |
timeout is not listed in the official docs, but the property exists and is applied end-to-end at runtime (same behaviour as on Script.Util.HttpRequest). Its default value is 30, matching the 30-second send() timeout the docs describe — so the unit is seconds, not milliseconds.
Show test script
<script runat="server">
/*
* Chapter: HttpGetInstance Properties
*
* Proves, for each documented property, its DEFAULT value and that it is
* writable (reads back what was assigned):
* 1. retries default 1
* 2. continueOnError default false
* 3. emptyContentHandling default 0
* 4. timeout default 30 (see DEV below)
*
* DEVIATION (DEV):
* - `timeout` defaults to 30, NOT 30000. The official docs only state that
* send() times out after 30 seconds and do not list the property at all;
* the runtime value 30 is therefore expressed in SECONDS, not
* milliseconds. Script.Util.HttpRequest behaves identically.
*
* NOTE: property reads return CLR values, so every comparison normalises
* with `"" + value`. String(value) yields an equivalent real JS string on
* most of them, but throws "Object reference not set to an instance of an
* object." on contentType/encoding/headers, so `"" + value` is preferred.
* A CLR boolean stringifies capitalised as "False" / "True".
*
* EXPECTED OUTPUT: every line starts with PASS.
*/
function assert(id, actual, expected) {
var got;
try { got = "" + actual; } catch (ex) { got = "THREW: " + ("" + ex.message); }
Platform.Response.Write((got === "" + expected ? "PASS " : "FAIL ") + id + " -> [" + got + "]\n");
}
var req = new Script.Util.HttpGet("https://ssjs.guide/robots.txt");
/* 1. retries — default and writable. */
assert("retries default is 1", req.retries, "1");
req.retries = 2;
assert("retries is writable", req.retries, "2");
/* 2. continueOnError — default false (CLR booleans stringify capitalised). */
assert("continueOnError default is false", req.continueOnError, "False");
req.continueOnError = true;
assert("continueOnError is writable", req.continueOnError, "True");
/* 3. emptyContentHandling — default 0, accepts documented modes 1 and 2. */
assert("emptyContentHandling default is 0", req.emptyContentHandling, "0");
req.emptyContentHandling = 1;
assert("emptyContentHandling accepts 1 (stop)", req.emptyContentHandling, "1");
req.emptyContentHandling = 2;
assert("emptyContentHandling accepts 2 (next subscriber)", req.emptyContentHandling, "2");
req.emptyContentHandling = 0;
assert("emptyContentHandling reset to 0", req.emptyContentHandling, "0");
/* 4. DEVIATION — timeout defaults to 30 (seconds), not 30000 ms. */
var t = new Script.Util.HttpGet("https://ssjs.guide/robots.txt");
assert("DEV timeout default is 30, not 30000 (docs do not list the property)", t.timeout, "30");
t.timeout = 45;
assert("timeout is writable", t.timeout, "45");
</script>
HttpGetInstance Methods
| Method | Returns | Description |
|---|---|---|
clearHeaders() |
void | Remove all custom headers |
removeHeader(name) |
void | Remove a specific header by name |
send() |
HttpResponseInstance |
Send the request |
setHeader(name, value) |
void | Set a custom request header |
Calling setHeader() disables content caching for HttpGet.
Show test script
<script runat="server">
/*
* Chapter: HttpGetInstance Methods
*
* Proves that all four documented methods exist on the instance and that
* the header mutators can be called without throwing:
* 1. setHeader(name, value)
* 2. removeHeader(name)
* 3. clearHeaders()
* 4. send()
* 5. A request that had setHeader() called still succeeds (setHeader only
* disables content caching — it does not break the request).
*
* NOT ASSERTABLE (documented, but not observable from a GET-only CloudPage
* harness): that setHeader() disables content caching for mail sends, and
* that the custom header reaches the remote server. Both require either a
* mail-send context or an echo endpoint, neither of which this harness has.
*
* EXPECTED OUTPUT: every line starts with PASS.
*/
function assert(id, actual, expected) {
var got;
try { got = "" + actual; } catch (ex) { got = "THREW: " + ("" + ex.message); }
Platform.Response.Write((got === "" + expected ? "PASS " : "FAIL ") + id + " -> [" + got + "]\n");
}
function assertNoThrow(id, fn) {
var msg = "ok";
try { fn(); } catch (ex) { msg = "THREW: " + ("" + ex.message); }
Platform.Response.Write((msg === "ok" ? "PASS " : "FAIL ") + id + " -> " + msg + "\n");
}
var req = new Script.Util.HttpGet("https://ssjs.guide/robots.txt");
/* 1-4. All four documented methods are present. */
assert("setHeader() exists", typeof req.setHeader, "clrmethodinfo");
assert("removeHeader() exists", typeof req.removeHeader, "clrmethodinfo");
assert("clearHeaders() exists", typeof req.clearHeaders, "clrmethodinfo");
assert("send() exists", typeof req.send, "clrmethodinfo");
/* The mutators return void and do not throw. */
assertNoThrow("setHeader(name, value) does not throw", function () { req.setHeader("X-Probe", "abc"); });
assertNoThrow("removeHeader(name) does not throw", function () { req.removeHeader("X-Probe"); });
assertNoThrow("clearHeaders() does not throw", function () { req.clearHeaders(); });
/* 5. A request with a custom header still sends successfully. */
var withHeader = new Script.Util.HttpGet("https://ssjs.guide/robots.txt");
withHeader.setHeader("X-Probe", "abc");
var resp = withHeader.send();
assert("send() succeeds after setHeader()", resp.statusCode, "200");
</script>
HttpResponseInstance Object
HttpGet returns a response object of the same shape as HttpRequest, but its response metadata is never populated: contentType and encoding come back empty and a for..in over headers yields no real headers. The identical request through Script.Util.HttpRequest returns all of them. Use Script.Util.HttpRequest whenever you need response headers or the content type.
The resp object returned by req.send() has these properties:
| Property | Type | Description |
|---|---|---|
content |
CLR string | Response body (must use String() to convert) |
contentType |
string | The content type returned in the response — always empty on HttpGet (see bug callout above) |
encoding |
string | The encoding type returned in the response — always empty on HttpGet (see bug callout above) |
headers |
object | Response headers as a CLR object — not directly indexable, and on HttpGet the enumeration is always empty (see bug callout above) |
returnStatus |
number | A status value: 0 = OK, 1 = Empty URL, 2 = Call failed, 3 = Call succeeded with empty content |
statusCode |
number | HTTP status code — a CLR value: convert with Number() before === or switch (relational operators like >= 400 work on it directly) |
resp.content is a CLR string, not a JavaScript string. Always wrap it with String(resp.content) before calling ParseJSON() or string methods.
resp.statusCode and resp.returnStatus are CLR values, not JavaScript numbers — resp.statusCode === 200 is always false and switch (resp.statusCode) silently falls through to default. Convert once with Number(resp.statusCode). Do not use ==: loose equality against a CLR value backed by a .NET null throws Value cannot be null., which is exactly how resp.contentType and resp.encoding behave on this handler. Relational operators (>= 400, < 300) are the exception and are correct on the raw value. See Script.Util.HttpRequest for the full pattern.
resp.content.length returns -1 regardless of the real body length, while typeof already reports number — so the usual CLR tell is missing and the wrong answer is invisible on inspection. Use String(resp.content).length. Measured on the same response object type via Script.Util.HttpRequest; not yet re-measured through HttpGet.
The official docs example reads a single header via resp.headers["..."], but that access throws “Use of Common Language Runtime (CLR) is not allowed” at runtime. On Script.Util.HttpRequest headers are still readable by enumerating with for..in (see below); on HttpGet that enumeration is empty, so headers cannot be read at all.
Reading response headers
Direct access — resp.headers["Content-Type"], .Get(), .Item(), or String(resp.headers[key]) — throws “Use of Common Language Runtime (CLR) is not allowed”. On Script.Util.HttpRequest a for..in loop over resp.headers yields keys shaped "[Name, Value]" — the value is embedded in the key string itself — so the map below can be built without ever reading a CLR value. On Script.Util.HttpGet the same loop yields nothing, so the helper always returns an empty map; switch to Script.Util.HttpRequest when you need headers.
/**
* Build a plain { name: value } header map from an HttpResponse.
* Reads only the for..in enumeration keys (shaped "[Name, Value]") so it never
* touches a CLR value — avoiding "Use of CLR is not allowed".
* @param {object} resp - the response returned by req.send()
* @returns {object} map of lowercased header name => value string
*/
function getHeaderMap(resp) {
var map = {};
for (var k in resp.headers) {
var pair = String(k);
// Enumeration keys are wrapped in [ ] — strip them.
if (pair.charAt(0) === "[") { pair = pair.substring(1); }
if (pair.charAt(pair.length - 1) === "]") { pair = pair.substring(0, pair.length - 1); }
var idx = pair.indexOf(", ");
if (idx > -1) {
map[pair.substring(0, idx).toLowerCase()] = pair.substring(idx + 2);
}
}
return map;
}
var resp = req.send();
var headers = getHeaderMap(resp);
var contentType = headers["content-type"]; // "application/json; charset=utf-8"
Header names are lowercased in the map above so lookups are case-insensitive. A missing header returns undefined.
Show test script
<script runat="server">
/*
* Chapter: HttpResponseInstance Object
*
* Proves the response properties documented for HttpGet:
* 1. statusCode is the HTTP status code (200 for a reachable URL).
* 2. returnStatus is 0 on success ("OK").
* 3. content is a CLR value, not a JavaScript string, and String(content)
* / "" + content produce a usable JS string.
* 4. headers is present but NOT indexable — resp.headers["Content-Type"]
* throws "Use of Common Language Runtime (CLR) is not allowed".
* (This is the differs-from-docs claim: the official docs show exactly
* that access pattern.)
*
* BUG (proven, see the bug callout on the page):
* 5. On HttpGet the response is NOT equal to the HttpRequest response:
* contentType and encoding come back EMPTY, and a for..in over
* resp.headers yields only the synthetic "[prototype, ]" entry — no
* real response headers. The same request via Script.Util.HttpRequest
* returns a populated contentType and the full header enumeration.
*
* EXPECTED OUTPUT: every line starts with PASS.
*/
function assert(id, actual, expected) {
var got;
try { got = "" + actual; } catch (ex) { got = "THREW: " + ("" + ex.message); }
Platform.Response.Write((got === "" + expected ? "PASS " : "FAIL ") + id + " -> [" + got + "]\n");
}
function assertThrowsFragment(id, fn, fragment) {
var msg = "";
try { fn(); msg = "did NOT throw"; } catch (ex) { msg = "" + ex.message; }
Platform.Response.Write((msg.indexOf(fragment) > -1 ? "PASS " : "FAIL ") + id + " -> " + msg + "\n");
}
var resp = new Script.Util.HttpGet("https://ssjs.guide/site-index.json").send();
/* 1-2. Status properties. */
assert("statusCode is 200", resp.statusCode, "200");
assert("returnStatus is 0 (OK)", resp.returnStatus, "0");
/* 3. content is a CLR value that must be converted before use. */
assert("content is a CLR value, not a JS string", typeof resp.content, "clr");
var body = "" + resp.content;
assert("content converts to a non-empty JS string", body.length > 0, "true");
assert("String(content) yields the same length", String(resp.content).length, body.length);
/* 4. DEV — direct header indexing throws, contradicting the official docs example. */
assert("headers property exists", typeof resp.headers, "clr");
assertThrowsFragment(
"DEV resp.headers[\"Content-Type\"] throws (official docs show this access)",
function () { return resp.headers["Content-Type"]; },
"Common Language Runtime"
);
/* 5. BUG — HttpGet returns no response metadata (HttpRequest does). */
assert("BUG contentType is empty on HttpGet (HttpRequest returns it)", resp.contentType, "");
assert("BUG encoding is empty on HttpGet (HttpRequest returns it)", resp.encoding, "");
var realKeys = 0;
for (var k in resp.headers) {
var pair = "" + k;
if (pair.indexOf("[prototype") !== 0) { realKeys = realKeys + 1; }
}
assert("BUG for..in over headers yields 0 real headers on HttpGet", realKeys, "0");
</script>
Examples
Basic GET request
var req = new Script.Util.HttpGet("https://api.example.com/data");
var resp = req.send();
if (Number(resp.statusCode) === 200) {
var result = Platform.Function.ParseJSON(String(resp.content));
Write(Stringify(result));
}
GET with auth header
var req = new Script.Util.HttpGet("https://api.example.com/items");
req.setHeader("Authorization", "Bearer " + accessToken);
req.retries = 2;
req.continueOnError = true;
var resp = req.send();
if (Number(resp.statusCode) === 200) {
var items = Platform.Function.ParseJSON(String(resp.content));
for (var i = 0; i < items.length; i++) {
Write(items[i].name + "<br>");
}
}
Show test script
<script runat="server">
/*
* Chapter: Examples
*
* Proves that both documented example patterns run end-to-end, in a single
* request that combines them:
* 1. A custom auth-style header plus retries / continueOnError are set on
* the instance before send().
* 2. send() returns statusCode 200.
* 3. The body is converted from CLR and fed to ParseJSON, which returns a
* usable object — the exact pattern both examples on the page use.
*
* NOTE: one external call per deployment. Two live GETs of a large JSON
* document in the same CloudPage request exceed the page limit and return
* HTTP 422, which would be a harness artefact, not an API result.
*
* EXPECTED OUTPUT: every line starts with PASS.
*/
function assert(id, actual, expected) {
var got;
try { got = "" + actual; } catch (ex) { got = "THREW: " + ("" + ex.message); }
Platform.Response.Write((got === "" + expected ? "PASS " : "FAIL ") + id + " -> [" + got + "]\n");
}
/* 1. Configure the request exactly as the "GET with auth header" example does. */
var req = new Script.Util.HttpGet("https://ssjs.guide/site-index.json");
req.setHeader("Authorization", "Bearer dummy-token");
req.retries = 2;
req.continueOnError = true;
/* 2. Send and check the status code, as both examples do. */
var resp = req.send();
assert("GET with custom header returns statusCode 200", resp.statusCode, "200");
/* 3. Convert the CLR body and parse it — the ParseJSON pattern from both examples. */
var body = "" + resp.content;
assert("body converts to a non-empty JS string", body.length > 0, "true");
var result = Platform.Function.ParseJSON(body);
assert("ParseJSON of the body returns an object", typeof result, "object");
</script>
Notes
- Only works with HTTP on port 80 and HTTPS on port 443. Other ports require
Script.Util.HttpRequest. - Caches the response content for use in mail send personalisation — unless
setHeader()is called (which disables caching). - Does not require
Platform.Load.