HTTP
Five members on the http namespace for outbound requests: http.get, http.post, http.put, http.delete, and http.request for arbitrary methods. Each returns a uniform response object with ok, status, headers, body, and error. Branch on ok, and read status and body when you need them. All calls share one connection pool, and arrays or objects passed as the body are auto-JSON-encoded with a Content-Type: application/json header.
http.get: fetch a URL
Server-side only; pure data shape
http.get(url, opts?) issues a GET request and returns the response object. opts is optional and accepts timeout (ms), headers (object), follow_redirects (bool), and body (rare for GET). Default timeout is 30 seconds.
> server
resp: http.get('https://api.github.com/repos/anthropics/anthropic-sdk-python')
ok: resp.ok
status: resp.status
> page
<p> [status] / ok? [ok] Response shape
Uniform regardless of method or status
Every response object has exactly five fields. ok is true when status is between 200 and 399. error is non-empty only when the transport itself failed (DNS, timeout, broken connection), and for those failures status is 0. The body is always a string; call json.parse(resp.body) when you expect JSON.
# resp shape - five fields:
# ok: true (status < 400)
# status: 200 (0 on transport failure)
# headers: {content-type: '...'} (lower-cased keys)
# body: 'raw bytes as string'
# error: '' (non-empty on transport failure) http.post: JSON body
Object or array body → automatic JSON encoding
Passing an object or array as the body is the common case. Rat marshals it with encoding/json and sets Content-Type: application/json for you, unless you override the header. Pass a string body verbatim when you need form-encoded or pre-built payloads.
> server
resp: http.post('https://httpbin.org/post', {hello: 'rat'})
> page
<p> echo'd back: [resp.body] Custom headers and timeout
Third arg = opts object
The opts object on POST / PUT goes in the third slot (after URL and body); on GET / DELETE / request it's the second slot. headers merges with the defaults; explicit Content-Type wins over the auto-set one.
> server
resp: http.post( 'https://api.example.com/items', {name: 'rat'}, { timeout: 5000, headers: {Authorization: 'Bearer ' + api_token}, }, ) http.request: arbitrary method
For PATCH, HEAD, OPTIONS, etc.
http.request(method, url, opts?) takes the method as a string. The body goes inside opts.body rather than a positional slot, so the call shape stays uniform for verbs that don't conventionally carry a body.
> server
resp: http.request('PATCH', 'https://api.example.com/items/1', { body: {name: 'updated'}, headers: {Authorization: 'Bearer ' + api_token}, }) Disabling redirect-following
follow_redirects: false to stop at 3xx
By default http follows redirects up to the standard net/http limit. Setting follow_redirects: false returns the 3xx response untouched, which is useful when you want to inspect the Location header without chasing it.
> server
resp: http.get('https://example.com/old-path', {follow_redirects: false})
next: resp.headers['location'] Composing with json
JSON in, JSON out
The standard pattern: pass an object body (auto-encoded), parse the response body. The connection pool is reused for repeat calls to the same host, so successive requests are cheap.
> server
resp: http.post('https://api.example.com/echo', {hello: 'rat'})
data: json.parse(resp.body)
> page
<p> server saw: [data.hello] SSRF guard in prod mode
Refuses loopback / link-local / RFC1918 targets
When RAT_MODE=prod, every outbound call goes through a Server-Side Request Forgery gate. The scheme must be http or https. The resolved IP must not be loopback (127/8, ::1), link-local (169.254/16, including the cloud metadata endpoint), or RFC1918 private (10/8, 172.16/12, 192.168/16). The check fires three times per request: at parse time, at connect time (closing the DNS-rebinding window), and on every redirect hop. Dev mode keeps loopback open so authors can hit their own services. Blocked requests come back as {ok: false, status: 0, error: 'ssrf: ...'}, the same shape as any transport failure, so handler code needs no special branch.
> server
# In prod, this returns an error response without dialing:
resp: http.get('http://169.254.169.254/latest/meta-data/')
ok: resp.ok
err: resp.error See also JSON · Public endpoints · Encoding