---
slug: 32-http-client
title: HTTP client
description: Make HTTP requests in Synsema — http/http_get/post/put/delete and fetch — all gated by net(host), with a uniform response map.
example_ids: [http-client]
---

# HTTP client

Every request (`http*` and `fetch`) is gated by `net(host)` — deny-by-default, even under `run`. HTTPS works out of the box (rustls + OS root CAs).

```synsema
-- Doc example: the HTTP client is gated by net(host). A request to an undeclared
-- host is refused at the capability check — before any network call happens.
intent: "doc example: HTTP client capability gating"
require net("api.allowed.com")

task reach_undeclared()
    give http_get("https://evil.example.com/data")    -- host not declared → denied

test "a request to an undeclared host is blocked (deny-by-default)"
    assert_error(reach_undeclared)
```

## Making requests

```synsema
require net("api.store.com")

let r be http_get("https://api.store.com/products")
let r be http_get(url, {"x-api-key": secret("API_KEY")}, {"page": "1"})   -- headers, query
let r be http_post(url, {"name": "Alice"}, {"Authorization": bearer(secret("API_KEY"))})
let r be http("POST", url, headers, query, body)                          -- full control
```

Credentials go in **headers** (a `secret` materializes only at the socket) — see **[Secrets](/en/0.6.x/21-secrets)**.

## Timeout

Every HTTP builtin takes an optional **timeout in seconds** as its last argument (default **30**;
absent or invalid falls back to 30 — never an error):

```synsema
let r be http("GET", url, nothing, nothing, nothing, 120)   -- slow API: wait up to 2 min
let r be http_get(url, nothing, nothing, 5)                  -- fail fast: 5s
let r be fetch(url, "GET", nothing, nothing, 60)
```

Signatures: `http(method, url, headers?, query?, body?, timeout?)` · `http_get(url, headers?,
query?, timeout?)` · `http_post(url, body, headers?, timeout?)` · `http_put(url, body, headers?,
timeout?)` · `http_delete(url, headers?, timeout?)` · `fetch(url, method?, headers?, body?,
timeout?)`. On timeout the response comes back with `ok: false` and the OS error in `error of r`.

## The response

A request returns a map:

```synsema
status of r     -- 200
ok of r         -- true (200–299)
body of r       -- raw text
json of r       -- parsed JSON (if the content-type is JSON)
headers of r    -- response headers
error of r      -- error message if it failed
```

`require net` / `net("*")` allow any host; `net("*.x.com")` matches subdomains.
