conflict-and-environment.md (40963B)
1 --- 2 title: "Conflict & Environmental Monitoring" 3 description: "Event datasets, munitions identification, fire and deforestation feeds, and the tools built for documenting harm." 4 category: osint 5 subcategory: "Thematic" 6 tags: [osint, conflict, environment, monitoring, satellite] 7 tools: [acled, gdelt, firms, sentinel-hub, global-forest-watch, osmp, liveuamap] 8 difficulty: intermediate 9 updated: 2026-10-03 10 references: 11 - name: "Bellingcat's Online Investigation Toolkit" 12 url: "https://bellingcat.gitbook.io/toolkit" 13 author: "Bellingcat" 14 license: none 15 relation: derived 16 note: "Tool catalogue: names, descriptions, cost flags and links for this area." 17 - name: "OSINT Newsletter Tools Library" 18 url: "https://tools.osintnewsletter.com" 19 author: "The OSINT Newsletter" 20 license: none 21 relation: derived 22 note: "Second tool catalogue, cross-checked against the above." 23 --- 24 25 ## What this covers 26 27 Two thematic areas with mature, purpose-built open datasets: armed conflict and environmental 28 change. Both are unusual in OSINT for having authoritative structured data behind them rather than 29 just tools, which moves the skill from finding sources to reading methodologies and querying them 30 properly. 31 32 ## Method 33 34 1. **Start from a bounded question with a place and a date range.** These are datasets, not search 35 engines — a query without a bounding box and a window either returns nothing useful or exceeds 36 your quota. 37 2. **Read the codebook before the data.** What ACLED counts as an event, how FIRMS defines a 38 thermal anomaly, what a Global Forest Watch alert confidence class means: each materially 39 changes what a number means, and each is documented. 40 3. **Query the dataset rather than the dashboard.** Dashboards aggregate in ways you cannot audit 41 and cannot cite. Pull the records, store them with the retrieval date, and do your own 42 aggregation. 43 4. **Establish the baseline before the anomaly.** One fire detection at an industrial site means 44 nothing until you know how often that pixel normally burns. Pull the same query for the previous 45 year and the previous month. 46 5. **Corroborate across two independent feeds.** Independence means different sensors or different 47 collection methods — a VIIRS hotspot and a Sentinel-2 burn scar, an ACLED event and a dated 48 photograph, a GDELT article cluster and a satellite image. Two aggregators that both scrape the 49 same wire service are one source. 50 6. **For munitions, work from markings outward**, and write "consistent with" unless you have 51 stencilling, lot numbers or a fuze you can read. This is the single most common place 52 open-source conflict reporting goes wrong. 53 7. **Record the evidentiary chain for each claim**: the dataset and its version, the exact query, 54 the retrieval timestamp, the hash of any imagery you downloaded, and the second source. A 55 finding that cannot be re-run is not a finding. 56 57 ## Conflict 58 59 | Source | What it gives you | 60 | --- | --- | 61 | [ACLED](https://acleddata.com/) | Coded conflict events — date, location, actors, event type, fatalities — globally, with a published methodology and an API. The reference dataset. | 62 | [GDELT](https://www.gdeltproject.org/) | Global news coverage indexed by location, theme, tone and actor, updated every 15 minutes. Not events; coverage of events. | 63 | [Liveuamap](https://liveuamap.com/) | Near-real-time mapped events aggregated from social media. Fast but unverified; a lead generator only. | 64 | [Geoconfirmed](https://geoconfirmed.org/) | Volunteer-geolocated conflict media, each entry with the coordinates and the reasoning. Verified work you can audit. | 65 | [Open Source Munitions Portal](https://osmp.ngo/) | Identification reference for munitions and their remnants, which is how you turn a photo of debris into a weapon type. | 66 | [Bellingcat's Civilian Harm datasets](https://ukraine.bellingcat.com/) | Verified incident databases with the sourcing attached to each entry. | 67 68 ### ACLED API 69 70 Coded event data with a documented methodology, which is what separates it from every aggregator in 71 the table above. Each record carries a date, coordinates with a stated precision level, named 72 actors, an event type and sub-event type, a fatality estimate, and the sources it was coded from. 73 74 **The API moved and the auth model changed.** The old `api.acleddata.com` host with 75 `key=`/`email=` query parameters is gone — the hostname no longer resolves, and the key system was 76 retired in September 2025. Current access is a bearer token from an OAuth password grant against 77 your myACLED account, used against `acleddata.com/api/`. 78 79 ```bash 80 # token: valid 24 hours, with a refresh token valid 14 days 81 TOKEN=$(curl -s -X POST 'https://acleddata.com/oauth/token' \ 82 -d 'username=you@example.com' --data-urlencode 'password=YOUR_PASSWORD' \ 83 -d 'grant_type=password' -d 'client_id=acled' -d 'scope=authenticated' \ 84 | jq -r .access_token) 85 ``` 86 87 ```bash 88 # events in one country over a window, as JSON 89 curl -s -H "Authorization: Bearer $TOKEN" -G 'https://acleddata.com/api/acled/read' \ 90 --data-urlencode 'country=Ukraine' \ 91 --data-urlencode 'event_date=2026-01-01|2026-03-31' \ 92 --data-urlencode 'event_date_where=BETWEEN' --data-urlencode 'limit=0' \ 93 | jq '.count' 94 95 # CSV straight to disk, which is what you archive 96 curl -s -H "Authorization: Bearer $TOKEN" -G 'https://acleddata.com/api/acled/read' \ 97 --data-urlencode '_format=csv' --data-urlencode 'country=Sudan' \ 98 --data-urlencode 'event_date=2026-01-01|2026-03-31' \ 99 --data-urlencode 'event_date_where=BETWEEN' --data-urlencode 'limit=0' \ 100 -o "acled-sudan-$(date -u +%F).csv" 101 102 # one event type only — the sub-event taxonomy is where the precision is 103 curl -s -H "Authorization: Bearer $TOKEN" -G 'https://acleddata.com/api/acled/read' \ 104 --data-urlencode 'country=Nigeria' \ 105 --data-urlencode 'event_type=Explosions/Remote violence' \ 106 --data-urlencode 'event_date=2026-01-01|2026-03-31' \ 107 --data-urlencode 'event_date_where=BETWEEN' | jq '.data | length' 108 109 # a named actor's events, for an order-of-battle or attribution question 110 curl -s -H "Authorization: Bearer $TOKEN" -G 'https://acleddata.com/api/acled/read' \ 111 --data-urlencode 'actor1=Military Forces of Sudan (2019-)' \ 112 --data-urlencode 'actor1_where=%3D' --data-urlencode 'limit=200' | jq '.count' 113 114 # narrow to an admin region, which is how you bound to a locality 115 curl -s -H "Authorization: Bearer $TOKEN" -G 'https://acleddata.com/api/acled/read' \ 116 --data-urlencode 'country=Mali' --data-urlencode 'admin1=Mopti' \ 117 --data-urlencode 'event_date=2026-01-01|2026-03-31' \ 118 --data-urlencode 'event_date_where=BETWEEN' | jq '.data | length' 119 120 # only the fields you need, which keeps responses small 121 curl -s -H "Authorization: Bearer $TOKEN" -G 'https://acleddata.com/api/acled/read' \ 122 --data-urlencode 'country=Ukraine' --data-urlencode 'limit=50' \ 123 --data-urlencode 'fields=event_date|event_type|sub_event_type|location|latitude|longitude|fatalities|source' \ 124 | jq '.data[0]' 125 126 # the deleted-records endpoint, so a stored copy can be reconciled with upstream revisions 127 curl -s -H "Authorization: Bearer $TOKEN" -G 'https://acleddata.com/api/deleted/read' \ 128 --data-urlencode 'deleted_timestamp=1735689600' | jq '.count' 129 ``` 130 131 The `R` package `acledR` and the `acled` Python package both wrap this and handle the token 132 refresh; either is less work than scripting the grant yourself if you query regularly. Access is 133 free for non-commercial use with registration, and the terms restrict redistribution of the raw 134 data — cite and link rather than re-publishing the CSV. 135 136 Read the codebook before drawing any conclusion. Three things in particular: coordinate precision 137 is a graded field, and a precision-3 event is located to an admin region rather than a point; 138 fatality figures are the most conservative reported estimate, not a count; and event density tracks 139 reporting density, so comparing two regions compares their journalism as much as their violence. 140 The `deleted` endpoint exists because ACLED revises history as better sourcing arrives, which means 141 a stored extract drifts from upstream. 142 143 ### GDELT DOC 2.0 API 144 145 Global news coverage, indexed every 15 minutes across languages, queryable by phrase, location, 146 theme, tone and source country. It tells you what was *reported*, where and in what volume, which 147 is a different and complementary question to what happened. No key, no registration. 148 149 ```bash 150 # articles matching a phrase, geographically filtered, as JSON 151 curl -s -G 'https://api.gdeltproject.org/api/v2/doc/doc' \ 152 --data-urlencode 'query="artillery" sourcecountry:UP' \ 153 --data-urlencode 'mode=artlist' --data-urlencode 'format=json' \ 154 --data-urlencode 'maxrecords=100' --data-urlencode 'timespan=7d' \ 155 | jq '.articles[] | {seendate, domain, title, language}' 156 157 # coverage volume over time — the shape of the curve is the finding 158 curl -s -G 'https://api.gdeltproject.org/api/v2/doc/doc' \ 159 --data-urlencode 'query="port of berbera"' \ 160 --data-urlencode 'mode=timelinevol' --data-urlencode 'format=json' \ 161 --data-urlencode 'timespan=3m' | jq '.timeline[0].data[-10:]' 162 163 # an exact window rather than a rolling span 164 curl -s -G 'https://api.gdeltproject.org/api/v2/doc/doc' \ 165 --data-urlencode 'query="chemical plant" sourcelang:eng' \ 166 --data-urlencode 'mode=artlist' --data-urlencode 'format=json' \ 167 --data-urlencode 'startdatetime=20260301000000' \ 168 --data-urlencode 'enddatetime=20260315000000' | jq '.articles | length' 169 170 # which countries are covering a story, and which are not 171 curl -s -G 'https://api.gdeltproject.org/api/v2/doc/doc' \ 172 --data-urlencode 'query="grain corridor"' \ 173 --data-urlencode 'mode=timelinesourcecountry' --data-urlencode 'format=json' \ 174 --data-urlencode 'timespan=1m' | jq '[.timeline[].series] | .[0:8]' 175 176 # tone distribution, for spotting a coordinated framing shift 177 curl -s -G 'https://api.gdeltproject.org/api/v2/doc/doc' \ 178 --data-urlencode 'query="sanctions relief"' \ 179 --data-urlencode 'mode=tonechart' --data-urlencode 'format=json' \ 180 --data-urlencode 'timespan=2w' | jq '.tonechart' 181 182 # images published alongside the coverage, which sometimes surface the source photo 183 curl -s -G 'https://api.gdeltproject.org/api/v2/doc/doc' \ 184 --data-urlencode 'query="refinery fire"' \ 185 --data-urlencode 'mode=imagecollageinfo' --data-urlencode 'format=json' \ 186 --data-urlencode 'timespan=3d' | jq '.images[0:5]' 187 ``` 188 189 The DOC API serves a rolling window — roughly the last three months by default, reaching back to 190 1 January 2017 with explicit datetimes — and caps at 250 records per request for article and image 191 modes. For anything longer or larger, the GDELT Event and GKG tables are published on BigQuery and 192 as raw CSV, which is the right route for a multi-year series. 193 194 What it does not tell you: GDELT indexes articles, not events, so one incident covered by four 195 hundred outlets is four hundred records. A volume spike measures attention. Syndication means the 196 same wire copy appears under many domains, which is why a GDELT cluster and an aggregator that 197 scrapes the same wires are not two independent sources. 198 199 ### NASA FIRMS 200 201 Active fire and thermal anomaly detections from MODIS and VIIRS, available within three hours of 202 satellite overpass. Thermal detection is a far broader OSINT primitive than wildfire monitoring: 203 gas flaring, industrial process heat, shelling, burning buildings and crop residue all register. 204 205 Get a free `MAP_KEY` from 206 [firms.modaps.eosdis.nasa.gov/api/map_key](https://firms.modaps.eosdis.nasa.gov/api/map_key/). 207 208 ```bash 209 export MAP_KEY='your-map-key' 210 ``` 211 212 ```bash 213 # check the key works and how much of your quota is left 214 curl -s "https://firms.modaps.eosdis.nasa.gov/mapserver/mapkey_status/?MAP_KEY=$MAP_KEY" 215 216 # detections in a bounding box over the last day — order is west,south,east,north 217 curl -s "https://firms.modaps.eosdis.nasa.gov/api/area/csv/$MAP_KEY/VIIRS_SNPP_NRT/30.0,46.0,36.0,50.0/1" 218 219 # a specific past date, with the day range starting from it (1-10 days) 220 curl -s "https://firms.modaps.eosdis.nasa.gov/api/area/csv/$MAP_KEY/VIIRS_NOAA20_NRT/30.0,46.0,36.0,50.0/2/2026-03-11" \ 221 -o firms-2026-03-11.csv 222 223 # the archival standard-quality product, for anything older than the NRT window 224 curl -s "https://firms.modaps.eosdis.nasa.gov/api/area/csv/$MAP_KEY/VIIRS_SNPP_SP/30.0,46.0,36.0,50.0/5/2025-07-01" \ 225 -o firms-archive.csv 226 227 # which products cover which dates, so you query one that exists 228 curl -s "https://firms.modaps.eosdis.nasa.gov/api/data_availability/csv/$MAP_KEY/all" 229 230 # filter to high-confidence daytime detections at a site 231 curl -s "https://firms.modaps.eosdis.nasa.gov/api/area/csv/$MAP_KEY/VIIRS_SNPP_NRT/30.0,46.0,36.0,50.0/1" \ 232 | awk -F, 'NR==1 || ($10=="h" || $10>=80)' 233 234 # a year of the same small box, to build the baseline before you call an anomaly 235 for d in 2025-0{1..9}-01 2025-1{0..2}-01; do 236 curl -s "https://firms.modaps.eosdis.nasa.gov/api/area/csv/$MAP_KEY/VIIRS_SNPP_SP/30.0,46.0,36.0,50.0/10/$d" 237 sleep 2 238 done > baseline.csv 239 ``` 240 241 The quota is 5,000 transactions per 10-minute window per key, and a large bounding box counts as 242 several, so loop with a sleep. Day range per request is 1–10. VIIRS at 375 m resolves far more than 243 MODIS at 1 km and is what you want for anything at a single site; the `_NRT` products are the fast 244 ones, `_SP` the reprocessed archive. 245 246 Reading a detection: `confidence` is `l`/`n`/`h` for VIIRS and a 0–100 percentage for MODIS, `frp` 247 is fire radiative power in megawatts, `daynight` matters because daytime detections suffer more 248 false positives from reflective surfaces, and `scan`/`track` give the actual pixel footprint — the 249 detection is a pixel, not a point, so the coordinates are the pixel centre and the real event is 250 somewhere within a few hundred metres of it. 251 252 A hotspot is a thermal anomaly and nothing more. Gas flares burn continuously and appear every 253 single night, which is why the baseline query above matters: the finding is not "a hotspot at this 254 site", it is "a hotspot at a site that produced none in the preceding twelve months". 255 256 ### Sentinel Hub on the Copernicus Data Space Ecosystem 257 258 Free Sentinel-1, Sentinel-2 and Sentinel-3 imagery, both as an interactive browser and as an API. 259 Use the browser to find the scene and settle the band combination; use the API to pull the exact 260 same thing reproducibly, with a date and a bounding box you can cite. 261 262 **Access moved to the Copernicus Data Space Ecosystem**, which replaced both the old Copernicus 263 Open Access Hub and the commercial Sentinel Hub free tier for Sentinel data. Register at 264 [dataspace.copernicus.eu](https://dataspace.copernicus.eu/), create an OAuth client in your account 265 settings, and you get a monthly processing-unit quota at no cost. 266 267 Start in [EO Browser](https://browser.dataspace.copernicus.eu/): set the area, set the date range, 268 pick Sentinel-2 L2A, and step through the available passes. The band combinations that make change 269 visible where true colour hides it: 270 271 ```text 272 Burn scars (Sentinel-2): B12, B8A, B4 — recent burns go red-brown, vegetation green 273 Vegetation health (NDVI): (B8 - B4) / (B8 + B4) 274 Normalised burn ratio (NBR): (B8 - B12) / (B8 + B12), differenced pre/post for severity 275 Water and flooding: B8A, B11, B4 — water goes near-black, wet soil distinct 276 Bare soil / earthworks: B11, B8, B2 — fresh excavation and berms separate from vegetation 277 Smoke and plumes: true colour, then B12/B8A/B4 to see through thin smoke 278 Sentinel-1 radar (SAR): VV/VH, for cloud cover, night, and detecting hulls at sea 279 ``` 280 281 Then reproduce it through the API: 282 283 ```bash 284 # token, valid about ten minutes 285 TOKEN=$(curl -s -X POST \ 286 'https://identity.dataspace.copernicus.eu/auth/realms/CDSE/protocol/openid-connect/token' \ 287 -H 'content-type: application/x-www-form-urlencoded' \ 288 -d 'grant_type=client_credentials' -d 'client_id=YOUR_CLIENT_ID' \ 289 --data-urlencode 'client_secret=YOUR_CLIENT_SECRET' | jq -r .access_token) 290 291 # which Sentinel-2 passes exist over this box in this window, and how cloudy 292 curl -s -X POST 'https://sh.dataspace.copernicus.eu/api/v1/catalog/1.0.0/search' \ 293 -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \ 294 -d '{"collections":["sentinel-2-l2a"], 295 "bbox":[30.0,46.0,30.3,46.3], 296 "datetime":"2026-03-01T00:00:00Z/2026-03-20T00:00:00Z", 297 "limit":20, 298 "fields":{"include":["id","properties.datetime","properties.eo:cloud_cover"]}}' \ 299 | jq '.features[] | {id, datetime:.properties.datetime, cloud:.properties["eo:cloud_cover"]}' 300 301 # render a false-colour burn-scar image for one date, as a GeoTIFF you can hash 302 curl -s -X POST 'https://sh.dataspace.copernicus.eu/api/v1/process' \ 303 -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \ 304 -d '{"input":{"bounds":{"bbox":[30.0,46.0,30.3,46.3]}, 305 "data":[{"type":"sentinel-2-l2a", 306 "dataFilter":{"timeRange":{"from":"2026-03-11T00:00:00Z","to":"2026-03-12T00:00:00Z"}}}]}, 307 "output":{"width":1024,"height":1024,"responses":[{"identifier":"default","format":{"type":"image/tiff"}}]}, 308 "evalscript":"//VERSION=3\nfunction setup(){return {input:[\"B12\",\"B8A\",\"B04\"],output:{bands:3}}}\nfunction evaluatePixel(s){return [s.B12*2.5, s.B8A*2.5, s.B04*2.5]}"}' \ 309 -o burnscar-2026-03-11.tiff 310 ``` 311 312 Hash the GeoTIFF the moment you download it and record the scene ID, the exact bounding box and the 313 acquisition datetime. That tuple is what makes the imagery claim reproducible — "a Sentinel-2 image 314 from around then" is not a citation. 315 316 Quotas are monthly processing units and are replenished on the first of the month; a 1024-pixel 317 request is cheap, a full-resolution multi-band pull over a province is not. Sentinel-2 revisits 318 every five days at the equator and the optical bands see nothing through cloud, which is why 319 Sentinel-1 radar is the fallback for a specific date you cannot miss. 320 321 ### Global Forest Watch Data API 322 323 Deforestation and fire alerts as a queryable dataset rather than a dashboard. The integrated alerts 324 layer combines three independent alert systems (GLAD-L, GLAD-S2 and RADD radar), which makes it one 325 of the few places where cross-sensor corroboration is already built in and labelled. 326 327 Create an account, exchange it for a token, then mint an API key; the key goes in an `x-api-key` 328 header. Without one, every query returns a 401 telling you so. 329 330 ```bash 331 # sign up and get a short-lived token 332 curl -s -X POST 'https://data-api.globalforestwatch.org/auth/token' \ 333 -d 'username=you@example.com' --data-urlencode 'password=YOUR_PASSWORD' | jq -r .data.access_token 334 335 # mint an API key with that token 336 curl -s -X POST 'https://data-api.globalforestwatch.org/auth/apikey' \ 337 -H "Authorization: Bearer $GFW_TOKEN" -H 'Content-Type: application/json' \ 338 -d '{"alias":"osint-research","organization":"research","email":"you@example.com","domains":[]}' \ 339 | jq -r .data.api_key 340 ``` 341 342 ```bash 343 export GFW_KEY='your-api-key' 344 345 # what datasets exist, and what each version is called 346 curl -s 'https://data-api.globalforestwatch.org/datasets' \ 347 | jq -r '.data[].dataset' | grep -E 'integrated_alerts|viirs|burned' 348 349 # daily integrated deforestation alerts for one country 350 curl -s -H "x-api-key: $GFW_KEY" -G \ 351 'https://data-api.globalforestwatch.org/dataset/gadm__integrated_alerts__iso_daily_alerts/latest/query/json' \ 352 --data-urlencode "sql=SELECT iso, gfw_integrated_alerts__date, SUM(alert__count) AS alerts 353 FROM results WHERE iso='BRA' AND gfw_integrated_alerts__date >= '2026-02-01' 354 GROUP BY iso, gfw_integrated_alerts__date ORDER BY 2 DESC" | jq '.data[0:5]' 355 356 # down to a second-level administrative area, which is a usable locality 357 curl -s -H "x-api-key: $GFW_KEY" -G \ 358 'https://data-api.globalforestwatch.org/dataset/gadm__integrated_alerts__adm2_daily_alerts/latest/query/json' \ 359 --data-urlencode "sql=SELECT adm1, adm2, SUM(alert__count) AS alerts FROM results 360 WHERE iso='COL' AND gfw_integrated_alerts__date >= '2026-01-01' 361 GROUP BY adm1, adm2 ORDER BY alerts DESC LIMIT 20" | jq '.data[0:5]' 362 363 # alert confidence, which is where the cross-sensor corroboration lives 364 curl -s -H "x-api-key: $GFW_KEY" -G \ 365 'https://data-api.globalforestwatch.org/dataset/gadm__integrated_alerts__adm2_daily_alerts/latest/query/json' \ 366 --data-urlencode "sql=SELECT gfw_integrated_alerts__confidence, SUM(alert__count) FROM results 367 WHERE iso='PER' GROUP BY 1" | jq '.data' 368 369 # VIIRS fire alerts from the same API, so fire and forest loss share one query layer 370 curl -s -H "x-api-key: $GFW_KEY" -G \ 371 'https://data-api.globalforestwatch.org/dataset/gadm__viirs__adm2_daily_alerts/latest/query/json' \ 372 --data-urlencode "sql=SELECT adm1, adm2, SUM(alert__count) FROM results 373 WHERE iso='IDN' AND alert__date >= '2026-02-01' GROUP BY 1,2 ORDER BY 3 DESC LIMIT 10" \ 374 | jq '.data[0:5]' 375 376 # which fields a dataset actually has, before you guess a column name 377 curl -s -H "x-api-key: $GFW_KEY" \ 378 'https://data-api.globalforestwatch.org/dataset/gadm__integrated_alerts__adm2_daily_alerts/latest/fields' \ 379 | jq -r '.data[].name' 380 381 # CSV instead of JSON, for a spreadsheet handover 382 curl -s -H "x-api-key: $GFW_KEY" -G \ 383 'https://data-api.globalforestwatch.org/dataset/gadm__integrated_alerts__iso_daily_alerts/latest/query/csv' \ 384 --data-urlencode "sql=SELECT * FROM results WHERE iso='BRA' LIMIT 1000" -o gfw-bra.csv 385 ``` 386 387 Versions are dated (`v20261003`), and `latest` resolves to the newest — pin the explicit version in 388 anything you cite, because `latest` moves under you. A `highest` confidence integrated alert means 389 more than one of the three systems detected the same loss, which is the built-in second source; a 390 `low` confidence alert is a single-sensor detection and should be treated as a lead. 391 392 An alert is tree-cover loss, not a crime. Legal logging concessions, plantation harvest cycles, 393 storm damage and seasonal leaf-off all generate alerts. The question that turns an alert into a 394 story is whether the polygon falls inside a protected area or a concession boundary, and that is a 395 separate dataset join. 396 397 ### Open Source Munitions Portal 398 399 A searchable reference library of verified photographs of munitions and their remnants, built by 400 Airwars and Armament Research Services. Web-only, no API, and the correct first stop when you have 401 a photograph of debris and no identification. Over a thousand verified entries, weighted heavily 402 towards the Middle East and Ukraine. 403 404 Search at [osmp.ngo](https://osmp.ngo/) by munition category, country, date or free text, then work 405 the comparison deliberately: 406 407 ```text 408 1. Measure first find a scale reference in your photo — a hand, a boot, a kerb, a 409 standard brick — and estimate diameter and length before you 410 browse, so you are filtering rather than pattern-matching 411 2. Filter by category bomb, rocket, artillery projectile, guided missile, submunition. 412 Getting the category right eliminates most of the library 413 3. Compare the parts tail assembly and fin count, fuze well, lug spacing, nose profile, 414 body seams, driving band. These are discriminating; overall shape 415 is not 416 4. Read the stencilling alphanumeric markings, lot numbers, factory codes, colour bands. 417 A legible lot number is the only thing that gets you to a 418 production batch and a country of manufacture 419 5. Check the remnant fragmentation pattern and wall thickness distinguish families 420 that look identical intact 421 6. Cross-reference CAT-UXO for explosive ordnance detail and Bulletpicker's 422 manual archive for the original technical drawings 423 ``` 424 425 Record the OSMP entry ID for every comparison you made, including the ones you rejected, and the 426 specific features you matched on. "Consistent with a 9M27K rocket on the basis of fin count and 427 motor diameter, per OSMP entry NNNN" is defensible. "A cluster munition" is not, unless you can 428 read the markings. 429 430 The geographic bias is a real limitation: a munition used outside the Middle East or Ukraine may 431 simply be absent from the library, and absence is not evidence of a different weapon. Pair OSMP 432 with [CAT-UXO](https://cat-uxo.com/) and 433 [Bulletpicker](https://bulletpicker.com/)'s scanned ordnance manuals, which have broader scope and 434 worse search. 435 436 ## Corroborating one event across two independent feeds 437 438 The single most useful habit in this area, and the thing that makes a claim stand up. "Independent" 439 means a different sensor or a different collection route, not a different website. 440 441 ```text 442 Claim a munitions strike on a named facility, 11 March 2026, from a social post 443 Source A ACLED event: query country + admin1 + date, with event_type 444 Explosions/Remote violence. Returns a coded event, its own sources, a 445 coordinate precision level and a fatality estimate 446 Source B NASA FIRMS: VIIRS detections in a tight box around the facility for that 447 date and the day after. A thermal anomaly at the right pixel is an 448 independent sensor observation, not a report 449 Source C Sentinel-2 via CDSE: the first clear pass after the date, false-colour 450 B12/B8A/B4. A new burn scar or structural change on imagery acquired after 451 and absent before is the third, strongest leg 452 Baseline the same FIRMS box for the preceding year, to show the pixel does not 453 routinely register; the preceding Sentinel-2 pass, to show the scar is new 454 Negative GDELT timelinevol for the facility name, to see whether coverage preceded 455 the imagery — coverage before the satellite pass means the reporting is not 456 derived from the imagery and is genuinely separate 457 Record per leg: dataset and pinned version, exact query, retrieval timestamp, 458 SHA-256 of any downloaded raster, and the scene ID with its acquisition time 459 ``` 460 461 Two legs from the same family is one leg: ACLED and Liveuamap both code from media reporting, 462 GDELT and a news aggregator both index the same wires, MODIS and VIIRS are different sensors but 463 can be on the same platform pass. A sensor observation plus a reported event is a genuine pair, and 464 imagery plus a sensor observation is better. 465 466 State the negative result as explicitly as the positive one. "No VIIRS detection in a 2 km box on 467 either date, and the first clear Sentinel-2 pass is nine days later" is a useful finding about the 468 limits of what can be established, and publishing it is what distinguishes this work from 469 advocacy. 470 471 ## Environment 472 473 | Source | What it gives you | 474 | --- | --- | 475 | [Global Forest Watch](https://www.globalforestwatch.org/) | Deforestation alerts and tree-cover change, near-real-time, with an API behind it. | 476 | [NASA FIRMS](https://firms.modaps.eosdis.nasa.gov/) | Active fire and thermal anomaly detections, updated within three hours of overpass. | 477 | [Global Fishing Watch](https://globalfishingwatch.org/map) | Apparent fishing effort and vessel-behaviour events inferred from AIS. See [transport tracking](/sheets/osint/transport-tracking) for its API. | 478 | [Copernicus Browser](https://browser.dataspace.copernicus.eu/) | Free Sentinel-1/2/3 imagery with band combinations for burn scars, water, vegetation and earthworks. | 479 | [UNOSAT](https://unosat.org/products) | UN satellite analyses of humanitarian emergencies, already interpreted and published with methodology. | 480 | [Resource Watch](https://resourcewatch.org/) | 300+ environmental and human-wellbeing datasets, several of them real-time. | 481 482 ## Tool reference 483 484 | Tool | What it does | Cost | 485 | --- | --- | --- | 486 | [ACLED (Armed Conflict Location & Event Data Project)](https://acleddata.com/) | ACLED provides data and analysis on political violence and protest around the world, facilitating research, policy making, and journalistic reporting. | partly free | 487 | [AllTrails](https://www.alltrails.com/) | AllTrails.com is a tool for discovering hiking, biking, and running trails worldwide, providing detailed trail maps, user reviews, and navigation support… | partly free | 488 | [Amazonia Socio Ambiental (RAISG)](https://www.amazoniasocioambiental.org/en/) | Amazon rainforest maps and shapefiles of natural protected areas, concessions, indigenous territories, oil, mining, roads, fires, deforestation in… | free | 489 | [Aqueduct Water Risk Atlas](https://www.wri.org/applications/aqueduct/water-risk-atlas/) | The Aqueduct water Risk Atlas, developed by the World Resources Institute (WRI), is an interactive platform for assessing water-related risks globally. | free | 490 | [BirdNet](https://birdnet.cornell.edu/map) | Identify bird sounds - find bird sounds on a global map. | free | 491 | [Bulletpicker](https://bulletpicker.com/pdf/bulletpicker-24-11-20.zip) | Bulletpicker.com is a collection of ammunition guidebooks and manuals from several different armed forces. | free | 492 | [CITES Trade Database](https://trade.cites.org/) | Around 23 million records of trade in wildlife since 1975. | free | 493 | [CryO Tools](https://cryo-tools.org/) | Scientific tools for investigating the cryosphere (areas with snow & ice) | free | 494 | [EIA Global Environmental Crime Tracker](https://eia-international.org/global-environmental-crime-tracker/) | Map/tracker of environmental crimes including trade in ivory, rhino, big cats, and other exotic animals. | free | 495 | [Environmental Justice Atlas](https://ejatlas.org/) | Map of environmental-related conflict globally | free | 496 | [Global Monitoring System - ECOSOLVE](https://www.ecosolve.eco/dashboard) | Illicit online wildlife markets data from over 30 countries and regions | free | 497 | [Google Flood Hub](https://sites.research.google/floods/) | A visual tool to monitor river levels and forecast floods based on AI models developed by Google Research. | free | 498 | [Locust Hub](https://locust-hub-hqfao.hub.arcgis.com/) | A repository for desert locust data with maps and other resources for tracking movements, early detection and planning locust control interventions. | free | 499 | [Merlin](https://merlin.allaboutbirds.org/) | Identify birds (visually), through an app. | free | 500 | [Movebank](https://www.movebank.org/) | Platform for animal tracking data. | free | 501 | [Nullschool Earth Map](https://earth.nullschool.net/#current) | View current and historic wind, weather, ocean and pollution conditions on an interactive animated map. | partly free | 502 | [Police Records Access Project](https://clean.calmatters.org/) | A database providing searchable access to California law enforcement records including police use-of-force incidents, shootings, and misconduct cases. | free | 503 | [Resource Watch](https://resourcewatch.org/) | A free open-data platform that hosts 300+ datasets on different topics relating to the environment and human well-being, including real-time datasets. | free | 504 | [River Runner Global](https://river-runner-global.samlearner.com/) | Calculate which water stream a drop of rain will follow | free | 505 | [Species+](https://www.speciesplus.net/species) | Centralized website with vulnerable species information. | free | 506 | [UNOSAT Analyses](https://unosat.org/products) | UNOSAT Analyses is a tool that maps humanitarian emergencies across the globe utilising United Nations Satellite Centre data. | free | 507 | [WildEye](https://global.wildeye.oxpeckers.org/) | Tracking tool for data on environmental and wildlife crime cases, including court cases and convictions, across the globe. | free | 508 | [Wildlife Trade Portal](https://www.wildlifetradeportal.org/) | An open-source tool to search wildlife seizure data worldwide. | free | 509 | [WildMe & WildBook](https://wildme.org/#/platforms/bass) | Open source pattern recognition software to identify unique whales, sharks, zebras, jaguars, skunks, fish and much more. | free | 510 | [World Database on Protected and Conserved Areas](https://www.protectedplanet.net/en/search-areas?geo_type=site) | A comprehensive global database on terrestrial and marine protected areas. Also known as Protected Planet. | free | 511 | CAT UXO | A repository for professionals working in the explosive ordnance disposal (EOD) space. | partly free | 512 513 ## Pitfalls 514 515 - **Event datasets reflect reporting, not reality.** Areas with more journalists produce more 516 recorded events. Never read event counts as a direct measure of violence. 517 - **Real-time aggregators are unverified.** Liveuamap and similar repost claims. Verify before use. 518 - **Thermal detections have mundane causes.** Flaring, agricultural burning and industrial process 519 heat all look alike from orbit. 520 - **Deforestation alerts include legal logging** and seasonal change. Alert ≠ crime. 521 - **Documenting harm carries duty of care.** Graphic material needs handling policies, and 522 identifying victims or witnesses can endanger them. Minimise what you publish. 523 524 ## Worked example 525 526 One datum: a coordinate and a date. `46.482, 30.724` on 11 March 2026, from a post claiming a 527 strike on a grain terminal. The coordinate is real and the returns below are illustrative; what 528 matters is which query answers which part of the claim. 529 530 ```bash 531 # 1. was anything coded as an event there 532 TOKEN=$(curl -s -X POST 'https://acleddata.com/oauth/token' \ 533 -d 'username=you@example.com' --data-urlencode 'password=PW' \ 534 -d 'grant_type=password' -d 'client_id=acled' -d 'scope=authenticated' | jq -r .access_token) 535 536 curl -s -H "Authorization: Bearer $TOKEN" -G 'https://acleddata.com/api/acled/read' \ 537 --data-urlencode 'country=Ukraine' --data-urlencode 'admin1=Odesa' \ 538 --data-urlencode 'event_date=2026-03-10|2026-03-13' \ 539 --data-urlencode 'event_date_where=BETWEEN' \ 540 --data-urlencode 'fields=event_date|sub_event_type|location|latitude|longitude|geo_precision|fatalities|source' \ 541 | jq '.data[]' 542 # one event, 2026-03-11, sub_event_type "Shelling/artillery/missile attack", 543 # geo_precision 2, coordinates the city centroid rather than the terminal 544 ``` 545 546 Geo-precision 2 means ACLED located this to a town, not a point — so it corroborates that something 547 happened in Odesa that day, and says nothing about this coordinate. 548 549 ```bash 550 # 2. an independent sensor: thermal detections in a tight box around the terminal 551 curl -s "https://firms.modaps.eosdis.nasa.gov/api/area/csv/$MAP_KEY/VIIRS_SNPP_NRT/30.70,46.46,30.75,46.50/2/2026-03-11" 552 # two detections, 2026-03-11 ~22:40 UTC, frp 4.8 and 2.1, confidence n, daynight N 553 ``` 554 555 A night-time detection with that radiative power inside a 4 km box is a real heat source, and 556 nothing about it came from a news report. 557 558 ```bash 559 # 3. the baseline — does this pixel burn routinely 560 for d in 2025-03-01 2025-06-01 2025-09-01 2025-12-01 2026-01-01 2026-02-01; do 561 curl -s "https://firms.modaps.eosdis.nasa.gov/api/area/csv/$MAP_KEY/VIIRS_SNPP_SP/30.70,46.46,30.75,46.50/10/$d" | tail -n +2 562 sleep 2 563 done | wc -l 564 # 0 — no detections in that box across sixty sampled days of the preceding year 565 ``` 566 567 That is what converts the hotspot from an observation into an anomaly. 568 569 ```bash 570 # 4. imagery: find the first clear pass after the date 571 TOK=$(curl -s -X POST \ 572 'https://identity.dataspace.copernicus.eu/auth/realms/CDSE/protocol/openid-connect/token' \ 573 -H 'content-type: application/x-www-form-urlencoded' \ 574 -d 'grant_type=client_credentials' -d 'client_id=ID' \ 575 --data-urlencode 'client_secret=SECRET' | jq -r .access_token) 576 577 curl -s -X POST 'https://sh.dataspace.copernicus.eu/api/v1/catalog/1.0.0/search' \ 578 -H "Authorization: Bearer $TOK" -H 'Content-Type: application/json' \ 579 -d '{"collections":["sentinel-2-l2a"],"bbox":[30.70,46.46,30.75,46.50], 580 "datetime":"2026-03-08T00:00:00Z/2026-03-20T00:00:00Z","limit":20, 581 "fields":{"include":["id","properties.datetime","properties.eo:cloud_cover"]}}' \ 582 | jq '.features[] | {id, datetime:.properties.datetime, cloud:.properties["eo:cloud_cover"]}' 583 # 2026-03-09 cloud 3%, 2026-03-14 cloud 11% — a usable before/after pair 584 ``` 585 586 Pull both as B12/B8A/B4 GeoTIFFs through `/api/v1/process`, hash each, and record the scene IDs. 587 The 14 March image shows a dark scar and a collapsed roof span on one silo where the 9 March image 588 shows an intact structure. 589 590 What you can assert: a thermal anomaly at the coordinate on the night of 11 March, from a sensor 591 and not a report; no comparable detection in that pixel across the preceding year; and structural 592 change visible between two named Sentinel-2 scenes bracketing the date. Three legs, two of them 593 independent of all media reporting, each tied to a query, a retrieval time and a hash. 594 595 What is not: the cause. Neither the hotspot nor the scar distinguishes a missile strike from a 596 drone, an accident or deliberate arson, and attribution needs the remnant. If a photograph of 597 debris surfaces, that goes to [OSMP](https://osmp.ngo/) for a markings comparison, and the 598 identification is stated as consistent-with until a lot number is legible. 599 600 What would falsify it: a second pre-event image showing the roof already damaged, a FIRMS quality 601 flag or geolocation error placing the detections outside the terminal, or a longer baseline showing 602 routine industrial heat at the same pixel. The imagery pair carries the structural-change claim; 603 the thermal detections only narrow the time window and do not identify a cause. 604 605 ## Broader catalogues 606 607 - [Conflict OSINT](https://tools.osintnewsletter.com/tool-categories/conflict-osint) 608 - [Environment & Wildlife OSINT](https://tools.osintnewsletter.com/tool-categories/environment-and-wildlife-osint) 609 - [Public Media OSINT](https://tools.osintnewsletter.com/tool-categories/public-media-osint) 610 611 612 ## More tools 613 614 Further tools for this area from the OSINT Newsletter Tools Library ([Conflict OSINT](https://tools.osintnewsletter.com/tool-categories/conflict-osint), [Environment & Wildlife OSINT](https://tools.osintnewsletter.com/tool-categories/environment-and-wildlife-osint), [Public Media OSINT](https://tools.osintnewsletter.com/tool-categories/public-media-osint)), excluding those already listed above. 615 616 | Tool | What it does | 617 | --- | --- | 618 | [1stHeadlines](https://mediabiasfactcheck.com/1stheadlines-bias-and-credibility/) | News aggregation and headline-monitoring site that brings together headlines from multiple news organisations in one searchable… | 619 | [AllYouCanRead](https://www.allyoucanread.com/) | A global directory of newspapers, magazines and news websites covering more than 200 countries. | 620 | [Bamqam](https://bamqam.com/) | A real-time geopolitical intelligence dashboard that aggregates military activity and conflict data into a map-based interface. | 621 | [Combat Aircraft](https://www.combataircraft.com/) | Military aviation reference database providing aircraft specifications, operators, imagery, aviation news, and background… | 622 | [Combined IUU Vessel List](https://iuu-vessels.org/Home/Search) | A free, searchable database that consolidates official Illegal, Unreported and Unregulated (IUU) fishing vessel lists published… | 623 | [Country Studies](https://countrystudies.us/) | A free online reference library providing detailed country profiles covering history, politics, society, economics, security… | 624 | [ECO-SOLVE Global Monitoring System](https://www.ecosolve.eco/dashboard) | An OSINT and AI-enabled monitoring platform for identifying and analysing online environmental crime, particularly illegal… | 625 | [EJAtlas (Environmental Justice Atlas)](https://ejatlas.org/) | A global, collaborative database that documents environmental conflicts and environmental justice struggles. | 626 | [Europe Media Monitor](https://media-monitor.europa.eu/home) | An automatic system that monitors global news media in near real time, aggregating multilingual news, alerts and trend analysis… | 627 | [Geoconfirmed](https://geoconfirmed.org/) | A collaborative OSINT platform to help analysts verify and geolocate images, videos, and events from conflicts worldwide. | 628 | [OSNT.IN](https://www.osnt.in/) | An AI-powered daily intelligence brief on the Russia-Ukraine war, synthesized from 200+ Ukrainian and Russian-language open… | 629 630 ## Sources 631 632 Both catalogues below are maintained by other people and are considerably larger than 633 this page. Use them as the canonical index; this sheet is a working route through them. 634 635 - [Bellingcat's Online Investigation Toolkit](https://bellingcat.gitbook.io/toolkit) — ~340 tools, each with its own 636 review page covering cost, difficulty, requirements and limitations. 637 - [OSINT Newsletter Tools Library](https://tools.osintnewsletter.com) — ~280 tools, organised by investigative goal. 638 639 Neither publishes a licence, so nothing here is copied from them: tool names, one-line 640 descriptions, cost flags and links are catalogue facts, and the method and commentary are 641 this site's own. See [credits](/credits).