sifting/io
Postgres · Foreign Data Wrapper

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 GitHub

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

Read-only, on demand
The wrapper never writes to SiftingIO and never runs in the background. Each query turns into an API request and counts against your plan, so cache results or schedule the Edge Function when you need steady, low-latency reads.

What you'll need

The wrapper runs inside an existing Supabase project. Before you start, line up:

  • A Supabase project with the wrappers extension, version 0.4.1 or 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.
Step 1

Enable the Wrappers extension

Wrappers ships with Supabase. Enable it into the extensions schema:

enable wrappers
create extension if not exists wrappers with schema extensions;
Step 2

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:

vault secret
-- Returns a UUID; use it as api_key_id below.
select vault.create_secret('<YOUR_SIFTINGIO_KEY>', 'siftingio_api_key');
Step 3

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.

wrapper + server
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>'  );
Pin the release
Take fdw_package_url, version, and checksum from the repository’s releases page, and verify the checksum against the release’s checksum.txt before you create the server.
Step 4

Define foreign tables

Create a private schema and one foreign table per object you want to query. A few to start:

foreign tables
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.

Step 5

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:

queries
-- 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, symbol and a ts lower bound are needed; quotes and trades need venue and symbol.
  • Predicates must be literal values. An expression like now() - interval '5 days' will not push down.
  • LIMIT maps 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 then join
-- 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.

Live quotes
The 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 siftingio schema with no row-level security, so do not expose that schema through your API.
  • Serve data to clients through SECURITY DEFINER functions in the public schema, where you control access.
  • Verify the fdw_package_checksum against 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.

Get started

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.