What this covers
Two thematic areas with mature, purpose-built open datasets: armed conflict and environmental change. Both are unusual in OSINT for having authoritative structured data behind them rather than just tools, which moves the skill from finding sources to reading methodologies and querying them properly.
Method
- Start from a bounded question with a place and a date range. These are datasets, not search engines — a query without a bounding box and a window either returns nothing useful or exceeds your quota.
- Read the codebook before the data. What ACLED counts as an event, how FIRMS defines a thermal anomaly, what a Global Forest Watch alert confidence class means: each materially changes what a number means, and each is documented.
- Query the dataset rather than the dashboard. Dashboards aggregate in ways you cannot audit and cannot cite. Pull the records, store them with the retrieval date, and do your own aggregation.
- Establish the baseline before the anomaly. One fire detection at an industrial site means nothing until you know how often that pixel normally burns. Pull the same query for the previous year and the previous month.
- Corroborate across two independent feeds. Independence means different sensors or different collection methods — a VIIRS hotspot and a Sentinel-2 burn scar, an ACLED event and a dated photograph, a GDELT article cluster and a satellite image. Two aggregators that both scrape the same wire service are one source.
- For munitions, work from markings outward, and write “consistent with” unless you have stencilling, lot numbers or a fuze you can read. This is the single most common place open-source conflict reporting goes wrong.
- Record the evidentiary chain for each claim: the dataset and its version, the exact query, the retrieval timestamp, the hash of any imagery you downloaded, and the second source. A finding that cannot be re-run is not a finding.
Conflict
| Source | What it gives you |
|---|---|
| ACLED | Coded conflict events — date, location, actors, event type, fatalities — globally, with a published methodology and an API. The reference dataset. |
| GDELT | Global news coverage indexed by location, theme, tone and actor, updated every 15 minutes. Not events; coverage of events. |
| Liveuamap | Near-real-time mapped events aggregated from social media. Fast but unverified; a lead generator only. |
| Geoconfirmed | Volunteer-geolocated conflict media, each entry with the coordinates and the reasoning. Verified work you can audit. |
| Open Source Munitions Portal | Identification reference for munitions and their remnants, which is how you turn a photo of debris into a weapon type. |
| Bellingcat’s Civilian Harm datasets | Verified incident databases with the sourcing attached to each entry. |
ACLED API
Coded event data with a documented methodology, which is what separates it from every aggregator in the table above. Each record carries a date, coordinates with a stated precision level, named actors, an event type and sub-event type, a fatality estimate, and the sources it was coded from.
The API moved and the auth model changed. The old api.acleddata.com host with
key=/email= query parameters is gone — the hostname no longer resolves, and the key system was
retired in September 2025. Current access is a bearer token from an OAuth password grant against
your myACLED account, used against acleddata.com/api/.
# token: valid 24 hours, with a refresh token valid 14 days
TOKEN=$(curl -s -X POST 'https://acleddata.com/oauth/token' \
-d 'username=you@example.com' --data-urlencode 'password=YOUR_PASSWORD' \
-d 'grant_type=password' -d 'client_id=acled' -d 'scope=authenticated' \
| jq -r .access_token)
# events in one country over a window, as JSON
curl -s -H "Authorization: Bearer $TOKEN" -G 'https://acleddata.com/api/acled/read' \
--data-urlencode 'country=Ukraine' \
--data-urlencode 'event_date=2026-01-01|2026-03-31' \
--data-urlencode 'event_date_where=BETWEEN' --data-urlencode 'limit=0' \
| jq '.count'
# CSV straight to disk, which is what you archive
curl -s -H "Authorization: Bearer $TOKEN" -G 'https://acleddata.com/api/acled/read' \
--data-urlencode '_format=csv' --data-urlencode 'country=Sudan' \
--data-urlencode 'event_date=2026-01-01|2026-03-31' \
--data-urlencode 'event_date_where=BETWEEN' --data-urlencode 'limit=0' \
-o "acled-sudan-$(date -u +%F).csv"
# one event type only — the sub-event taxonomy is where the precision is
curl -s -H "Authorization: Bearer $TOKEN" -G 'https://acleddata.com/api/acled/read' \
--data-urlencode 'country=Nigeria' \
--data-urlencode 'event_type=Explosions/Remote violence' \
--data-urlencode 'event_date=2026-01-01|2026-03-31' \
--data-urlencode 'event_date_where=BETWEEN' | jq '.data | length'
# a named actor's events, for an order-of-battle or attribution question
curl -s -H "Authorization: Bearer $TOKEN" -G 'https://acleddata.com/api/acled/read' \
--data-urlencode 'actor1=Military Forces of Sudan (2019-)' \
--data-urlencode 'actor1_where=%3D' --data-urlencode 'limit=200' | jq '.count'
# narrow to an admin region, which is how you bound to a locality
curl -s -H "Authorization: Bearer $TOKEN" -G 'https://acleddata.com/api/acled/read' \
--data-urlencode 'country=Mali' --data-urlencode 'admin1=Mopti' \
--data-urlencode 'event_date=2026-01-01|2026-03-31' \
--data-urlencode 'event_date_where=BETWEEN' | jq '.data | length'
# only the fields you need, which keeps responses small
curl -s -H "Authorization: Bearer $TOKEN" -G 'https://acleddata.com/api/acled/read' \
--data-urlencode 'country=Ukraine' --data-urlencode 'limit=50' \
--data-urlencode 'fields=event_date|event_type|sub_event_type|location|latitude|longitude|fatalities|source' \
| jq '.data[0]'
# the deleted-records endpoint, so a stored copy can be reconciled with upstream revisions
curl -s -H "Authorization: Bearer $TOKEN" -G 'https://acleddata.com/api/deleted/read' \
--data-urlencode 'deleted_timestamp=1735689600' | jq '.count'
The R package acledR and the acled Python package both wrap this and handle the token
refresh; either is less work than scripting the grant yourself if you query regularly. Access is
free for non-commercial use with registration, and the terms restrict redistribution of the raw
data — cite and link rather than re-publishing the CSV.
Read the codebook before drawing any conclusion. Three things in particular: coordinate precision
is a graded field, and a precision-3 event is located to an admin region rather than a point;
fatality figures are the most conservative reported estimate, not a count; and event density tracks
reporting density, so comparing two regions compares their journalism as much as their violence.
The deleted endpoint exists because ACLED revises history as better sourcing arrives, which means
a stored extract drifts from upstream.
GDELT DOC 2.0 API
Global news coverage, indexed every 15 minutes across languages, queryable by phrase, location, theme, tone and source country. It tells you what was reported, where and in what volume, which is a different and complementary question to what happened. No key, no registration.
# articles matching a phrase, geographically filtered, as JSON
curl -s -G 'https://api.gdeltproject.org/api/v2/doc/doc' \
--data-urlencode 'query="artillery" sourcecountry:UP' \
--data-urlencode 'mode=artlist' --data-urlencode 'format=json' \
--data-urlencode 'maxrecords=100' --data-urlencode 'timespan=7d' \
| jq '.articles[] | {seendate, domain, title, language}'
# coverage volume over time — the shape of the curve is the finding
curl -s -G 'https://api.gdeltproject.org/api/v2/doc/doc' \
--data-urlencode 'query="port of berbera"' \
--data-urlencode 'mode=timelinevol' --data-urlencode 'format=json' \
--data-urlencode 'timespan=3m' | jq '.timeline[0].data[-10:]'
# an exact window rather than a rolling span
curl -s -G 'https://api.gdeltproject.org/api/v2/doc/doc' \
--data-urlencode 'query="chemical plant" sourcelang:eng' \
--data-urlencode 'mode=artlist' --data-urlencode 'format=json' \
--data-urlencode 'startdatetime=20260301000000' \
--data-urlencode 'enddatetime=20260315000000' | jq '.articles | length'
# which countries are covering a story, and which are not
curl -s -G 'https://api.gdeltproject.org/api/v2/doc/doc' \
--data-urlencode 'query="grain corridor"' \
--data-urlencode 'mode=timelinesourcecountry' --data-urlencode 'format=json' \
--data-urlencode 'timespan=1m' | jq '[.timeline[].series] | .[0:8]'
# tone distribution, for spotting a coordinated framing shift
curl -s -G 'https://api.gdeltproject.org/api/v2/doc/doc' \
--data-urlencode 'query="sanctions relief"' \
--data-urlencode 'mode=tonechart' --data-urlencode 'format=json' \
--data-urlencode 'timespan=2w' | jq '.tonechart'
# images published alongside the coverage, which sometimes surface the source photo
curl -s -G 'https://api.gdeltproject.org/api/v2/doc/doc' \
--data-urlencode 'query="refinery fire"' \
--data-urlencode 'mode=imagecollageinfo' --data-urlencode 'format=json' \
--data-urlencode 'timespan=3d' | jq '.images[0:5]'
The DOC API serves a rolling window — roughly the last three months by default, reaching back to 1 January 2017 with explicit datetimes — and caps at 250 records per request for article and image modes. For anything longer or larger, the GDELT Event and GKG tables are published on BigQuery and as raw CSV, which is the right route for a multi-year series.
What it does not tell you: GDELT indexes articles, not events, so one incident covered by four hundred outlets is four hundred records. A volume spike measures attention. Syndication means the same wire copy appears under many domains, which is why a GDELT cluster and an aggregator that scrapes the same wires are not two independent sources.
NASA FIRMS
Active fire and thermal anomaly detections from MODIS and VIIRS, available within three hours of satellite overpass. Thermal detection is a far broader OSINT primitive than wildfire monitoring: gas flaring, industrial process heat, shelling, burning buildings and crop residue all register.
Get a free MAP_KEY from
firms.modaps.eosdis.nasa.gov/api/map_key.
export MAP_KEY='your-map-key'
# check the key works and how much of your quota is left
curl -s "https://firms.modaps.eosdis.nasa.gov/mapserver/mapkey_status/?MAP_KEY=$MAP_KEY"
# detections in a bounding box over the last day — order is west,south,east,north
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"
# a specific past date, with the day range starting from it (1-10 days)
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" \
-o firms-2026-03-11.csv
# the archival standard-quality product, for anything older than the NRT window
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" \
-o firms-archive.csv
# which products cover which dates, so you query one that exists
curl -s "https://firms.modaps.eosdis.nasa.gov/api/data_availability/csv/$MAP_KEY/all"
# filter to high-confidence daytime detections at a site
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" \
| awk -F, 'NR==1 || ($10=="h" || $10>=80)'
# a year of the same small box, to build the baseline before you call an anomaly
for d in 2025-0{1..9}-01 2025-1{0..2}-01; do
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"
sleep 2
done > baseline.csv
The quota is 5,000 transactions per 10-minute window per key, and a large bounding box counts as
several, so loop with a sleep. Day range per request is 1–10. VIIRS at 375 m resolves far more than
MODIS at 1 km and is what you want for anything at a single site; the _NRT products are the fast
ones, _SP the reprocessed archive.
Reading a detection: confidence is l/n/h for VIIRS and a 0–100 percentage for MODIS, frp
is fire radiative power in megawatts, daynight matters because daytime detections suffer more
false positives from reflective surfaces, and scan/track give the actual pixel footprint — the
detection is a pixel, not a point, so the coordinates are the pixel centre and the real event is
somewhere within a few hundred metres of it.
A hotspot is a thermal anomaly and nothing more. Gas flares burn continuously and appear every single night, which is why the baseline query above matters: the finding is not “a hotspot at this site”, it is “a hotspot at a site that produced none in the preceding twelve months”.
Sentinel Hub on the Copernicus Data Space Ecosystem
Free Sentinel-1, Sentinel-2 and Sentinel-3 imagery, both as an interactive browser and as an API. Use the browser to find the scene and settle the band combination; use the API to pull the exact same thing reproducibly, with a date and a bounding box you can cite.
Access moved to the Copernicus Data Space Ecosystem, which replaced both the old Copernicus Open Access Hub and the commercial Sentinel Hub free tier for Sentinel data. Register at dataspace.copernicus.eu, create an OAuth client in your account settings, and you get a monthly processing-unit quota at no cost.
Start in EO Browser: set the area, set the date range, pick Sentinel-2 L2A, and step through the available passes. The band combinations that make change visible where true colour hides it:
Burn scars (Sentinel-2): B12, B8A, B4 — recent burns go red-brown, vegetation green
Vegetation health (NDVI): (B8 - B4) / (B8 + B4)
Normalised burn ratio (NBR): (B8 - B12) / (B8 + B12), differenced pre/post for severity
Water and flooding: B8A, B11, B4 — water goes near-black, wet soil distinct
Bare soil / earthworks: B11, B8, B2 — fresh excavation and berms separate from vegetation
Smoke and plumes: true colour, then B12/B8A/B4 to see through thin smoke
Sentinel-1 radar (SAR): VV/VH, for cloud cover, night, and detecting hulls at sea
Then reproduce it through the API:
# token, valid about ten minutes
TOKEN=$(curl -s -X POST \
'https://identity.dataspace.copernicus.eu/auth/realms/CDSE/protocol/openid-connect/token' \
-H 'content-type: application/x-www-form-urlencoded' \
-d 'grant_type=client_credentials' -d 'client_id=YOUR_CLIENT_ID' \
--data-urlencode 'client_secret=YOUR_CLIENT_SECRET' | jq -r .access_token)
# which Sentinel-2 passes exist over this box in this window, and how cloudy
curl -s -X POST 'https://sh.dataspace.copernicus.eu/api/v1/catalog/1.0.0/search' \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{"collections":["sentinel-2-l2a"],
"bbox":[30.0,46.0,30.3,46.3],
"datetime":"2026-03-01T00:00:00Z/2026-03-20T00:00:00Z",
"limit":20,
"fields":{"include":["id","properties.datetime","properties.eo:cloud_cover"]}}' \
| jq '.features[] | {id, datetime:.properties.datetime, cloud:.properties["eo:cloud_cover"]}'
# render a false-colour burn-scar image for one date, as a GeoTIFF you can hash
curl -s -X POST 'https://sh.dataspace.copernicus.eu/api/v1/process' \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{"input":{"bounds":{"bbox":[30.0,46.0,30.3,46.3]},
"data":[{"type":"sentinel-2-l2a",
"dataFilter":{"timeRange":{"from":"2026-03-11T00:00:00Z","to":"2026-03-12T00:00:00Z"}}}]},
"output":{"width":1024,"height":1024,"responses":[{"identifier":"default","format":{"type":"image/tiff"}}]},
"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]}"}' \
-o burnscar-2026-03-11.tiff
Hash the GeoTIFF the moment you download it and record the scene ID, the exact bounding box and the acquisition datetime. That tuple is what makes the imagery claim reproducible — “a Sentinel-2 image from around then” is not a citation.
Quotas are monthly processing units and are replenished on the first of the month; a 1024-pixel request is cheap, a full-resolution multi-band pull over a province is not. Sentinel-2 revisits every five days at the equator and the optical bands see nothing through cloud, which is why Sentinel-1 radar is the fallback for a specific date you cannot miss.
Global Forest Watch Data API
Deforestation and fire alerts as a queryable dataset rather than a dashboard. The integrated alerts layer combines three independent alert systems (GLAD-L, GLAD-S2 and RADD radar), which makes it one of the few places where cross-sensor corroboration is already built in and labelled.
Create an account, exchange it for a token, then mint an API key; the key goes in an x-api-key
header. Without one, every query returns a 401 telling you so.
# sign up and get a short-lived token
curl -s -X POST 'https://data-api.globalforestwatch.org/auth/token' \
-d 'username=you@example.com' --data-urlencode 'password=YOUR_PASSWORD' | jq -r .data.access_token
# mint an API key with that token
curl -s -X POST 'https://data-api.globalforestwatch.org/auth/apikey' \
-H "Authorization: Bearer $GFW_TOKEN" -H 'Content-Type: application/json' \
-d '{"alias":"osint-research","organization":"research","email":"you@example.com","domains":[]}' \
| jq -r .data.api_key
export GFW_KEY='your-api-key'
# what datasets exist, and what each version is called
curl -s 'https://data-api.globalforestwatch.org/datasets' \
| jq -r '.data[].dataset' | grep -E 'integrated_alerts|viirs|burned'
# daily integrated deforestation alerts for one country
curl -s -H "x-api-key: $GFW_KEY" -G \
'https://data-api.globalforestwatch.org/dataset/gadm__integrated_alerts__iso_daily_alerts/latest/query/json' \
--data-urlencode "sql=SELECT iso, gfw_integrated_alerts__date, SUM(alert__count) AS alerts
FROM results WHERE iso='BRA' AND gfw_integrated_alerts__date >= '2026-02-01'
GROUP BY iso, gfw_integrated_alerts__date ORDER BY 2 DESC" | jq '.data[0:5]'
# down to a second-level administrative area, which is a usable locality
curl -s -H "x-api-key: $GFW_KEY" -G \
'https://data-api.globalforestwatch.org/dataset/gadm__integrated_alerts__adm2_daily_alerts/latest/query/json' \
--data-urlencode "sql=SELECT adm1, adm2, SUM(alert__count) AS alerts FROM results
WHERE iso='COL' AND gfw_integrated_alerts__date >= '2026-01-01'
GROUP BY adm1, adm2 ORDER BY alerts DESC LIMIT 20" | jq '.data[0:5]'
# alert confidence, which is where the cross-sensor corroboration lives
curl -s -H "x-api-key: $GFW_KEY" -G \
'https://data-api.globalforestwatch.org/dataset/gadm__integrated_alerts__adm2_daily_alerts/latest/query/json' \
--data-urlencode "sql=SELECT gfw_integrated_alerts__confidence, SUM(alert__count) FROM results
WHERE iso='PER' GROUP BY 1" | jq '.data'
# VIIRS fire alerts from the same API, so fire and forest loss share one query layer
curl -s -H "x-api-key: $GFW_KEY" -G \
'https://data-api.globalforestwatch.org/dataset/gadm__viirs__adm2_daily_alerts/latest/query/json' \
--data-urlencode "sql=SELECT adm1, adm2, SUM(alert__count) FROM results
WHERE iso='IDN' AND alert__date >= '2026-02-01' GROUP BY 1,2 ORDER BY 3 DESC LIMIT 10" \
| jq '.data[0:5]'
# which fields a dataset actually has, before you guess a column name
curl -s -H "x-api-key: $GFW_KEY" \
'https://data-api.globalforestwatch.org/dataset/gadm__integrated_alerts__adm2_daily_alerts/latest/fields' \
| jq -r '.data[].name'
# CSV instead of JSON, for a spreadsheet handover
curl -s -H "x-api-key: $GFW_KEY" -G \
'https://data-api.globalforestwatch.org/dataset/gadm__integrated_alerts__iso_daily_alerts/latest/query/csv' \
--data-urlencode "sql=SELECT * FROM results WHERE iso='BRA' LIMIT 1000" -o gfw-bra.csv
Versions are dated (v20261003), and latest resolves to the newest — pin the explicit version in
anything you cite, because latest moves under you. A highest confidence integrated alert means
more than one of the three systems detected the same loss, which is the built-in second source; a
low confidence alert is a single-sensor detection and should be treated as a lead.
An alert is tree-cover loss, not a crime. Legal logging concessions, plantation harvest cycles, storm damage and seasonal leaf-off all generate alerts. The question that turns an alert into a story is whether the polygon falls inside a protected area or a concession boundary, and that is a separate dataset join.
Open Source Munitions Portal
A searchable reference library of verified photographs of munitions and their remnants, built by Airwars and Armament Research Services. Web-only, no API, and the correct first stop when you have a photograph of debris and no identification. Over a thousand verified entries, weighted heavily towards the Middle East and Ukraine.
Search at osmp.ngo by munition category, country, date or free text, then work the comparison deliberately:
1. Measure first find a scale reference in your photo — a hand, a boot, a kerb, a
standard brick — and estimate diameter and length before you
browse, so you are filtering rather than pattern-matching
2. Filter by category bomb, rocket, artillery projectile, guided missile, submunition.
Getting the category right eliminates most of the library
3. Compare the parts tail assembly and fin count, fuze well, lug spacing, nose profile,
body seams, driving band. These are discriminating; overall shape
is not
4. Read the stencilling alphanumeric markings, lot numbers, factory codes, colour bands.
A legible lot number is the only thing that gets you to a
production batch and a country of manufacture
5. Check the remnant fragmentation pattern and wall thickness distinguish families
that look identical intact
6. Cross-reference CAT-UXO for explosive ordnance detail and Bulletpicker's
manual archive for the original technical drawings
Record the OSMP entry ID for every comparison you made, including the ones you rejected, and the specific features you matched on. “Consistent with a 9M27K rocket on the basis of fin count and motor diameter, per OSMP entry NNNN” is defensible. “A cluster munition” is not, unless you can read the markings.
The geographic bias is a real limitation: a munition used outside the Middle East or Ukraine may simply be absent from the library, and absence is not evidence of a different weapon. Pair OSMP with CAT-UXO and Bulletpicker’s scanned ordnance manuals, which have broader scope and worse search.
Corroborating one event across two independent feeds
The single most useful habit in this area, and the thing that makes a claim stand up. “Independent” means a different sensor or a different collection route, not a different website.
Claim a munitions strike on a named facility, 11 March 2026, from a social post
Source A ACLED event: query country + admin1 + date, with event_type
Explosions/Remote violence. Returns a coded event, its own sources, a
coordinate precision level and a fatality estimate
Source B NASA FIRMS: VIIRS detections in a tight box around the facility for that
date and the day after. A thermal anomaly at the right pixel is an
independent sensor observation, not a report
Source C Sentinel-2 via CDSE: the first clear pass after the date, false-colour
B12/B8A/B4. A new burn scar or structural change on imagery acquired after
and absent before is the third, strongest leg
Baseline the same FIRMS box for the preceding year, to show the pixel does not
routinely register; the preceding Sentinel-2 pass, to show the scar is new
Negative GDELT timelinevol for the facility name, to see whether coverage preceded
the imagery — coverage before the satellite pass means the reporting is not
derived from the imagery and is genuinely separate
Record per leg: dataset and pinned version, exact query, retrieval timestamp,
SHA-256 of any downloaded raster, and the scene ID with its acquisition time
Two legs from the same family is one leg: ACLED and Liveuamap both code from media reporting, GDELT and a news aggregator both index the same wires, MODIS and VIIRS are different sensors but can be on the same platform pass. A sensor observation plus a reported event is a genuine pair, and imagery plus a sensor observation is better.
State the negative result as explicitly as the positive one. “No VIIRS detection in a 2 km box on either date, and the first clear Sentinel-2 pass is nine days later” is a useful finding about the limits of what can be established, and publishing it is what distinguishes this work from advocacy.
Environment
| Source | What it gives you |
|---|---|
| Global Forest Watch | Deforestation alerts and tree-cover change, near-real-time, with an API behind it. |
| NASA FIRMS | Active fire and thermal anomaly detections, updated within three hours of overpass. |
| Global Fishing Watch | Apparent fishing effort and vessel-behaviour events inferred from AIS. See transport tracking for its API. |
| Copernicus Browser | Free Sentinel-1/2/3 imagery with band combinations for burn scars, water, vegetation and earthworks. |
| UNOSAT | UN satellite analyses of humanitarian emergencies, already interpreted and published with methodology. |
| Resource Watch | 300+ environmental and human-wellbeing datasets, several of them real-time. |
Tool reference
| Tool | What it does | Cost |
|---|---|---|
| ACLED (Armed Conflict Location & Event Data Project) | ACLED provides data and analysis on political violence and protest around the world, facilitating research, policy making, and journalistic reporting. | partly free |
| AllTrails | AllTrails.com is a tool for discovering hiking, biking, and running trails worldwide, providing detailed trail maps, user reviews, and navigation support… | partly free |
| Amazonia Socio Ambiental (RAISG) | Amazon rainforest maps and shapefiles of natural protected areas, concessions, indigenous territories, oil, mining, roads, fires, deforestation in… | free |
| 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 |
| BirdNet | Identify bird sounds - find bird sounds on a global map. | free |
| Bulletpicker | Bulletpicker.com is a collection of ammunition guidebooks and manuals from several different armed forces. | free |
| CITES Trade Database | Around 23 million records of trade in wildlife since 1975. | free |
| CryO Tools | Scientific tools for investigating the cryosphere (areas with snow & ice) | free |
| EIA Global Environmental Crime Tracker | Map/tracker of environmental crimes including trade in ivory, rhino, big cats, and other exotic animals. | free |
| Environmental Justice Atlas | Map of environmental-related conflict globally | free |
| Global Monitoring System - ECOSOLVE | Illicit online wildlife markets data from over 30 countries and regions | free |
| Google Flood Hub | A visual tool to monitor river levels and forecast floods based on AI models developed by Google Research. | free |
| Locust Hub | A repository for desert locust data with maps and other resources for tracking movements, early detection and planning locust control interventions. | free |
| Merlin | Identify birds (visually), through an app. | free |
| Movebank | Platform for animal tracking data. | free |
| Nullschool Earth Map | View current and historic wind, weather, ocean and pollution conditions on an interactive animated map. | partly free |
| Police Records Access Project | A database providing searchable access to California law enforcement records including police use-of-force incidents, shootings, and misconduct cases. | free |
| Resource Watch | 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 |
| River Runner Global | Calculate which water stream a drop of rain will follow | free |
| Species+ | Centralized website with vulnerable species information. | free |
| UNOSAT Analyses | UNOSAT Analyses is a tool that maps humanitarian emergencies across the globe utilising United Nations Satellite Centre data. | free |
| WildEye | Tracking tool for data on environmental and wildlife crime cases, including court cases and convictions, across the globe. | free |
| Wildlife Trade Portal | An open-source tool to search wildlife seizure data worldwide. | free |
| WildMe & WildBook | Open source pattern recognition software to identify unique whales, sharks, zebras, jaguars, skunks, fish and much more. | free |
| World Database on Protected and Conserved Areas | A comprehensive global database on terrestrial and marine protected areas. Also known as Protected Planet. | free |
| CAT UXO | A repository for professionals working in the explosive ordnance disposal (EOD) space. | partly free |
Pitfalls
- Event datasets reflect reporting, not reality. Areas with more journalists produce more recorded events. Never read event counts as a direct measure of violence.
- Real-time aggregators are unverified. Liveuamap and similar repost claims. Verify before use.
- Thermal detections have mundane causes. Flaring, agricultural burning and industrial process heat all look alike from orbit.
- Deforestation alerts include legal logging and seasonal change. Alert ≠ crime.
- Documenting harm carries duty of care. Graphic material needs handling policies, and identifying victims or witnesses can endanger them. Minimise what you publish.
Worked example
One datum: a coordinate and a date. 46.482, 30.724 on 11 March 2026, from a post claiming a
strike on a grain terminal. The coordinate is real and the returns below are illustrative; what
matters is which query answers which part of the claim.
# 1. was anything coded as an event there
TOKEN=$(curl -s -X POST 'https://acleddata.com/oauth/token' \
-d 'username=you@example.com' --data-urlencode 'password=PW' \
-d 'grant_type=password' -d 'client_id=acled' -d 'scope=authenticated' | jq -r .access_token)
curl -s -H "Authorization: Bearer $TOKEN" -G 'https://acleddata.com/api/acled/read' \
--data-urlencode 'country=Ukraine' --data-urlencode 'admin1=Odesa' \
--data-urlencode 'event_date=2026-03-10|2026-03-13' \
--data-urlencode 'event_date_where=BETWEEN' \
--data-urlencode 'fields=event_date|sub_event_type|location|latitude|longitude|geo_precision|fatalities|source' \
| jq '.data[]'
# one event, 2026-03-11, sub_event_type "Shelling/artillery/missile attack",
# geo_precision 2, coordinates the city centroid rather than the terminal
Geo-precision 2 means ACLED located this to a town, not a point — so it corroborates that something happened in Odesa that day, and says nothing about this coordinate.
# 2. an independent sensor: thermal detections in a tight box around the terminal
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"
# two detections, 2026-03-11 ~22:40 UTC, frp 4.8 and 2.1, confidence n, daynight N
A night-time detection with that radiative power inside a 4 km box is a real heat source, and nothing about it came from a news report.
# 3. the baseline — does this pixel burn routinely
for d in 2025-03-01 2025-06-01 2025-09-01 2025-12-01 2026-01-01 2026-02-01; do
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
sleep 2
done | wc -l
# 0 — no detections in that box across sixty sampled days of the preceding year
That is what converts the hotspot from an observation into an anomaly.
# 4. imagery: find the first clear pass after the date
TOK=$(curl -s -X POST \
'https://identity.dataspace.copernicus.eu/auth/realms/CDSE/protocol/openid-connect/token' \
-H 'content-type: application/x-www-form-urlencoded' \
-d 'grant_type=client_credentials' -d 'client_id=ID' \
--data-urlencode 'client_secret=SECRET' | jq -r .access_token)
curl -s -X POST 'https://sh.dataspace.copernicus.eu/api/v1/catalog/1.0.0/search' \
-H "Authorization: Bearer $TOK" -H 'Content-Type: application/json' \
-d '{"collections":["sentinel-2-l2a"],"bbox":[30.70,46.46,30.75,46.50],
"datetime":"2026-03-08T00:00:00Z/2026-03-20T00:00:00Z","limit":20,
"fields":{"include":["id","properties.datetime","properties.eo:cloud_cover"]}}' \
| jq '.features[] | {id, datetime:.properties.datetime, cloud:.properties["eo:cloud_cover"]}'
# 2026-03-09 cloud 3%, 2026-03-14 cloud 11% — a usable before/after pair
Pull both as B12/B8A/B4 GeoTIFFs through /api/v1/process, hash each, and record the scene IDs.
The 14 March image shows a dark scar and a collapsed roof span on one silo where the 9 March image
shows an intact structure.
What you can assert: a thermal anomaly at the coordinate on the night of 11 March, from a sensor and not a report; no comparable detection in that pixel across the preceding year; and structural change visible between two named Sentinel-2 scenes bracketing the date. Three legs, two of them independent of all media reporting, each tied to a query, a retrieval time and a hash.
What is not: the cause. Neither the hotspot nor the scar distinguishes a missile strike from a drone, an accident or deliberate arson, and attribution needs the remnant. If a photograph of debris surfaces, that goes to OSMP for a markings comparison, and the identification is stated as consistent-with until a lot number is legible.
What would falsify it: a second pre-event image showing the roof already damaged, a FIRMS quality flag or geolocation error placing the detections outside the terminal, or a longer baseline showing routine industrial heat at the same pixel. The imagery pair carries the structural-change claim; the thermal detections only narrow the time window and do not identify a cause.
Broader catalogues
More tools
Further tools for this area from the OSINT Newsletter Tools Library (Conflict OSINT, Environment & Wildlife OSINT, Public Media OSINT), excluding those already listed above.
| Tool | What it does |
|---|---|
| 1stHeadlines | News aggregation and headline-monitoring site that brings together headlines from multiple news organisations in one searchable… |
| AllYouCanRead | A global directory of newspapers, magazines and news websites covering more than 200 countries. |
| Bamqam | A real-time geopolitical intelligence dashboard that aggregates military activity and conflict data into a map-based interface. |
| Combat Aircraft | Military aviation reference database providing aircraft specifications, operators, imagery, aviation news, and background… |
| Combined IUU Vessel List | A free, searchable database that consolidates official Illegal, Unreported and Unregulated (IUU) fishing vessel lists published… |
| Country Studies | A free online reference library providing detailed country profiles covering history, politics, society, economics, security… |
| ECO-SOLVE Global Monitoring System | An OSINT and AI-enabled monitoring platform for identifying and analysing online environmental crime, particularly illegal… |
| EJAtlas (Environmental Justice Atlas) | A global, collaborative database that documents environmental conflicts and environmental justice struggles. |
| Europe Media Monitor | An automatic system that monitors global news media in near real time, aggregating multilingual news, alerts and trend analysis… |
| Geoconfirmed | A collaborative OSINT platform to help analysts verify and geolocate images, videos, and events from conflicts worldwide. |
| OSNT.IN | An AI-powered daily intelligence brief on the Russia-Ukraine war, synthesized from 200+ Ukrainian and Russian-language open… |
Sources
Both catalogues below are maintained by other people and are considerably larger than this page. Use them as the canonical index; this sheet is a working route through them.
- Bellingcat’s Online Investigation Toolkit — ~340 tools, each with its own review page covering cost, difficulty, requirements and limitations.
- OSINT Newsletter Tools Library — ~280 tools, organised by investigative goal.
Neither publishes a licence, so nothing here is copied from them: tool names, one-line descriptions, cost flags and links are catalogue facts, and the method and commentary are this site’s own. See credits.