BlueDot Dengue Surveillance
Brazil, Colombia, Singapore, Vietnam, India, Malaysia
as of 2026-08-18 the date the archive was last pulled
Country
Timeframe

Cross-country overview

Surging first, then Rising, then Not rising, each by the maximum end's percent descending. No comparison is a labelled group beneath the order and never last within it.
Surging Rising Not rising No comparison
Ordered by threshold state on reported cases, maximum end.

Figures may carry anomalies from uneven reporting. Figures account for downward data corrections.

Find a place

A readout carries no cluster count, no direction, no threshold state and no weighted figure. Those describe a whole level, and a level's movement is not one city's.
Suggestions begin at 3 characters and show at most 8, ordered by how well the name matches and then by reported cases over 5 years. Where more match, the list says how many.
Search covers 1,145 places across six countries, 1,132 of them offered as suggestions. These are the places this archive has named, which is not every place that exists: the API publishes case rows and not a place directory.

Figures may carry anomalies from uneven reporting. Figures account for downward data corrections.

Sources and coverage

Where each feed reaches, per country and level, read from the same tables the figures above are read from.
Country Country level, from State level, from City level, from Indicator-based Newsfeed clusters Population denominator
Brazil 2014-01-04 2020-01-01 2020-01-01 2013-12-29T00:00:00 to 2026-08-01T00:00:00, 1838 rows 2024-10-21, 22 of 60 months over 5 years 213421037, Instituto Brasileiro de Geografia e Estatística (IBGE), 2025, official annual estimate
Colombia 2014-01-04 2019-12-31 2019-12-31 2013-12-29T00:00:00 to 2026-07-31T00:00:00, 1840 rows 2024-10-25, 22 of 60 months over 5 years 53474637, Departamento Administrativo Nacional de Estadística (DANE), 2026, official projection from the 2018 census
Singapore 2020-01-13 Not reported below country level Not reported below country level 2012-01-01T00:00:00 to 2026-08-08T00:00:00, 989 rows 2024-10-30, 22 of 60 months over 5 years 6111175, Department of Statistics Singapore (SingStat), 2025, official annual estimate
Vietnam 2020-03-26 2020-05-05 Not shown, under review 2019-01-13T00:00:00 to 2026-04-18T00:00:00, 227 rows 2024-10-21, 22 of 60 months over 5 years 101112656, General Statistics Office of Viet Nam (now the National Statistics Office), 2024-04-01, sample survey result, not a full enumeration: roughly 20 percent of enumeration areas nationwide (39,340 areas), designed to be representative for population size at district level
India 2020-05-01 2020-05-01 2020-05-01 2024-01-01T00:00:00 to 2026-06-30T00:00:00, 30 rows 2024-10-21, 22 of 60 months over 5 years 1423435000, National Commission on Population, Ministry of Health and Family Welfare, 2026 (as on 1 March), official projection from the 2011 census, published 2020
Malaysia 2020-02-13 2020-05-31 2020-07-27 2018-12-30T00:00:00 to 2026-06-30T00:00:00, 266 rows 2024-10-24, 22 of 60 months over 5 years 34389300, Department of Statistics Malaysia (DOSM), 2026-01-01, official annual estimate
Population figures are each country's own statistical body's estimates and projections. They are not enumerated counts, they are not ours, and a figure computed over them is never an incidence rate.

Definitions

The plain-language rule for each transformation, written before it was built and read straight out of the specification rather than restated here.
T1. Timeframe aggregation.

For one country, one geographic level and one timeframe, the sum of every EBS report whose `reportedDate` falls inside the window. Computed separately for the minimum and the maximum of all four measure pairs: reported cases, confirmed cases, suspected cases and deaths.

Windows count back from the anchor, which is the date the archive was last pulled. 7 days and 30 days are the anchor and the 6 or 29 days before it, inclusive. YTD is 1 January of the anchor's year through the anchor. 1, 3 and 5 years are 365, 1,095 and 1,825 days back from the anchor.

