> ## Documentation Index
> Fetch the complete documentation index at: https://fruitstand.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# How to Get Historical Stock Prices into Snowflake (3 Ways)

> Three ways to get historical stock prices into Snowflake: a Marketplace listing, loading files from S3 with COPY INTO, or an API pipeline, with SQL for each.

*By [Fruit Stand Team](https://fruitstand.dev/about.md) · October 31, 2026 · Guides*

There are three ways to get historical stock prices into Snowflake:
subscribe to a **Snowflake Marketplace listing**, **load files** with
`COPY INTO` or Snowpipe, or **pull from a market data API** with a Python
pipeline. A Marketplace listing is the fastest. The data appears as a
read-only database in your account within minutes, with nothing to build
or maintain.

Loading files or running your own API pipeline gives you full control, but
you also take on keeping the adjusted price history correct.

## The short answer

| Method | Time to first query | Ongoing work | Best for |
| - | - | - | - |
| Snowflake Marketplace listing | Minutes | None, the provider refreshes it | Most teams; anyone who doesn't want to run a market data pipeline |
| Upload a CSV you already have (Snowsight or `PUT`) | Minutes | Uploading a new file every time you want newer prices | One-off analysis of raw data you already have |
| Files from S3, Azure or GCS (`COPY INTO`, Snowpipe) | Hours to days | Restating history, monitoring loads | Teams that already license a vendor's flat-file feed |
| API pipeline (Python, Airflow, dbt) | Days to weeks | Rate limits, symbol changes, corporate actions, retries | Custom universes or vendors with no Snowflake delivery |

The rest of this post walks through each option, with SQL and the gotchas
that usually show up a few months in.

## Option 1: Subscribe to a Snowflake Marketplace listing

Marketplace data is delivered through Snowflake's secure data sharing.
The provider's tables show up as a database in your account. Nothing is
copied, you don't pay storage for it, and each day's refresh appears
without you doing anything. You only pay for the warehouse compute your
queries use, plus the listing's price if it's a paid listing.

**1. Find the listing**

Search the Snowflake Marketplace for stock price data. The [Historical Stock Prices listing](https://app.snowflake.com/marketplace/listing/GZTYZTG3LNFA) covers 50,000+ US stocks, ETFs and mutual funds with raw and adjusted OHLC back to each security's listing date.

**2. Get it**

Click **Get**, choose the database name the listing will be mounted as (for example `STOCK_PRICES`), and pick which roles can access it.

**3. Query it**

Select that database and its schema in a worksheet and query the views directly.

Once it's mounted, the 10-year total return of three tickers is one query:

```sql theme={null}
select
    code,
    max_by(adjusted_close, date) / min_by(adjusted_close, date) - 1 as total_return_10y
from security_price_history_vw
where code in ('AAPL', 'VTI', 'VFIAX')
  and exchange_code = 'US'
  and date between '2016-01-01' and '2025-12-31'
group by code;
```

Check these points when comparing stock price listings:

* **Coverage.** Some listings cover only large caps such as the S\&P 500.
  Others include small caps, ETFs and mutual funds. The Historical Stock
  Prices listing has about 18,500 stocks, 5,950 ETFs and 25,800 mutual
  funds in one table.
* **History depth.** Some listings start at a fixed year. Others go back
  to each security's first trading day.
* **Adjusted prices.** Check that adjusted OHLC is included, and that the
  adjusted history is restated when splits and dividends post. See
  [Adjusted Close vs. Close](https://fruitstand.dev/blog/adjusted-close-vs-close.md) for why this
  matters.
* **Delisted securities.** If you backtest, check whether delisted tickers
  are included. Most listings, including ours today, cover securities
  currently listed, which introduces survivorship bias over long periods.
* **Refresh time and price.** Check when each day's data lands and
  whether pricing is flat-rate or usage-based. Historical Stock Prices is
  \$100/month flat, refreshed daily at 5:00 AM Pacific.

## Option 2: Load files from cloud storage

If your vendor delivers flat files (CSV or Parquet) to a bucket, Snowflake
can load them directly. Create a target table, point a stage at the
bucket, and run `COPY INTO`:

```sql theme={null}
create table if not exists daily_prices (
    code            varchar,
    date            date,
    open            number(18, 6),
    high            number(18, 6),
    low             number(18, 6),
    close           number(18, 6),
    adjusted_close  number(18, 6),
    volume          number(38, 0)
);

create file format if not exists prices_csv
    type = csv
    skip_header = 1;

create stage if not exists prices_stage
    url = 's3://your-bucket/prices/'
    storage_integration = your_s3_integration
    file_format = prices_csv;

copy into daily_prices
from @prices_stage
pattern = '.*[.]csv';
```

`COPY INTO` keeps load metadata and skips files it has already loaded, so
rerunning it is safe. The flip side is that if your vendor resends a
corrected file under the same name, it's silently skipped. Have restated
files land under new names (a dated folder works well), or reload them with
`force = true` into a staging table.

To load new files automatically as they arrive, wrap the same `COPY INTO`
in a pipe with event notifications on the bucket:

```sql theme={null}
create pipe if not exists prices_pipe
    auto_ingest = true
as
copy into daily_prices
from @prices_stage;
```

### Already have the raw data as a CSV? Upload it directly

If you already have price history in a CSV, for example an export from a
vendor or a broker, you don't need a bucket at all. In Snowsight, open the
target table and use **Load Data** to upload the file through the browser.
From SnowSQL or the Snowflake CLI, `PUT` the file into the table's own
stage and copy it in:

```sql theme={null}
put file:///path/to/prices.csv @%daily_prices;

copy into daily_prices
from @%daily_prices
file_format = (format_name = 'prices_csv');
```

The downside is that a CSV is a snapshot. It stops at the last day in the
file, so every time you want newer prices you have to get a new file and
upload it again. That's fine for a one-off analysis, but for anything that
needs current prices, use a scheduled load or a Marketplace listing that
refreshes for you.

**The catch: append-only loading gets adjusted prices wrong.** Every split
or dividend changes the adjusted price of *every earlier day* for that
security. A pipe that only appends new days leaves stale adjusted history
in place. Returns then jump on ex-dividend dates. Plan to land full-history
refreshes in a staging table and `MERGE` them into the main table:

```sql theme={null}
merge into daily_prices t
using daily_prices_staging s
    on t.code = s.code and t.date = s.date
when matched then update set
    open = s.open, high = s.high, low = s.low, close = s.close,
    adjusted_close = s.adjusted_close, volume = s.volume
when not matched then insert (code, date, open, high, low, close, adjusted_close, volume)
    values (s.code, s.date, s.open, s.high, s.low, s.close, s.adjusted_close, s.volume);
```

## Option 3: Pull from a market data API

With no file feed available, the usual route is a Python job. It calls a
market data API per ticker or in bulk, builds a DataFrame and writes it to
Snowflake with `write_pandas`. Schedule it with Airflow, Dagster or a cron
job.

```python theme={null}
import os

import snowflake.connector
from snowflake.connector.pandas_tools import write_pandas

conn = snowflake.connector.connect(
    account=os.environ["SNOWFLAKE_ACCOUNT"],
    user=os.environ["SNOWFLAKE_USER"],
    password=os.environ["SNOWFLAKE_PASSWORD"],
    warehouse="LOAD_WH",
    database="MARKET_DATA",
    schema="RAW",
)

# fetch_prices() is your vendor's client: return one row per ticker per day
# with columns CODE, DATE, OPEN, HIGH, LOW, CLOSE, ADJUSTED_CLOSE, VOLUME.
df = fetch_prices(tickers=["AAPL", "VTI", "VFIAX"], start="2000-01-01")

write_pandas(conn, df, "DAILY_PRICES_STAGING", auto_create_table=True, overwrite=True)
```

Then run the same `MERGE` as above to move the staging table into
`daily_prices`.

Plan for this work before committing to an API pipeline:

* **Rate limits.** Free tiers often allow only tens to a few hundred calls
  a day. A full US backfill of tens of thousands of tickers needs a bulk
  endpoint or many days of throttled calls.
* **Universe maintenance.** New listings, ticker changes and delistings
  happen every week. Without a refreshed symbol list, the table drifts
  away from the market.
* **Corporate actions.** Detect new splits and dividends and refetch those
  securities' full history, or adjusted prices go stale (see Option 2).
* **Bad rows.** Watch for zero or placeholder prices, duplicate days and
  stub rows before the real trading history begins. These break returns
  quietly.
* **Licensing.** Many free APIs, including unofficial Yahoo Finance
  wrappers, are for personal use only. Check the terms before loading the
  data into a shared warehouse.

## Check your load

Whichever method you pick, run these two checks before trusting the data.
First, there should be exactly one row per ticker per day:

```sql theme={null}
select code, date, count(*) as rows_per_day
from daily_prices
group by code, date
having count(*) > 1;
```

Second, every ticker should be current:

```sql theme={null}
select code, max(date) as latest_date
from daily_prices
group by code
having max(date) < dateadd('day', -5, current_date())
order by latest_date;
```

The first query should return nothing. The second lists tickers that have
stopped updating. They may be delisted or renamed, or a load may have
failed.

## Which should you choose?

* **You want to analyze prices, not maintain a pipeline:** use a
  Marketplace listing.
* **You already pay for a vendor's flat-file feed:** load the files from
  storage with Snowpipe, and `MERGE` full-history refreshes.
* **You need a custom universe or a vendor with no Snowflake delivery:**
  build the API pipeline, and budget time for corporate actions and
  monitoring.

## F.A.Q.

> [accordion] Is there free historical stock price data in Snowflake?
>
> Yes, you can try it free. The [Historical Stock Prices listing](https://app.snowflake.com/marketplace/listing/GZTYZTG3LNFA) comes with a free 30-day trial, so you can query the full dataset (50,000+ US stocks, ETFs and mutual funds with raw and adjusted history) before paying anything. After the trial it's \$100/month flat.

> [accordion] Do I pay storage for Marketplace data?
>
> No. Shared data stays in the provider's account and is mounted into yours as a read-only database. You pay only for the compute your queries use, plus the listing's subscription if it has one.

> [accordion] Can I get ETF and mutual fund prices the same way?
>
> Yes. The Historical Stock Prices listing keeps stocks, ETFs and mutual fund NAVs in the same table. Filter by `asset_class` in `security_vw` (`stock`, `etf` or `fund`).

> [accordion] Can I join Marketplace stock data with my own tables?
>
> Yes. A mounted listing is queried like any other database, so you can join it to your holdings, transactions or other Marketplace data such as [FRED economic series](https://fruitstand.dev/datasets/fred-series.md) in the same query.

> [accordion] Is this real-time data?
>
> No. Everything in this post is end-of-day data, one row per security per trading day. Intraday or real-time feeds need a streaming vendor and a different setup.

To skip building the pipeline, start a free 30-day trial of the
[Historical Stock Prices listing on the Snowflake Marketplace](https://app.snowflake.com/marketplace/listing/GZTYZTG3LNFA).
It has daily raw and adjusted OHLC for 50,000+ US stocks, ETFs and mutual
funds, refreshed every morning. The schema and more queries are in the
[dataset docs](https://fruitstand.dev/datasets/historical-stock-prices.md). Questions? Reach out at
[contact@fruitstand.dev](mailto:contact@fruitstand.dev).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.