Supabase.
Query SiftingIO market data directly from Supabase with SQL. The Foreign Data Wrapper exposes prices, historical bars, and fundamentals as foreign tables, fetched on demand with no pipeline to maintain, and a companion Edge Function keeps a live quotes table fresh.
View on GitHubHow it works
The SiftingIO Foreign Data Wrapper is a read-only WebAssembly wrapper built on Supabase Wrappers. It exposes the SiftingIO API as foreign tables in a private siftingio schema, so you query live and historical market data with plain SQL. Rows are fetched on demand at query time, with no pipeline to build and nothing copied into your database until you choose to cache it.
What you'll need
The wrapper runs inside an existing Supabase project. Before you start, line up:
- A Supabase project with the
wrappersextension, version0.4.1or later. - A SiftingIO API key. Create one from your dashboard.
- Access to Supabase Vault to store the key, and the SQL editor or a migration to run the statements below.
Enable the Wrappers extension
Wrappers ships with Supabase. Enable it into the extensions schema:
create extension if not exists wrappers with schema extensions;Store your API key in Vault
Keep the key out of SQL and version control by storing it in Vault. The call returns a UUID you pass to the server as api_key_id:
-- Returns a UUID; use it as api_key_id below.
select vault.create_secret('<YOUR_SIFTINGIO_KEY>', 'siftingio_api_key');Create the wrapper and server
Register the WASM wrapper, then a server that points at the released package, the SiftingIO API, and your Vault key id.
create foreign data wrapper wasm_wrapper handler wasm_fdw_handler validator wasm_fdw_validator; create server siftingio_server foreign data wrapper wasm_wrapper options ( -- Use the latest values from the repo's releases page. fdw_package_url 'https://github.com/siftingio/supabase-fdw/releases/download/v0.2.0/siftingio_fdw.wasm', fdw_package_name 'siftingio:siftingio-fdw', fdw_package_version '0.2.0', fdw_package_checksum '<SHA256_FROM_RELEASE>', api_url 'https://api.sifting.io', api_key_id '<VAULT_UUID>' );checksum.txt before you create the server.Define foreign tables
Create a private schema and one foreign table per object you want to query. A few to start:
create schema if not exists siftingio; -- Latest top-of-book quote for any market.create foreign table siftingio.last_quote ( venue text, symbol text, bid numeric, bid_size numeric, ask numeric, ask_size numeric, ts timestamptz) server siftingio_server options ( object 'last_quote' ); -- Historical OHLCV bars.create foreign table siftingio.stocks_bars ( symbol text, ts timestamptz, open numeric, high numeric, low numeric, close numeric, volume numeric, interval text) server siftingio_server options ( object 'stocks_bars' ); create foreign table siftingio.crypto_bars ( symbol text, ts timestamptz, open numeric, high numeric, low numeric, close numeric, volume numeric, interval text) server siftingio_server options ( object 'crypto_bars' );The wrapper also exposes forex and DEX bars, last trade, currency conversion, and stock fundamentals such as profile, ratios, earnings, insiders, and financial statements. See the repository for every object and its full column list.
Query market data with SQL
Select from the foreign tables with the required filters. Symbols use USD-quoted form such as BTCUSD and EURUSD, and bar intervals are 1m, 5m, 15m, 30m, and 1h:
-- Latest crypto quote.select * from siftingio.last_quotewhere venue = 'crypto' and symbol = 'BTCUSD'; -- Hourly bars for a US stock.select ts, open, high, low, close, volumefrom siftingio.stocks_barswhere symbol = 'AAPL' and ts >= '2026-06-16' and interval = '1h'order by ts desc;How filters push down
The wrapper turns your WHERE clause into the API request, so a few rules matter:
- Required filters become the request. For bars,
symboland atslower bound are needed; quotes and trades needvenueandsymbol. - Predicates must be literal values. An expression like
now() - interval '5 days'will not push down. LIMITmaps to the API limit, and bars paginate automatically.- A query with no required filter returns zero rows, so always constrain the symbol.
Because joins do not push the symbol down, cache the rows you need first, then join against your own tables:
-- Cache the rows you need, then join against your own tables.create table my_quotes as select * from siftingio.last_quote where venue = 'stocks' and symbol = 'AAPL' union all select * from siftingio.last_quote where venue = 'stocks' and symbol = 'MSFT'; select p.symbol, p.shares, q.bid, q.askfrom public.portfolio pjoin my_quotes q using (symbol);Keep a live quotes table fresh
For continuously updated prices, the repo ships a companion Edge Function and a pg_cron schedule.
siftingio-quotes Edge Function pulls a snapshot on a pg_cron schedule and upserts into a public.market_quotes table protected by row-level security. Your app reads it like any table and subscribes to changes over Realtime. Per-table HTTP Edge Functions are also available for stateless access. See the repository for deployment steps.Security
- Keep your API key in Vault, never in SQL or version control.
- The foreign tables live in a private
siftingioschema with no row-level security, so do not expose that schema through your API. - Serve data to clients through
SECURITY DEFINERfunctions in thepublicschema, where you control access. - Verify the
fdw_package_checksumagainst the release before trusting the WASM package.
Notes and limits
- Read-only. The wrapper queries market data and never writes back to SiftingIO.
- On-demand reads count against your API plan. Cache or schedule to control volume.
- Prices are an aggregated fair-price reference computed across sources, consistent with the REST, WebSocket, and FIX products.
- The wrapper is an open-source integration under Apache-2.0, not an official Supabase product.
FAQ
- Do I need an ETL pipeline to use SiftingIO in Supabase?
No. The Foreign Data Wrapper queries the SiftingIO API on demand and returns rows directly, so there is nothing to copy or schedule. Add the optional Edge Function only if you want a continuously refreshed quotes table.
- Is the wrapper read-only?
Yes. It only reads market data from SiftingIO. You cannot write back through the foreign tables.
- Which data can I query?
Latest quotes and trades, historical OHLCV bars for stocks, crypto, forex, and DEX, currency conversion, and stock fundamentals and filings. The repository lists every foreign table and its columns.
- Why does a bare SELECT * return no rows?
Each table needs its required filter, for example symbol, or venue and symbol. Those filters form the API request, so a query without them returns zero rows or an error. Predicates must be literal values, not expressions like now() minus an interval.
- Can I join the foreign tables with my own tables?
Yes, but the symbol filter only pushes down for literal predicates, so a join will not pass the symbol through. Cache the rows you need into a regular table first, then join against it.
- Does every query use my API quota?
Yes. Reads fetch from the API on demand and count against your plan. Cache results or schedule the Edge Function to keep request volume under control.
Query market data from Supabase
Grab an API key, clone the wrapper, and you are running market-data SQL in minutes. The repository has the full object list, releases, and deployment steps.