Exactly one geographic level is summed per view and levels are never summed together. Each sub-national view carries the cases reported without attribution as a line of its own, on the maximum end. The national minimum is raised to the greatest level-wise minimum wherever that exceeds the country rows' own. Both ends carry through, so a figure renders as a range when they differ and as a single number when they agree.

All six timeframe controls stay selectable for all six countries. Where a window holds nothing, the slot carries the applicable absence and never a zero.

The properties of the underlying reports that make these rules necessary are in [`reference/METHODOLOGY.md`](reference/METHODOLOGY.md).

T8. Cluster counts.

For one country, one geographic level and one timeframe, the number of distinct newsfeed clusters whose `clusterWindow` falls inside the window, and the number of articles those clusters contain. A cluster is a group of related news articles about dengue in one country, carrying a single date. The feed produces roughly one cluster per country per day, which is an observed property of the archive and not part of this definition: four dates in the archive carry two clusters for one country.

Windows and the anchor are T1's, unchanged. Clusters are counted once each, by cluster identifier, because the monthly pull overlaps at month boundaries and repeats 293 clusters in the archive as it stands.

A cluster names places beneath the country as well as the country itself, and it commonly names several. So the three levels overlap rather than partition: a cluster naming three states is counted in each of them, and the state figures can exceed the country figure. They are read as clusters mentioning a place, never as a division of the country's clusters, and they are never summed. Each sub-national view carries the clusters that named the country and no place at that level as a line of its own, phrased *"Country-wide, no state named"* and, on a city view, *"Country-wide, no city named"*.

Cluster counts carry no minimum and maximum. They are a count of records rather than two readings of an overlap, so T1's minimum floor and its unattributed residual have no analogue here and are not computed.

The cluster archive begins 2024-10-21 for Brazil, India and Vietnam, 2024-10-24 for Malaysia, 2024-10-25 for Colombia and 2024-10-30 for Singapore, so the 3 year and 5 year windows are covered in part and never in full, for every country. A partly covered window renders its figure with the coverage line beneath it naming the floor and the covered fraction. Where a window holds no clusters, or a country names no places at a level anywhere in the archive, the slot carries the applicable absence and never a zero. **T2. Trend indicator.** For one country, one geographic level, one timeframe and one measure, the direction and size of the change from a baseline period to the selected window, computed on the same figures T1 renders rather than on the raw sums beneath them. The baseline is the equal-length period immediately before the window, except year to date, which compares against 1 January to the anchor's date one year earlier. A window and its baseline never overlap.

Both ends carry through, as they do in T1, and the minimum end is the floored minimum. Where the two ends agree the slot states one direction and its percent change. Where they disagree the slot says the direction depends on the overlap assumption and gives both, which in the archive as it stands is Vietnam's year-to-date reported cases and nothing else. Where the baseline is zero and the window is not, the direction renders without a percent, since no percent exists.

The indicator draws only when the baseline period is fully covered by the archive. It never degrades on partial coverage, unlike a figure, because a direction computed against a short baseline is not a less precise direction but a wrong one, biased upward by the months the baseline is missing. An undrawn arrow carries the applicable absence from `007`, phrased against the baseline rather than the window: *"No comparison, not collected before `<date>`"*.

Where either period sums to nothing or below because a correction cancelled it, the slot carries the cancelled-by-a-correction absence rather than a direction.

On the cluster panel the same computation runs on cluster counts, which have no minimum and maximum, and it is worded as news coverage rather than as cases. It draws on 7 days, 30 days and year to date, and on 1, 3 and 5 years it draws for no country, ever.

T4. Threshold flags and the ordering they drive.

For one country, one geographic level, one timeframe and one measure, a named state describing where that slot's trend sits against a fixed threshold, and the order the six countries are listed in when that state is what the reader is scanning for.

Four states, in order. **Surging**, the trend is up by more than 50 percent. **Rising**, the trend is up but by 50 percent or less. **Not rising**, the trend is flat or down. **No comparison**, T2 drew no arrow for the slot, so no state can be assigned.

