Filters
Twenty-plus exact filters, a radius or box on the map, event classes, numeric signal ranges, facet aggregation and pagination — combinable in one request, on every plan.
Publication
| Parameter | Matches | Example |
|---|---|---|
source | Publisher domain | bbc.co.uk |
source_type | Kind of publication | web |
publisher_country | Where the publisher is based | US |
language | Source language, ISO-639-3 | hin |
author | Byline | jane doe |
Place
| Parameter | Matches | Example |
|---|---|---|
country | Country the article is about (FIPS 10-4) | GM |
city | City mentioned | Berlin |
region | Administrative region mentioned | Bavaria |
location | Any place name mentioned | Rhine |
location_id | Exact gazetteer identifier | -1746443 |
near / radius_km | A city mentioned within that many km of a point | 51.9244,4.4777 / 50 |
bbox | A city mentioned inside a box: south, west, north, east | 47.3,5.9,55.1,15.0 |
Subject and entities
| Parameter | Matches | Example |
|---|---|---|
topic | Canonical topic identifier | EPU_POLICY |
organization | Organisation mentioned | european union |
person | Person mentioned | benjamin netanyahu |
name | Any proper name mentioned | Ministry of Finance |
entity_id | Every spelling grouped under a resolved id | organization:asml |
event_type | One of seventeen event classes | labor_strike |
Statements and figures
| Parameter | Matches | Example |
|---|---|---|
quote_verb | Verb introducing a quotation | warned |
amount_object | What a quantity refers to | barrels |
amount_min / amount_max | A numeric amount mentioned within the bounds | 1000 |
mentioned_date | A date the article refers to | 20260801 |
Time and grouping
| Parameter | Matches | Example |
|---|---|---|
from / to | Publication window, to the second | 20260729T140000 |
story_id | One clustered story | st_9f2… |
Signal ranges
Every article carries scored language signals across industry, business and risk groups. Filter on a range rather than an exact value, and stack constraints — all of them must match.
# energy coverage carrying financial-stress language
GET /search?signal=energy&signal_min=0.3
# stack several: supply disruption AND financial negativity
GET /search?signals=supply_disruption:0.2,financial_negative:0.15 Facets: ask what values exist
Rather than guessing which countries, topics or outlets are worth filtering on, ask. Any filterable field can be requested as a facet, returning values with counts across the whole result set.
GET /search?q=semiconductors&facets=country,organization,language&facet_limit=25 Responses include how much sits outside the returned buckets, so a truncated facet is visible rather than mistaken for the complete distribution. This is what the coverage figures on this site are generated from.
Pagination
Results carry offset, limit, has_more and a
total. The total is marked exact for keyword and metadata queries and
approximate for ranked semantic modes, so you know whether a count
can be relied on or only compared.
Combining them
Filters are conjunctive: every constraint must match. That makes narrow queries cheap to express and makes it easy to over-constrain into an empty result set, so build up rather than starting from everything at once.
# German-language coverage about France, energy sector,
# from publishers based in Germany, last 48 hours
GET /stories
?language=deu
&country=FR
&publisher_country=DE
&signal=energy&signal_min=0.25
&from=20260729T000000 Why an over-constrained query is the common failure
Because every constraint must match, adding one can only ever remove results, and the removal is silent — an empty response is indistinguishable from an absence of coverage. In practice teams write the query they want in one go, get nothing back, and conclude the index does not cover their subject. Build the query up instead: start with the subject and a time bound, check the count, then add one constraint at a time and watch what each costs. When the count collapses on a particular addition, that constraint is the one carrying an assumption that turned out to be wrong, and you will know which rather than guessing.
Facets are the other half of that discipline. Rather than guessing which countries, languages or organisations to constrain on, request them as facets first and read the distribution — it tells you what values actually exist in the result set and how much volume sits behind each, so the filter you add next is chosen from the data rather than from memory. The same response reports how much sits outside the returned buckets, which is what stops a truncated facet from being mistaken for the full picture.
Filters are exact matches on the values the index holds. organization=ASML
Holding NV matches nothing where asml matches thousands, and the
empty result looks like an absence of coverage. Ask for the spellings first:
resolve_entity over MCP, GET /entities over REST, returns the
values the index holds for a name, grouped, with a count per spelling and a confidence
for the match. Pass a returned value verbatim as the filter.
Entity ids
Each resolved group also carries an id, such as organization:asml. Pass it
as entity_id on search, stories, top headlines or aggregate and it matches
every spelling grouped under it within the window you asked for, so asml
and asml holding nv are one filter rather than two queries you merge by
hand. Up to five ids combine, and all of them must match. The response lists the
spellings each id used in resolved, and an id with no spelling in the
window comes back with an empty list rather than as an empty result you could mistake
for missing coverage.
# resolve once
GET /entities?q=ASML Holding NV&type=organization&from=2026-08-01
# then filter on the group, not one spelling
GET /stories?entity_id=organization:asml&from=2026-08-01
The grouping is deterministic and approximate: spellings are normalised and grouped
by rule, not matched against a company register. Resolving the id costs a credit or
two, counted in the response's credits_charged. The same ids are what
relationship queries start from.
Near a place, or inside a box
near takes a point as latitude,longitude, and
radius_km a distance from 0.1 to 2,000 km. bbox takes a box as
south,west,north,east instead. Either one matches articles that mention a
city inside the area, on search, stories and top headlines, and on the MCP tools
search_news, list_stories and top_headlines. The
index first narrows to the geohash cells covering the area, then checks every city an
article mentions exactly against the circle or the box, so a city that shares a cell
with the area but lies outside it does not match.
# a port strike reported within 50 km of Rotterdam
GET /search?q=port strike&near=51.9244,4.4777&radius_km=50&from=2026-09-01
Each hit lists the cities that matched in matched_locations, nearest first.
An excerpt of one hit, with invented values for illustration rather than a measurement
of coverage:
{
"results": [
{
"id": "20260909071500-4be2",
"title": "Dockworkers extend strike at container terminals",
"url": "https://news.example.com/logistics/dockworkers-strike",
"source": "news.example.com",
"published_at": "20260909071500",
"language": "eng",
"story_id": "20260908163000-91d4",
"matched_locations": [
{ "name": "Rotterdam", "country": "NL", "latitude": 51.9225, "longitude": 4.4792, "distance_km": 0.2 },
{ "name": "Vlaardingen", "country": "NL", "latitude": 51.9125, "longitude": 4.3417, "distance_km": 9.4 },
{ "name": "Dordrecht", "country": "NL", "latitude": 51.8133, "longitude": 4.6901, "distance_km": 19.1 }
]
}
],
"credits_charged": 2,
"partial": false,
"coverage": "complete"
}
Matches inside a box carry no distance_km, since there is no point to
measure from. Only cities count. A region's or a country's coordinates mark its centre
rather than where anything happened, so a radius around a capital does not stand in for
the country: filter countries with country. A box whose west edge is greater
than its east crosses the antimeridian, so a box around Fiji is one box rather than two.
Circles, boxes and every field they return:
the geographic search reference.
Event classes
event_type filters on one of seventeen classes of event. The same list is a
facet, so facets=event_type shows which classes a result set holds before
you pick one, and every article carries its classes as event_types:
bankruptcy, earnings_report, ipo, cyber_attack, outage, industrial_accident, sanctions, trade_dispute, boycott, nationalization, privatization, money_laundering, executive_change, factory_closure, supply_shortage, labor_strike, antitrust_action
# strikes reported in France since the start of the month
GET /stories?event_type=labor_strike&country=FR&from=2026-09-01
Classes are derived from the topics an article's source supplies, under version
events-v1. Today one source supplies topics, so an article that
arrived through another carries no class until Unzoi computes its own
(how it works). Each class names the topics it
needs, and each of those is checked against that source's published vocabulary.
Where one theme is ambiguous, a second is required: an appointment counts as an
executive_change only together with a chief-executive theme.
That makes classes precise rather than complete. An article can describe a bankruptcy
without carrying the bankruptcy class, so when a missed event matters, run a
query in words beside the filter rather than relying on the class alone.
Acquisitions, funding rounds, layoffs, lawsuits, contract awards, product launches and factory openings are not classes. No theme marks them reliably, so they wait for extraction from article bodies. Until then, semantic search is the way to find them, as the deal and layoff recipes show. Every class and parameter: the filter reference.
Numeric amounts
amount_min and amount_max match articles that mention a
numeric amount within the bounds. One amount must satisfy every bound:
amount_min=1000&amount_max=5000 needs a single figure between the two,
not one figure above 1,000 and another below 5,000. With amount_object,
that same amount must also describe the object.
# a thousand workers or more, in September's reporting
GET /search?amount_object=workers&amount_min=1000&from=2026-09-01 How complete these filters are today, and what they cost
Places, event classes and amounts are derived from records every article in the index
already holds, so nothing is ingested again. Each part of the index gains them when it
is next rebuilt. Until a part has been rebuilt, a query using these filters answers from
the parts that have, with partial: true and
coverage: field_unavailable. The results that come back are correct, there
may be more in the part that could not answer, and retrying before its rebuild changes
nothing. Every result says when it is incomplete, so read those two fields before
treating a short or empty answer as an absence.
A geographic or amount filter reads candidates in batches and checks each one. Each
batch costs a credit, counted in the response's credits_charged, and
when there are more candidates than a call reads, the total is estimated and marked
approximate.
Reference
Every parameter, what it accepts and how it behaves against the index:
the filter reference. Circles,
boxes and matched_locations:
the geographic search reference. What a
name resolves to before you filter on it:
the /entities reference.