API
A REST API generated from your tables (PostgREST). Filter, sort and page with query parameters; results are JSON.
Base URL
https://bfvxjkcqdihgaxadebkr.supabase.co/rest/v1/<table>Required headers
apikey: <publishable key — Heimaleiga gives you this with your credential>
Authorization: Bearer <service key or session token>
Accept-Profile: <your owner schema, e.g. ylma>The Accept-Profile header selects your owner schema. Heimaleiga tells you its name with your credential. Tables: listings, reservations, reviews, stats, sync_status.
Your first request
curl "https://bfvxjkcqdihgaxadebkr.supabase.co/rest/v1/reservations?select=confirmation_code,check_in,status,host_payout&status=eq.confirmed&check_in=gte.2026-01-01&order=check_in.desc&limit=20" \
-H "apikey: <publishable key — Heimaleiga gives you this with your credential>" \
-H "Authorization: Bearer $OWNER_KEY" \
-H "Accept-Profile: ylma"Python:
import requests, os
r = requests.get("https://bfvxjkcqdihgaxadebkr.supabase.co/rest/v1/stats",
params={"select": "check_in,listing_nickname,host_payout,owner_revenue", "check_in_year": "eq.2026", "order": "check_in"},
headers={"apikey": "<publishable key — Heimaleiga gives you this with your credential>", "Authorization": f"Bearer {os.environ['OWNER_KEY']}", "Accept-Profile": "ylma"})
rows = r.json()Filtering, sorting, paging
| Need | Query |
|---|---|
| Equality / comparison | status=eq.confirmed, check_in=gte.2026-01-01, nights_count=lt.3 |
| In a set | channel=in.(bookingCom,airbnb2) |
| Pattern | listing_nickname=like.SW* (case-sensitive), ilike for insensitive |
| Null checks | owner_revenue=is.null / not.is.null |
| Columns | select=confirmation_code,check_in,host_payout |
| Sort | order=check_in.desc,confirmation_code |
| Page | limit=1000&offset=1000, or the Range: 0-999 header. Default page is 1,000 rows — always sort when paging. |
| Count only | Header Prefer: count=exact with limit=0; read Content-Range. |
Full operator reference: PostgREST's documentation on tables and views. Anything it supports for reading works here.
Freshness
Data is copied from Heimaleiga's production every 15 minutes. GET /sync_status returns one row per table with last_success_at and a stale flag (no successful copy in the last 60 minutes). Check it before trusting a surprising number.
Errors and empties
- Empty array, HTTP 200 — the credential cannot see anything (wrong or missing owner, revoked key, MFA not satisfied) or the filter matched nothing. Deliberate: the API never reveals other owners' schema names.
- 401 — malformed or expired token. 42501 permission denied — you tried to write, or reached a table that is not yours.
- Response too large — page it; 1,000 rows per request is the ceiling.
Limits
Requests time out after 8 seconds of database work; narrow with filters rather than pulling whole tables repeatedly. There is no per-key rate limit beyond the platform's defaults, but a scheduled job should pull deltas (updated_at=gte.<last run>) rather than everything.
Auth base: https://bfvxjkcqdihgaxadebkr.supabase.co/auth/v1 (only relevant if you script a personal sign-in).