# The Data API (REST)

> How the auto-generated REST API works on SupaHost: project URLs, PostgREST, request headers, supabase-js and curl examples, row limits, the schema cache and capacity.

Source: https://supahost.app/docs/api/overview · Updated 2026-11-11

Every SupaHost project has a REST API generated from its database. Create a table, and you can read and write it over HTTPS straight away, with access decided by Postgres grants and Row Level Security. This is the Data API, served by [PostgREST](https://postgrest.org), and it's what `supabase.from(...)` and `supabase.rpc(...)` call under the hood.

## Where it lives

The Data API is at `/rest/v1` on your project's URL:

```
https://<project>.<instance>.supahost.space/rest/v1/
```

For project `app-one` on instance `acme`, a table called `recipes` is at `https://app-one.acme.supahost.space/rest/v1/recipes`. If you add a [custom domain](/docs/platform/custom-domains), the same paths work there too.

The project URL and keys are under *Dashboard → your project → **Connect** tab → **App***.

| Piece | On SupaHost |
|---|---|
| Server | PostgREST `v14.18` (release `2026.11.02`), one per project |
| Gateway | Caddy, which handles TLS, routing and the API key check |
| Exposed schemas | `public`, `storage`, `graphql_public`, `pgmq_public` (fixed) |
| Rows per request | at most 1000 |
| Database connections | a pool of 5 per project |

## What gets an endpoint

| In your database | Becomes |
|---|---|
| Table or view `public.recipes` | `/rest/v1/recipes` (GET, POST, PATCH, DELETE) |
| Function `public.search_recipes(...)` | `/rest/v1/rpc/search_recipes` |
| Anything in a schema that isn't exposed | Nothing |

Foreign keys become joins you can ask for in `select`. See [Querying and filtering data](/docs/api/querying) and [Calling database functions (RPC)](/docs/api/rpc).

## Making requests with supabase-js

```ts
import { createClient } from '@supabase/supabase-js'

const supabase = createClient(
  'https://app-one.acme.supahost.space',
  process.env.SUPABASE_ANON_KEY!
)

const { data, error } = await supabase
  .from('recipes')
  .select('id, title, servings')
  .gte('servings', 4)
  .order('title')
  .limit(20)
```

Once a user signs in with `supabase.auth`, the client sends their access token automatically and your RLS policies see them as `authenticated`.

## Making requests over plain HTTP

Any HTTP client works. Every request needs the `apikey` header (or an `apikey` query parameter). Add `Authorization: Bearer <token>` to act as a signed-in user.

```bash
# Read
curl "https://app-one.acme.supahost.space/rest/v1/recipes?select=id,title&servings=gte.4&order=title" \
  -H "apikey: $SUPABASE_ANON_KEY"

# Insert and get the new row back
curl -X POST "https://app-one.acme.supahost.space/rest/v1/recipes" \
  -H "apikey: $SUPABASE_ANON_KEY" \
  -H "Authorization: Bearer $USER_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Prefer: return=representation" \
  -d '{"title": "Lentil soup", "servings": 4}'
```

If you send only `apikey`, SupaHost's gateway copies it into `Authorization: Bearer`, so the request runs with that key's role. See [API keys](/docs/api/api-keys).

The full filter syntax is in the [PostgREST API reference](https://postgrest.org/en/stable/references/api.html), and every supabase-js method is in the [JavaScript reference](https://supabase.com/docs/reference/javascript/select).

## Who can see what

The anon key is public by design. Security comes from two layers in Postgres:

1. **Grants** decide whether `anon` or `authenticated` may touch a table at all.
2. **Row Level Security** decides which rows.

Enable RLS on every table in `public`. See [Securing your API](/docs/api/securing-your-api) and [Row Level Security](/docs/database/row-level-security).

## Limits worth knowing

- **1000 rows per request.** Larger results are cut at 1000 rows without an error. Page through with `.range()`; see [Querying and filtering data](/docs/api/querying).
- **Exposed schemas are fixed.** Only the four schemas above are reachable, and that list can't be changed. See [Exposed schemas](/docs/api/exposed-schemas).
- **Five database connections per project.** Each request holds a connection only while its query runs, so fast queries go a long way. Slow queries queue up behind each other; index what you filter on.
- **Throughput.** Load tests measured about 300 API reads per second on Builder, 540 on Pro and 950 on Business, shared by all projects on the instance.
- **Egress.** Response bytes count toward the instance's monthly egress allowance. Select only the columns you need.

## Schema cache

PostgREST keeps a cache of your tables, columns, relationships and functions. It reloads when you change the schema, so new tables normally appear within moments. If one doesn't, reload it by hand:

```sql
notify pgrst, 'reload schema';
```

See [PostgREST not seeing new tables, columns or functions](/kb/postgrest-schema-cache).

## What isn't available

- **GraphQL.** There's no `/graphql/v1` route on SupaHost, so pg_graphql over HTTP isn't available, even though the `graphql_public` schema exists.
- **Other paths.** The project URL only routes `/rest/v1`, `/auth/v1`, `/storage/v1`, `/functions/v1` and `/realtime/v1`. Anything else returns 404 with `no Route matched with those values`.

## Browse your API

Studio's **API docs** page lists every table and function with ready-to-copy supabase-js and curl snippets. See [Using Supabase Studio](/docs/database/studio).

## Related

- [HTTP status codes from the Supabase APIs](/kb/http-status-codes)
- [select returns an empty array but the table has data](/kb/empty-data-array)
- [Generating TypeScript types](/docs/api/generating-types)