The threshold is 50 percent on every timeframe, not on year to date alone. The committed example is year to date above 50 percent, and the same constant is applied to the other five windows rather than a different number being invented per window. A consequence worth stating: 50 percent over seven days is a far smaller movement in cases than 50 percent over a year, so the state is read within a timeframe and never across two.

States are computed from T2's own output and never recomputed from the figures beneath it, so a flag cannot contradict the arrow printed beside it. Where the baseline is zero and the window is not, T2 gives a direction without a percent. That is up without a magnitude, so it is **Rising** and not Surging: no percent exists to clear a threshold with.

Both ends carry through, as everywhere. **The state fires on the maximum end**, so Surging means the threshold is crossed on the fuller reading of the overlap. Where the minimum end does not also cross, the slot says the state depends on the overlap assumption and names what the minimum end gives instead. Where the two ends disagree about direction, which T2 records as `disagree`, the state is that disagreement and not one of the four.

The six countries are ordered by their state at country level, for the timeframe and measure being read: Surging, then Rising, then Not rising, each ordered by the maximum end's percent descending, ties broken by country name. **No comparison countries are a labelled group beneath the ordered list, never last within it**, because they are not last for being low, they are absent for having no baseline. Inside that group the order is the window's own maximum figure descending, which they do have, and the group states that this is the key it uses.

Ordering below country level, and the selection of which states and cities are shown at all, are T6's and are not decided here.

On the news panel the same four states run on cluster counts, which carry no minimum and maximum, and are worded as news coverage rather than as cases. They exist on 7 days, 30 days and year to date, and on 1, 3 and 5 years every country is No comparison, per T2.

T3. Population-weighted activity.

For one country, one timeframe and one measure, that country's T1 figure divided by that country's population and expressed per 100,000 people. Computed on the same figures T1 renders rather than on the raw sums beneath them, so the minimum end is the floored minimum, and both ends carry through as they do everywhere.

**It is not an incidence rate and the interface says so where the figure appears**, not only in the glossary. A case count is a count of reports rather than of people, the denominator is an estimate rather than an enumeration, and the two are not measured over the same span. The figure is indicative of relative activity between countries in one timeframe and is nothing more formal than that.

The denominator is the national figure published by that country's own statistical body, and every weighted figure is labelled with the body and the vintage behind it: IBGE 2025 for Brazil, DANE 2026 for Colombia, the National Commission on Population's 1 March 2026 projection for India, DOSM as at 1 January 2026 for Malaysia, SingStat 2025 for Singapore, and the General Statistics Office's 1 April 2024 intercensal survey for Vietnam. Vietnam's is a sample survey rather than a full enumeration and is the oldest vintage in the set, and both facts travel with it. India's is a projection from the 2011 census. These are the bodies' own estimates and projections, not ours and not enumerated counts.

**One vintage per country, applied to every window.** A 5 year window spans several vintages and this divides five years of reports by one year's population. The alternative, a denominator contemporaneous with each part of the window, is more correct and needs a historical series from six statistical bodies that we do not hold, which is post-MVP work rather than a formatting choice. So the same constant denominator is used on all six windows, on the same reasoning that put one threshold constant on all six in T4, and the cost is stated rather than hidden: **the weighted figure is read within a timeframe and never across two**, and it is **never annualized**. A 5 year figure is cases per 100,000 across those five years, not per year.

Both ends are weighted, and the slot renders a range exactly when T1's own two ends disagree, which is the same rule the count beside it follows. Where the two ends differ but round to the same displayed figure, the slot shows the single figure, because the count sitting beside it already carries the range.

The weighted figure sits beside the count and never replaces it.

The count is the primary figure. A transformation never replaces its input, and a rate promoted to headline is read as a rate no matter what the label says.

Figures are stored at full precision and displayed to two decimals below 10, one decimal from 10 to under 100, and as an integer at 100 and above, since neither the numerator nor the denominator supports more. A nonzero figure that would round to 0.00 renders as *"<0.01"* rather than as a zero, because a real case displayed as none is the one rounding error this application cannot make.

