---
title: API Methods
description: Make schema-validated GET, POST, PUT, PATCH, and DELETE requests with the api method helpers.
type: package
package: "@zap-studio/fetch"
---

`api.get`, `api.post`, `api.put`, `api.patch`, and `api.delete` are HTTP method helpers bound to `$fetch`. Use them when the HTTP method is known up front and the response should be validated.

```ts
import { api } from "@zap-studio/fetch";
```

## Methods

| Method       | HTTP method |
| ------------ | ----------- |
| `api.get`    | `GET`       |
| `api.post`   | `POST`      |
| `api.put`    | `PUT`       |
| `api.patch`  | `PATCH`     |
| `api.delete` | `DELETE`    |

Each helper has the same overloads, options, and error behavior as [`$fetch`](/fetch/validated-fetch-mode), but injects its HTTP method for you. The injected method always wins — a `method` passed in `options` is overridden.

:::note

The schema argument always validates the **response** body. It does not validate `json` or `body` request payloads.

:::

## Read Data

```ts
import { api } from "@zap-studio/fetch";
import { z } from "zod";

const UserSchema = z.object({
  id: z.string(),
  name: z.string(),
});

const user = await api.get("https://api.example.com/users/1", UserSchema);
```

## Send JSON

Use the `json` option for JSON request bodies. It is stringified with `JSON.stringify` and gets `Content-Type: application/json` when no content type is already set.

```ts
import { api } from "@zap-studio/fetch";
import { z } from "zod";

const CreateUserResponseSchema = z.object({
  id: z.string(),
  name: z.string(),
});

const user = await api.post("https://api.example.com/users", CreateUserResponseSchema, {
  json: {
    name: "Ada",
  },
});
```

`CreateUserResponseSchema` validates the response from the server. The `json` object is only the outgoing request body.

## Use Native Body

Use native `body` when sending `FormData`, `Blob`, `ReadableStream`, or an already-serialized payload.

```ts
import { api } from "@zap-studio/fetch";
import { z } from "zod";

const ProfileSchema = z.object({
  id: z.string(),
  avatarUrl: z.string(),
});

const avatarBytes = new Uint8Array([137, 80, 78, 71]);
const avatar = new Blob([avatarBytes], { type: "image/png" });

const formData = new FormData();
formData.set("avatar", avatar, "avatar.png");

const profile = await api.patch("https://api.example.com/profile", ProfileSchema, {
  body: formData,
});
```

`body` and `json` cannot be used together — providing both throws a `TypeError`.

## Query Params

`searchParams` accepts the same inputs as `new URLSearchParams(...)`.

```ts
import { api } from "@zap-studio/fetch";
import { z } from "zod";

const UserListSchema = z.array(z.object({ id: z.string(), name: z.string() }));

const page = await api.get("https://api.example.com/users", UserListSchema, {
  searchParams: {
    page: "1",
    limit: "20",
  },
});
```

## Non-Throw Modes

HTTP errors and validation issues throw by default. You can disable either behavior per request.

```ts
import { api } from "@zap-studio/fetch";
import { z } from "zod";

const UserSchema = z.object({ id: z.string(), name: z.string() });

// Does not throw FetchError on non-2xx responses
const user = await api.get("https://api.example.com/users/missing", UserSchema, {
  throwOnFetchError: false,
});

// Returns a Standard Schema result object instead of throwing ValidationError
const result = await api.get("https://api.example.com/users/1", UserSchema, {
  throwOnValidationError: false,
});

if (result.issues) {
  console.error(result.issues);
} else {
  console.log(result.value);
}
```

See [Validation](/fetch/validation) for details on the result object shape.

## Raw Responses

The method helpers mirror the `$fetch` overloads, so calling one without a schema returns the native `Response`:

```ts
import { api } from "@zap-studio/fetch";

const response = await api.delete("https://api.example.com/sessions/current");

console.log(response.status);
```

For clarity, prefer [`$fetch`](/fetch/raw-fetch-mode) directly when raw response handling is the main goal.
