Skip to main content

Filtering and projection

By default a request returns every column of every row that the endpoint and your entitlement permit, in whatever order the engine chooses. Three query parameters let you narrow the result set:

  • columnsProjection. Comma-separated list of column names to include in the response. Returns only those columns.
  • sortOrdering. Column name optionally prefixed with + (ascending, default) or - (descending). Multiple sort fields may be separated by commas.
  • Pascal-case column namesFiltering. Any column name passed as a query parameter (e.g. Ticker=AAPL, TradeDate=2023-01-17) is treated as a row-level filter.

The third mechanism is the principal source of confusion when reading the OpenAPI spec, which lists only the standard parameters (sort, columns, offset, limit, response_format, plus a few endpoint-specific ones such as adjusted and aggregation_logic). Column-name filters are accepted by the engine but are not enumerated per-endpoint. The schema for a given dataset is reachable via GET /api/v1/meta/datasets/{dataset_id}/columns; that endpoint is your authoritative source for the legal filter set. The {dataset_id} placeholder takes the opaque catalog id (see the catalog chapter).

Projection: columns

The default response includes every column. For a wide dataset (the Trade-and-Quote tables can have 30+ columns) this is wasteful when you only need a few. The columns parameter solves that.

Project to just the five columns we care about, on adjusted AAPL trades.
curl -s -H "X-API-KEY: $ALGOSEEK_API_KEY" \
"https://dev-datasets-api.algoseek.com/api/v1/data/us-equity/eq-trades/2023-01-17/AAPL?adjusted=true&limit=3&columns=TradeDate,EventDateTime,Ticker,Price,Quantity"

The response now contains only those columns:

Captured response body — data_eq_trades_columns
{
"data": [
{
"TradeDate": "2023-01-17",
"EventDateTime": "2023-01-17T19:59:59.054144-05:00",
"Ticker": "AAPL",
"Price": 133.3122,
"Quantity": 5
},
{
"TradeDate": "2023-01-17",
"EventDateTime": "2023-01-17T19:59:57.718214-05:00",
"Ticker": "AAPL",
"Price": 133.3122,
"Quantity": 1
},
{
"TradeDate": "2023-01-17",
"EventDateTime": "2023-01-17T19:59:57.361405-05:00",
"Ticker": "AAPL",
"Price": 133.3713,
"Quantity": 1
}
],
"pagination": {
"offset": 0,
"limit": 3,
"next_offset": 3
}
}

Projection is the cheapest cost optimisation on the wire side: it reduces data_returned (the byte count that maps to account-level bandwidth quota) but does not reduce data_scanned (which is a function of how much the engine has to read to evaluate the row predicate). For scan reduction, push filters into TradeDate-narrowed ranges and per-ticker queries, covered below.

Ordering: sort

Pass the name of a column with no prefix or a + prefix to sort ascending; pass it with a - prefix to sort descending.

Sort the same query by event time, descending: most recent trade first.
curl -s -H "X-API-KEY: $ALGOSEEK_API_KEY" \
"https://dev-datasets-api.algoseek.com/api/v1/data/us-equity/eq-trades/2023-01-17/AAPL?adjusted=true&limit=3&sort=-EventDateTime"

Multiple sort fields are comma-separated; later fields break ties on earlier ones:

Sort by Price descending, then Quantity descending. Largest trades by price come first; ties broken by size.
curl -s -H "X-API-KEY: $ALGOSEEK_API_KEY" \
"https://dev-datasets-api.algoseek.com/api/v1/data/us-equity/eq-trades/2023-01-17/AAPL?adjusted=true&limit=3&sort=-Price,-Quantity"
warning

Pagination (limit/offset) on top of an unspecified sort is a footgun: if the engine's default order ever changes between pages, you can skip or repeat rows on page boundaries. For any walk that issues more than one page, always pass an explicit sort= on a stable monotonic column (typically EventDateTime for tick data, TradeDate for daily data, EffectiveDate for reference data).

Filtering: column names as query parameters

This is the API's most important convention and the one most likely to surprise you: any column on the dataset can be passed as a query parameter to filter rows by that column. The parameter name must match the column name exactly (Pascal case as it appears in the schema) and the value matches by equality.

Filter shares-outstanding history to one ticker. Note Ticker is capitalised — it is the column name, not the lowercase parameter name you might expect from REST style.
curl -s -H "X-API-KEY: $ALGOSEEK_API_KEY" \
"https://dev-datasets-api.algoseek.com/api/v1/data/us-equity-ref/eq-shares-outst-basic?Ticker=AAPL&limit=5"

:::caution Gotcha The shares-outstanding example above requires the Shares Outstanding entitlement. The dev key used to capture other examples in this guide is not entitled to it, so the live response is 403 with body {"detail":"The account is not authorized to access the dataset US Equities Basic Shares Outstanding"}. The captured sample below is from a separately-entitled environment and is shown for shape only. Datasets confirmed working with the dev key include eq-trades (with adjusted=true, i.e. eq_trades_adj), eq-trades-1min, eq-market-holidays, eq-ipo, and eq-adj-factors-basic; substitute one of those if you are reproducing on the dev key. :::

