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.

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, 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, 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 and Calling database functions (RPC).

Making requests with supabase-js

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.

# 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.

The full filter syntax is in the PostgREST API reference, and every supabase-js method is in the JavaScript reference.

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 and 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.
  • Exposed schemas are fixed. Only the four schemas above are reachable, and that list can't be changed. See 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:

notify pgrst, 'reload schema';

See PostgREST not seeing new tables, columns or functions.

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.

Updated 11 November 2026 · View as Markdown · Something wrong or missing? Tell us