Click three chips on the live feed and look at your address bar:
That is the entire filter. There is no hidden state in the page, no session-side view object, nothing you have to re-derive. Every chip writes its values back into the query string, the server re-renders from that string, and the table dims with an "Updating…" pill while the transition runs. Which means the fastest way to hand someone your exact screen is to copy the URL, and the fastest way to build a view you can't quite find in the UI is to type the parameters yourself.
Worth knowing up front: the feed only shows rows the Verdict Engine has already scored. A filter that returns nothing may be a correct filter over an unscored minute.
Values OR inside a dimension, dimensions AND across each other
Six keys are recognised, and one of them (watchlist) we'll come back to.
tickers takes short symbols, uppercased for you. providers takes provider ids, the text keys from public.providers, not display names. The other four are tag dimensions, and the parameter name is the raw dimension id rather than the label you see on the chip:
Comma-separated values inside one key mean OR. Separate keys mean AND. So corp_activity=earnings,ma&market_country=US reads as "earnings or M&A, in the US", and it's evaluated in SQL by the feeds_matching_tags() RPC rather than by pulling ids into JavaScript first.
The Country chip is worth a note because it doesn't behave like its neighbours. Topic, Market and Sector are populated from the tags dictionary, so every option you see has data behind it. Country is rendered from the full ISO 3166-1 alpha-2 list client-side, flags and all, so you can select Vietnam on a day nobody published anything Vietnamese and get an empty table. That's the filter working. The same is true of market_country on the REST side.
The Tickers dropdown has its own edge. It's built from recent feed_symbols rows and capped at 500 distinct symbols, and the list renders 300 at a time. A thinly covered name can be absent from the dropdown while still being a perfectly valid filter value. Type it into the chip's search box, or put it in the URL by hand. Both work.
Overrides land one axis at a time
Add watchlist=<uuid> and the saved filter on that watchlist becomes your base. Anything else in the URL overrides it, per axis, and the merge is the load-bearing part:
Note the tag branch. It merges per dimension, so ?watchlist=…&market_country=GB swaps the country on a saved energy view and leaves its Topic and Sector selections intact. If that spread were a plain assignment, one URL parameter would silently clear three chips, and this guide would be mostly about that bug.
You now have a composable view: one saved lens, temporarily bent along a single axis, expressed as a link.
Unchecking a chip is not the same as deleting the key
Here's the step people trip on, usually while scripting a URL rather than clicking.
Parameters are three-valued, not two. Absent means "no opinion, use the watchlist's value". Present with values means "override with these". Present and empty, ?market=, means "override with nothing", which is how you clear one dimension of a saved view without clearing the rest. The UI writes that empty form deliberately:
So if you build a link by hand and drop the key you meant to clear, the watchlist's value springs back and you get more rows than you asked for, with no error anywhere. Keep the trailing =.
- 1watchlist=<uuid> loads the saved filterbase
- 2URL keys override it, one axis eachabsent · empty · valued
- 3loadFilteredFeeds runs feeds_matching_tags()100 rows, scored only
- 4toGatewayFilter subscribes the socketno reconnect
One honesty note about the (modified) marker next to the view name. It's presence-based, not value-based: it turns on when any filter key appears in the URL alongside a watchlist, even when the values are identical to what's saved. Paste a fully-specified link to your own view and you'll see (modified) and a Save button that would write back exactly what's already there. Harmless, mildly confusing, and on my list.
When a temporary view earns permanence you have two buttons. Save calls updateWatchlistFilter, writes the resolved filter into the row, then pushes you to /app/feed?watchlist=<id> with the override parameters stripped, because they're the saved state now. Save as new asks for a name (80 characters, required) and appends a fresh watchlist at the end of your sidebar order. The new one carries no bookmark colour; if you want the amber, use Duplicate on the original instead and rename the copy.
Same question, asked server-side
The tag dimensions are the public contract, so the query string you built by clicking is nearly the query string you send to REST:
Four differences to plan around. symbol is singular and takes one ticker, so the dashboard's tickers=NVDA,AMD is two requests and a merge on your side. aggregator and exchange are singular too. limit is capped by your plan (50 on Starter, 200 on Pro, 500 on Quant) rather than rejected, so ask for 500 on a Starter key and you'll quietly receive 50 with a next_cursor to continue from. And on Free the call doesn't run at all: you get a 403 with code: "historical_api_unavailable", because Free is realtime-only at 50 deliveries a day.
Every response carries next_cursor. Pass it back as cursor until it comes back null.
Live, the same object goes out on the socket as a subscribe frame, with the keys renamed once in transit: provider_ids becomes providers and tag_args becomes tags. Changing a chip re-subscribes on the existing connection instead of reconnecting, which is why the green Live badge doesn't flicker when you edit a view.
Which leaves one seam I'd rather you hear from me than discover at 07:00. The socket takes symbols as an array; REST takes symbol as a scalar. So a five-ticker view streams as one subscription and backfills as five paginated requests, and if you're reconciling a gap after a disconnect, that asymmetry is where your loop gets written twice. Until the list endpoint accepts a comma-separated symbol, the cheaper reconciliation is usually to drop the ticker parameter entirely, fetch the tag filter alone, and match symbols from each item's symbols[] array in your own code.