> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/oven-sh/bun/llms.txt
> Use this file to discover all available pages before exploring further.

# Fetch

> Bun implements the WHATWG fetch standard with server-side extensions for proxies, Unix sockets, TLS, streaming, and more.

Bun implements the [WHATWG `fetch` standard](https://fetch.spec.whatwg.org/) with extensions tailored for server-side use. The `fetch` global is available everywhere without imports.

***

## GET request

```typescript theme={null}
const response = await fetch("https://example.com");

console.log(response.status); // 200
console.log(response.headers.get("content-type")); // text/html; charset=UTF-8

const text = await response.text();
```

***

## Common HTTP methods

<CodeGroup>
  ```typescript GET theme={null}
  const res = await fetch("https://api.example.com/users");
  const users = await res.json();
  ```

  ```typescript POST (JSON) theme={null}
  const res = await fetch("https://api.example.com/users", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ name: "Alice", role: "admin" }),
  });
  const created = await res.json();
  ```

  ```typescript PUT theme={null}
  const res = await fetch("https://api.example.com/users/1", {
    method: "PUT",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ name: "Alice Updated" }),
  });
  ```

  ```typescript DELETE theme={null}
  const res = await fetch("https://api.example.com/users/1", {
    method: "DELETE",
  });
  console.log(res.status); // 204
  ```
</CodeGroup>

***

## Request headers

Pass headers as a plain object or a `Headers` instance:

<CodeGroup>
  ```typescript Plain object theme={null}
  const res = await fetch("https://api.example.com/data", {
    headers: {
      Authorization: "Bearer my-token",
      "Accept-Language": "en-US",
    },
  });
  ```

  ```typescript Headers instance theme={null}
  const headers = new Headers();
  headers.append("Authorization", "Bearer my-token");
  headers.append("X-Request-ID", crypto.randomUUID());

  const res = await fetch("https://api.example.com/data", { headers });
  ```
</CodeGroup>

***

## Reading response bodies

| Method                   | Returns                |
| ------------------------ | ---------------------- |
| `response.text()`        | `Promise<string>`      |
| `response.json()`        | `Promise<any>`         |
| `response.bytes()`       | `Promise<Uint8Array>`  |
| `response.arrayBuffer()` | `Promise<ArrayBuffer>` |
| `response.blob()`        | `Promise<Blob>`        |
| `response.formData()`    | `Promise<FormData>`    |

```typescript theme={null}
const res = await fetch("https://api.example.com/data");

// Read as JSON
const json = await res.json();

// Read as text
const text = await res.text();

// Write directly to a file
await Bun.write("output.json", res);
```

***

## Streaming response bodies

Use `response.body` (a `ReadableStream`) or an async iterator to process large responses without buffering them entirely in memory:

<CodeGroup>
  ```typescript Async iterator theme={null}
  const res = await fetch("https://example.com/large-file");

  for await (const chunk of res.body) {
    process.stdout.write(chunk);
  }
  ```

  ```typescript ReadableStream reader theme={null}
  const res = await fetch("https://example.com/large-file");
  const reader = res.body.getReader();

  while (true) {
    const { value, done } = await reader.read();
    if (done) break;
    console.log(`Chunk: ${value.byteLength} bytes`);
  }
  ```
</CodeGroup>

***

## Streaming request bodies

Send a `ReadableStream` as a request body. Bun streams it directly to the network without buffering the entire body in memory:

```typescript theme={null}
const stream = new ReadableStream({
  start(controller) {
    controller.enqueue("Hello");
    controller.enqueue(", ");
    controller.enqueue("world!");
    controller.close();
  },
});

const res = await fetch("https://api.example.com/upload", {
  method: "POST",
  body: stream,
  headers: { "Content-Type": "text/plain" },
});
```

***

## File uploads with FormData

```typescript theme={null}
const form = new FormData();
form.append("name", "Alice");
form.append("avatar", Bun.file("./avatar.png"), "avatar.png");

const res = await fetch("https://api.example.com/profile", {
  method: "POST",
  body: form,
  // Content-Type with multipart boundary is set automatically
});
```

***

## Timeouts and cancellation

### Timeout with `AbortSignal.timeout()`

```typescript theme={null}
try {
  const res = await fetch("https://slow.example.com", {
    signal: AbortSignal.timeout(5000), // abort after 5 seconds
  });
  const text = await res.text();
} catch (err) {
  if (err.name === "TimeoutError") {
    console.error("Request timed out");
  }
}
```

### Manual cancellation with `AbortController`

```typescript theme={null}
const controller = new AbortController();

// Cancel after 3 seconds
setTimeout(() => controller.abort(), 3000);

const res = await fetch("https://api.example.com/stream", {
  signal: controller.signal,
});
```

***

## Proxy support

Route requests through an HTTP or HTTPS proxy:

```typescript theme={null}
// Simple proxy
const res = await fetch("https://api.example.com/data", {
  proxy: "http://proxy.internal:8080",
});
```

Send custom headers to the proxy (for example, `Proxy-Authorization`):

```typescript theme={null}
const res = await fetch("https://api.example.com/data", {
  proxy: {
    url: "http://proxy.internal:8080",
    headers: {
      "Proxy-Authorization": "Bearer proxy-token",
    },
  },
});
```

<Note>
  You cannot use `proxy` and `unix` together in the same request.
</Note>

***

## Unix socket support

Fetch from a server listening on a Unix domain socket:

```typescript theme={null}
const res = await fetch("http://localhost/api/status", {
  unix: "/var/run/my-app.sock",
});
const data = await res.json();
```

***

## TLS options

Use a client certificate for mutual TLS authentication:

```typescript theme={null}
const res = await fetch("https://secure.example.com", {
  tls: {
    key: Bun.file("/path/to/client.key"),
    cert: Bun.file("/path/to/client.crt"),
    ca: Bun.file("/path/to/ca.crt"),
  },
});
```

Disable certificate verification (development only):

```typescript theme={null}
const res = await fetch("https://self-signed.example.com", {
  tls: {
    rejectUnauthorized: false,
  },
});
```

<Warning>
  Setting `rejectUnauthorized: false` disables TLS validation entirely. Never use this in production.
</Warning>

Custom server identity check:

```typescript theme={null}
const res = await fetch("https://example.com", {
  tls: {
    checkServerIdentity(hostname, cert) {
      if (hostname !== "expected.example.com") {
        return new Error("Hostname mismatch");
      }
    },
  },
});
```

***

## Additional protocols

Beyond HTTP and HTTPS, Bun's `fetch` supports several other URL schemes:

<AccordionGroup>
  <Accordion title="S3 — s3://">
    ```typescript theme={null}
    // Uses AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY / AWS_REGION env vars
    const res = await fetch("s3://my-bucket/path/to/object");

    // Or pass credentials explicitly
    const res2 = await fetch("s3://my-bucket/path/to/object", {
      s3: {
        accessKeyId: "ACCESS_KEY",
        secretAccessKey: "SECRET_KEY",
        region: "us-east-1",
      },
    });
    ```
  </Accordion>

  <Accordion title="File — file://">
    ```typescript theme={null}
    const res = await fetch("file:///home/user/data.json");
    const data = await res.json();
    ```
  </Accordion>

  <Accordion title="Data — data:">
    ```typescript theme={null}
    const res = await fetch("data:text/plain;base64,SGVsbG8sIFdvcmxkIQ==");
    const text = await res.text(); // "Hello, World!"
    ```
  </Accordion>

  <Accordion title="Blob — blob:">
    ```typescript theme={null}
    const blob = new Blob(["Hello, World!"], { type: "text/plain" });
    const url = URL.createObjectURL(blob);
    const res = await fetch(url);
    ```
  </Accordion>
</AccordionGroup>

***

## Performance

### DNS prefetching

Warm up the DNS cache before you need to make a request:

```typescript theme={null}
import { dns } from "bun";

dns.prefetch("api.example.com");

// ... later, when the request is made, DNS is already resolved
const res = await fetch("https://api.example.com/data");
```

### Preconnect

Start the TCP/TLS handshake before the request is issued:

```typescript theme={null}
import { fetch } from "bun";

fetch.preconnect("https://api.example.com");

// Or at startup via CLI
// bun --fetch-preconnect https://api.example.com server.ts
```

### Connection pooling

Bun automatically pools and reuses connections to the same host (HTTP keep-alive). To opt out for a single request:

```typescript theme={null}
const res = await fetch("https://api.example.com", {
  keepalive: false,
});
```

The default maximum number of simultaneous connections is 256. Raise it with:

```bash theme={null}
BUN_CONFIG_MAX_HTTP_REQUESTS=512 bun server.ts
```

***

## Debugging

Set `verbose: true` to print request and response headers to the terminal:

```typescript theme={null}
const res = await fetch("https://example.com", {
  verbose: true,
});
```

```
[fetch] > HTTP/1.1 GET https://example.com/
[fetch] > Connection: keep-alive
[fetch] > User-Agent: Bun/1.3.3
[fetch] > Accept: */*
[fetch] > Host: example.com

[fetch] < 200 OK
[fetch] < Content-Type: text/html; charset=UTF-8
[fetch] < Content-Length: 648
```

Pass `"curl"` for more detailed output mimicking `curl -v`.