Where T1 carries an absence the weighted slot carries the same absence, since a figure that does not exist cannot be weighted. **The weighted figure is country level only in this release.** All six countries have a national denominator, so no country-level slot is empty for want of one. Below country level the slot stays in place and carries an absence rather than disappearing: not in this release for Brazil, Colombia, India and Malaysia, Withheld for Vietnam, and for Singapore the absence T1 already gives it, since Singapore has no sub-national rows in any feed.

T6. Burden ranking and place selection.

For one country, one geographic level and one timeframe, the individual places at that level ordered by their own reported cases on the maximum end, and the five highest shown.

This is the first transformation that works on a single place. T1, T8, T2, T4 and T3 all describe a whole level: a state row is every state's reports added together, and the places behind it survive only as a count. T6 introduces the per-place layer, and nothing else is built on it. The cluster count, the trend arrow, the threshold state and the weighted figure stay level-wide, so a ranked place carries its own case figures and no direction and no state.

The ranking key is fixed.

Reported cases on the maximum end, whatever measure the reader is scanning. The maximum end is the fuller reading of the overlap, which is the end T4 fires its states on. Reported is the only measure populated at place level in every country, and a ranking that followed the measure control would empty itself on suspected cases, which are 19 rows in the whole archive. The five shown places then carry all four measures, both ends, with T1's rules and T1's absences unchanged, so the reader sees more than the key they were selected on.

The selection follows the timeframe control

and changes as the reader changes it. The places shown are the highest-burden places for the window on screen, which is what makes the figures beside them and their membership agree. Brazil has 6 states and 9 cities reporting cases in the 7 days to the anchor, and 27 states and 416 cities across 5 years, so this is a different list at each control rather than one list re-measured.

**States and cities are ranked separately and never mixed.** A state's figure contains its cities', so one list holding both would rank a whole against its own parts and put the same cases on the ladder twice. Exactly one level is read at a time here as everywhere else.

**Five per level, per country, and the remainder is stated** in the ranking line beneath each list. The number is chosen rather than derived: a share-of-total cut would swing between 1 and 20 places across these windows and would need a second explanation of what the share is a share of, given that a country's cases are not all attributed to a place. Where fewer than five places have a figure, all of them show and the line says so.

**Ordering is deterministic.** Reported maximum descending, ties broken by the place's own minimum descending, then by place name. Two builds of one archive produce one list, and a tie straddling the fifth position is cut by that key rather than by whatever order the rows arrived in. Nothing reshuffles on a refresh that brought no new reports.

**A place's minimum is its own rows' summed minimum, and the minimum floor rule does not run here.** Flooring a state by the cities inside it needs to know which cities those are, and the feed carries no parent field. The parent is recoverable only by parsing the display name, which joins for all 426 Brazilian cities and fails for 75 of Colombia's 111, so the rule would run in one country and not in its neighbour. A place-level minimum is therefore lower than a floored one would be, and it is stated rather than silently corrected. Reasoning in [`reference/METHODOLOGY.md`](reference/METHODOLOGY.md) section 12.

**A place below the cut is not an absence.** Its figures exist and are complete, and no slot on the page is empty on its behalf, so none of the eight absence kinds applies and no ninth is added. The ranking line accounts for it. Where a country has no places to rank at all the panel carries the absence T1 already gives that level: not reported at this resolution for Singapore at both levels, Withheld for Vietnam's cities, and nothing reported for a level whose window is simply empty, which in the current archive is Malaysia's cities over 7 days.

This runs for all six countries.

MVP-SCOPE item 8 asks about Brazil because Brazil is the example in front of the client, and a panel that existed on one country's view and not on the other five would read as a defect rather than as a decision.

T7. Place search index.

Every place the archive has ever named, in one index a reader can type into, with the individual place's own figures behind each entry. It answers MVP-SCOPE item 4, which commits that finding a city is easy.

The index is every place the archive mentions, and nothing more.