Captured response body — data_eq_shares_outst_basic
{
"detail": "The account is not authorized to access the dataset US Equities Basic Shares Outstanding"
}

Because filter names are column names, you discover them via the catalog endpoint (see the catalog chapter):

Inspect the column schema for a dataset. The path uses the opaque catalog dataset_id (here US6014 for fut_trades_1min). Every name listed in the response is a legal filter parameter for queries to this dataset's endpoint.
curl -s -H "X-API-KEY: $ALGOSEEK_API_KEY" \
"https://dev-datasets-api.algoseek.com/api/v1/meta/datasets/US6014/columns"
Captured response body — cat_meta_dataset_columns
[
{ "name": "TradeDate", "data_type": "date", "description": "The trading day" },
{ "name": "BarDateTime", "data_type": "date-time", "description": "The timestamp of the bar start (CST)" },
{ "name": "Ticker", "data_type": "string", "description": "Contract name" },
{ "name": "BaseSymbol", "data_type": "string", "description": "Base product name" },
{ "name": "OpenPrice", "data_type": "number", "description": "Price of the first trade" },
{ "name": "HighPrice", "data_type": "number", "description": "Trade with the highest price" },
{ "name": "LowPrice", "data_type": "number", "description": "Trade with the lowest price" },
{ "name": "ClosePrice", "data_type": "number", "description": "Price of the last trade" },
{ "name": "VolumeWeightPrice", "data_type": "number", "description": "Volume-weighted average price" },
{ "name": "TotalQuantity", "data_type": "integer", "description": "Total number of shares traded" },
{ "name": "BuyAggressorQuantity", "data_type": "integer", "description": "The number of shares traded with \"Aggressor on Buy\"" },
{ "name": "SellAggressorQuantity", "data_type": "integer", "description": "The number of shares traded with \"Aggressor on Buy\"" },
{ "name": "TotalTrades", "data_type": "integer", "description": "Total number of trades" },
{ "name": "BuyAggressorTrades", "data_type": "integer", "description": "The number of \"Aggressor on Buy\" trades" },
{ "name": "SellAggressorTrades", "data_type": "integer", "description": "The number of \"Aggressor on Sell\" trades" }
]

:::caution Gotcha The column-name filter convention is not listed in the endpoint's OpenAPI parameters. If you read the spec literally, you would conclude that the only legal query parameters are sort, columns, offset, limit, and response_format (plus a few endpoint-specific ones). You would also conclude that requests without filters always succeed. Neither is true: many query-style data endpoints require at least Ticker, and any column on the dataset can be passed as a filter. Use the columns endpoint above to discover the legal set per dataset; do not trust the OpenAPI spec on this point. :::

Path parameters vs. query filters

The data endpoints come in two URL shapes. Some take key identifiers as path parameters; others take them as query parameters in the column-name convention.

  • Path-style — e.g. GET /api/v1/data/us-equity/eq-trades/{trade_date}/{ticker}. Trade date and ticker are baked into the URL path itself. You cannot pass them as query parameters — the path is what selects the slice.
  • Query-style — e.g. GET /api/v1/data/us-equity/eq-trades-1min. Trade date and ticker are passed as query parameters using the column-name convention (TradeDate=2023-01-17, Ticker=AAPL).

The two shapes are functionally equivalent for the engine; the path shape is a courtesy convenience for endpoints whose TradeDate+Ticker pair is the natural key. Internal routing maps the path-style URLs onto the same query path used by the query-style endpoints, so the same filter, projection, sort, and pagination semantics apply.

Putting it all together

A realistic, well-shaped query touches all three mechanisms:

Project to the columns of interest, sort on the natural ordering column, filter to one ticker, page in chunks of 1000.
curl -s -H "X-API-KEY: $ALGOSEEK_API_KEY" \
"https://dev-datasets-api.algoseek.com/api/v1/data/us-equity/eq-trades-1min?Ticker=AAPL&TradeDate=2023-01-17&adjusted=true&columns=TradeDate,BarDateTime,Ticker,FirstTradePrice,LastTradePrice,Volume&sort=BarDateTime&limit=1000&offset=0"

In Python:

Same query in Python. Pass the parameters as a dict; requests URL-encodes everything correctly — including the +/- prefixes on sort.
import os, requests

API_KEY = os.environ["ALGOSEEK_API_KEY"]
url = ("https://dev-datasets-api.algoseek.com"
"/api/v1/data/us-equity/eq-trades-1min")
params = {
"Ticker": "AAPL",
"TradeDate": "2023-01-17",
"adjusted": "true",
"columns": "TradeDate,BarDateTime,Ticker,FirstTradePrice,LastTradePrice,Volume",
"sort": "BarDateTime",
"limit": 1000,
"offset": 0,
}
resp = requests.get(url, headers={"X-API-KEY": API_KEY},
params=params, timeout=60)
resp.raise_for_status()
print(resp.json()["pagination"])

The above is also the template most amenable to caching: a stable URL+param string maps cleanly to a cache key, and the engine output for a (TradeDate, Ticker, columns, sort) tuple is deterministic. If you build a per-pipeline disk cache, key it on the exact param string after lexicographic sort to maximise hit rate across runs.