A backend for small teams shipping more than one idea
Database, API, Auth, files, realtime, and logs—already wired.
Start with one free database. Move to three for $10 a month, with no seat fee. Keep direct SQL access and export whenever you want.
One database, 1 GB Postgres, 1 GB files, and no credit card.
Have a look
Filter the table and watch the REST URL change — it is the same query grammar the real endpoint parses. The rows are an example, and the preview says so.
| id | task | done | priority | created_at |
|---|---|---|---|---|
| 1 | Design the schema | 1 | 2026-02-03 | |
| 2 | Add row-level security | 1 | 2026-02-04 | |
| 3 | Wire up the webhook | — | 2 | 2026-02-08 |
| 4 | Write the migration | — | 1 | 2026-02-09 |
| 5 | Ship it | — | 3 | 2026-02-11 |
The real editor edits cells in place and writes straight to Postgres. The filters above are the same ones the REST tab turns into a URL.
Everything per project
Each project is a complete backend: tables, REST, RLS, Auth, files, realtime, durable webhooks, request logs, vectors, functions, and snapshots you can restore.
An isolated Postgres database with its own role and connection string. Use Prisma, Drizzle, psql, or any driver you already know.
Create tables over HTTP or SQL. Every table becomes an endpoint with filters, ordering, pagination and batch writes.
Run migrations and queries from the browser as your own database role. No hidden layer between you and Postgres.
Email and password accounts, rotating refresh tokens, logout, and per-project JWTs that work directly with row-level security.
Public or private buckets, signed uploads and downloads, MIME rules, and file metadata in your own database.
Postgres changes, filtered subscriptions, Broadcast, and Presence over WebSocket.
See API status, latency, request IDs, Auth outcomes, and every webhook attempt without storing bodies, tokens, or credentials.
Included on every plan, including Free. No Team SKU, no seat fee, and no organisation surcharge — unlike Supabase’s $25 org.
One toggle adds pgvector. Send your own floats, or turn Embed on and POST ?embed=true with a string — we fill vector(768). Search is POST /vector.
Write a function in SQL. Call it over HTTP: POST /rpc/your_function. No edge runtime — it runs in your database, as your role.
Paste any HTTPS URL. We POST signed row and schema changes, with visible retries. Same delivery as a webhook, not an edge runtime.
Download or restore a project from Settings. Pro and above also keep a daily copy. Not point-in-time recovery — only those snapshots.
Deploy an ordered group of schema changes once. Safe retries skip migrations that already succeeded.
One project, many doors
Create a table once. It is a REST resource, a realtime stream, a vector index if you want one, and a table in psql. Functions you write are POST /rpc.
create table todos (
id serial primary key,
task text not null,
done boolean not null default false,
created_at timestamptz not null default now()
);Send plain text, not vectors. starterpg automatically generates embeddings when you insert or update text with ?embed=true, and embeds your search question too. Choose your language for a complete working example: create a table, import help articles, search, filter, update, and search again. No embedding-provider key, SDK, or custom SQL function is needed.
export PROJECT_URL="https://www.starterpg.com/api/v1/your-project"
export API_KEY="your-server-side-secret-key"Run server-side only: never expose this secret key in browser or mobile code. Use a project without a help_articles table, or skip the first schema request if you already created the same table in the walkthrough. The examples use vector(768) and upsert sample IDs 1–3, so use a demo table, not production data. Writes and searches consume your embedding allowance. Search output contains id, title, content, and score; scores depend on your content.
Save as auto-embed.sh and run: bash auto-embed.sh. Requires Bash and cURL 7.76+.
#!/usr/bin/env bash
set -euo pipefail
: "${PROJECT_URL:?Set PROJECT_URL to your project API URL}"
: "${API_KEY:?Set API_KEY to your server-side secret key}"
export PROJECT_URL="${PROJECT_URL%/}"
# Create the table once (skip this step if it already exists)
curl --fail-with-body -X POST "$PROJECT_URL/schema" \
-H "apikey: $API_KEY" \
-H "content-type: application/json" \
-d '{
"kind": "create_table",
"name": "help_articles",
"columns": [
{
"name": "id",
"type": "integer",
"primaryKey": true
},
{
"name": "title",
"type": "text",
"nullable": false
},
{
"name": "category",
"type": "text",
"nullable": false
},
{
"name": "published",
"type": "boolean",
"default": "false"
},
{
"name": "content",
"type": "text",
"nullable": false
},
{
"name": "embedding",
"type": "vector(768)",
"nullable": false
}
]
}'
printf '\n'
# Send text; starterpg automatically generates and stores embeddings
curl --fail-with-body -X POST "$PROJECT_URL/rest/help_articles?embed=true&on_conflict=id" \
-H "apikey: $API_KEY" \
-H "Prefer: resolution=merge-duplicates,return=minimal" \
-H "content-type: application/json" \
-d '[
{
"id": 1,
"title": "Reset your password",
"category": "account",
"published": true,
"content": "Use Forgot password on the login page to receive a reset link by email."
},
{
"id": 2,
"title": "Change your plan",
"category": "billing",
"published": true,
"content": "Open Billing in your dashboard to upgrade your subscription or change your plan."
},
{
"id": 3,
"title": "Invite a teammate",
"category": "account",
"published": false,
"content": "Open your team settings and send an invitation to a colleague by email."
}
]'
printf '\n'
# Search with a sentence; starterpg also embeds the query
curl --fail-with-body -X POST "$PROJECT_URL/vector/help_articles?embed=true" \
-H "apikey: $API_KEY" \
-H "content-type: application/json" \
-d '{
"text": "I cannot sign in because I forgot my password",
"column": "embedding",
"metric": "cosine",
"select": "id,title,content",
"limit": 3
}'
printf '\n'
# Search only published articles in the account category
curl --fail-with-body -X POST "$PROJECT_URL/vector/help_articles?embed=true" \
-H "apikey: $API_KEY" \
-H "content-type: application/json" \
-d '{
"text": "I cannot sign in because I forgot my password",
"column": "embedding",
"metric": "cosine",
"select": "id,title,content",
"limit": 3,
"filters": {
"category": "eq.account",
"published": "eq.true"
}
}'
printf '\n'
# Update text and automatically regenerate its embedding
curl --fail-with-body -X PATCH "$PROJECT_URL/rest/help_articles?id=eq.1&embed=true" \
-H "apikey: $API_KEY" \
-H "Prefer: return=minimal" \
-H "content-type: application/json" \
-d '{
"content": "Choose Forgot password on the login page. Open the emailed reset link and choose a new password."
}'
printf '\n'
# Search again using the updated content
curl --fail-with-body -X POST "$PROJECT_URL/vector/help_articles?embed=true" \
-H "apikey: $API_KEY" \
-H "content-type: application/json" \
-d '{
"text": "I cannot sign in because I forgot my password",
"column": "embedding",
"metric": "cosine",
"select": "id,title,content",
"limit": 3
}'
printf '\n'
Use the same pattern for product discovery, a searchable FAQ, or internal knowledge search: replace the article text with your content and search in natural language. Include ?embed=true when searchable text changes; without it, a normal text update leaves the old embedding unchanged. See the step-by-step guide for indexing, relevance tuning, RLS, and troubleshooting.
How it works
01
Name it and choose a starter schema. An isolated Postgres database and scoped login role are provisioned in seconds.
02
Use the schema API, write SQL in the editor, or connect with psql and run migrations you already have.
03
Your tables are already an API. Grab the key and start querying from your app.
Pricing
Every plan includes the complete product. Free can dump and restore a project; Pro and above also keep a daily snapshot — the last two copies. That is not point-in-time recovery. Plan allowances are hard caps, so more databases, storage, or egress requires an upgrade. Inviting teammates and vector search are included. There is no Team SKU and no seat fee.
Database size includes indexes, so it reads higher than your rows alone. Storage grows automatically as you fill it — there is nothing to provision.
starterpg plan allowances are hard caps; the calculator chooses the lowest plan that includes the requested capacity. Supabase figures estimated from their published rates: $25 base, ~$10 per additional project, 8 GB of database included per project then $0.125/GB, 100 GB of file storage then $0.021/GB. Check their pricing page for the current numbers.
Free
$10/ month
$29/ month
$399/ month
$799/ month
Free can take a snapshot in Settings, download it, or put it back. Pro and above keep the last two daily copies. Not a restore SLA, and not point-in-time recovery.
Included on every plan, including Free. There is no Team SKU, no seat fee, and no organisation surcharge — unlike Supabase’s $25 org. Create a team and invite people from the dashboard.
Supabase includes much more traffic and storage in Pro. starterpg is priced for teams with several small databases: three projects are $10 here versus an estimated $45 there. Teammates are included.
| Databases | starterpg | Supabase Pro, Micro |
|---|---|---|
| 1 | $10 / Pro | $25 |
| 3 | $10 / Pro | $45 |
| 8 | $29 / Pro+ | $95 |
Supabase: $25 organisation + $10 Micro per project − $10 credit that covers one project, from supabase.com/pricing. Their Pro includes 8 GB disk per project, 100 GB files, and 250 GB egress. Ours starts smaller: Pro includes 2 GB / 5 GB / 3 GB, enforced as plan caps. If you run one high-traffic database, Supabase may be the better value. If you ship several small products or environments, starterpg is designed for that shape.
Yes — a real Postgres database, with its own login role and connection string. Point psql, Prisma, Drizzle or any Postgres driver at it. Nothing about your schema is proprietary.
Settings can dump the project, download the file, or restore it. Pro and above also get a daily snapshot. That is not point-in-time recovery — only those copies. You can still pg_dump the connection string yourself.
You skip provisioning, connection handling, and writing a CRUD API. Creating a project gives you a database plus generated REST endpoints, email/password auth, file storage, change subscriptions, vector search, and HTTP calls to your own Postgres functions.
Yes, optional. Secret API keys (sk_) bypass RLS. Publishable keys (pk_) are subject to whatever policies you write. If you enable RLS and add no policy, publishable keys cannot read or write that table.
In the United States, on a shared Postgres instance, as its own database with its own role. Other tenants cannot CONNECT to it. Dedicated instances are not offered. There is no uptime SLA.
Free is one database, 500 MB growing to 1 GB, 1 GB of files, 1 GB egress, 20k embed tokens, paused after 7 days idle, and a manual snapshot. Pro is $10/month for 3 databases with 2 GB Postgres, 5 GB files, 3 GB egress, and 200k embed tokens. Pro+ is $29 for 8 databases with 10 GB / 20 GB / 10 GB / 1M tokens. Those allowances are hard caps; more capacity requires the next plan. Your own vector floats do not consume embed tokens.
Supabase Pro includes much more storage and traffic for one busy project. starterpg is optimized for several small projects: Pro is $10 for three databases and Pro+ is $29 for eight, with teammates included. Choose Supabase for its larger ecosystem, edge runtime, and high included traffic; choose starterpg when direct Postgres plus several inexpensive backends is the better fit.
Yes. The project Logs tab shows seven days of API status, latency, normalized paths, request IDs, and end-user Auth outcomes. Responses include X-Starterpg-Request-Id for correlation. Bodies, query strings, credentials, tokens, and email addresses are not stored. Webhook delivery history has separate status filters, delivery-ID search, payload inspection, and replay.
Only if you turn Embed on in the project and pass ?embed=true. Otherwise you send floats yourself. Hosted embed uses Google’s gemini-embedding-001 into vector(768) and counts against the plan’s monthly token wall.
Yes — ordinary Postgres functions in the public schema. Create them in the SQL editor, then POST /rpc/<name> with named arguments. They run as your database role. There is no separate edge-function runtime. For outbound work, paste an invoke URL: we POST signed row-change events to your HTTPS endpoint and show every retry.
Immediately. Switch plans and the new allowances apply at once. Unused time on the old plan is credited on the next invoice.
Yes, on every plan — including Free. Create a team in the dashboard and invite accounts by email. There is no Team SKU, no seat fee, and no organisation surcharge — unlike Supabase’s $25 org.