The BlueDot API publishes case rows and not a place directory, so there is no list of cities to look up. A place is in the index because at least one dengue report named it. As pulled, that is 1,145 places: the six countries, 134 states and provinces, and 1,005 cities. The split was first written here as 108 and 1,031, which is the same total with Colombia's 26 departments counted as cities, and was corrected on 2026-08-21 against the built index. No check owned it: check 17 rebuilds the index from the raw archive and compares all 1,145 entries, and the total was right. A place that exists in the world and has never been named in a dengue report in this archive cannot be found, and the interface says so rather than returning an empty box.

The index does not change with the timeframe control.

This is the one rule T7 does not inherit from T6. T6 selects the places it shows per window so that its membership and its figures agree, which is right for a ranked list and wrong for a search box: only 36 of the 1,126 searchable places carry a figure in the 7 days to the anchor, against 1,097 across 5 years, so an index following the control would make the ordinary act of typing a real city's name return nothing. Every place stays findable at every control, and where the selected window holds nothing for a place its readout carries the applicable absence and never a zero.

**A match returns that place's own figures.** All four measures, both ends, under T1's rules and carrying T1's absences, for the timeframe on screen. This is T6's per-place layer generalised from the five it shows to all of them.

**A place readout carries no cluster count, no trend arrow, no threshold state and no weighted figure.** None of those is computed for a single place. They describe a whole geographic level, and a level's movement is not the movement of one city inside it. The readout adds one statement of its own instead: where the place sits in its level's ranking for the window on screen, phrased *"ranked 47 of 416 cities reporting in this window"*, so a place below T6's cut returns something a reader can size rather than a bare number.

**Matching runs against the whole name the feed wrote, including the parents inside it.** Cities render as `Formosa, Goias, Brazil` and states as `Goias, Brazil`, and all 1,145 names are unique, so the name is the key. `Recife, Pernambuco` is a working query without a hierarchy existing behind it. The parent is matched and displayed and is never treated as a linkage: the application does not assert that the `Tolima` inside a city's name is the state row called `Departamento de Tolima`, which is the claim T6 declined to invent and which search does not need.

**What the reader types is folded before it is compared**: lowercased, accents dropped, punctuation treated as a space, runs of whitespace collapsed, and the same rule applied to the stored name so there is one rule and not two. The feed already stripped its own accents, so this exists to make `São Paulo` match `Sao Paulo` rather than the other way round. One further equivalence is stated by name: the feed writes Vietnamese `Đ` as `GJ`, so `gj` and `d` match each other and `Dak Lak` finds `GJak Lak Province`. That is a matching rule only. **Nothing displayed changes**, and the page always shows the name the API returned.

**Suggestions are ranked, and the ranking is stated.** Places whose own name starts with what was typed come first, then places whose own name contains it, then places matched only through a parent or country segment. Within each group, the place's reported cases on the maximum end across 5 years, highest first, then the place's name. The 5 year figure is used at every control so the suggestions do not reshuffle when the reader changes the timeframe.

**Suggestions begin at three characters and show eight, and where more matched the list says how many**: *"showing 8 of 132 matches, keep typing to narrow"*. Because every name carries its country, common openings match hundreds, and roughly three queries in ten match more than eight places. A list that quietly showed the first eight would be hiding a cut of ours, which is what the ranking line exists to prevent one panel above.

Two results are statements rather than places.

Vietnam's 13 city rows are Withheld, so they are not offered as suggestions, and a query matching one carries a line beneath the list saying they are excluded and under review and offering province level instead. A query matching nothing carries a line naming what the search covers. **Neither is an absence**, and no ninth kind is added: nothing is missing and no slot is empty. Like the ranking line, their subject is our own index rather than the archive, the feed or scope.

Reasoning in [`reference/METHODOLOGY.md`](reference/METHODOLOGY.md) section 13.

Sources. BlueDot event-based surveillance, indicator-based surveillance and newsfeed clusters. Population figures are named with body and vintage where they appear.
as of 2026-08-18.
Every window counts back from the date the archive was last pulled. Built 2026-08-26 14:50 UTC.