Working With UFO sighting data is a mess, and this library doesn't fix that
If you've ever tried to work with UFO sighting records, you know the source material is basically unusable. The NUFORC database, the MUFON forms, the government datasets — they're all different schemas, different formats, and half of them contain free-text fields that look like they were written by people who didn't finish high school. That's where If Ufos Are Real The Ufo Library comes in, or at least where it tries to come in. It's a Python package that attempts to standardize UFO sighting data into a common structure. The basic premise is reasonable: ingest raw reports from multiple sources, clean them up, normalize the date formats and geographic coordinates, and output something queryable. In theory.
If Ufos Are Real The Ufo Library
The installation is straightforward enough. pip install ufo-library or whatever the current package name is on PyPI. You'll want a recent version of Python, preferably 3.10 or later, because the dependency chain gets finicky on older releases. The library depends on pandas, geopy, and a few other data processing tools that are usually fine, but watch out for version conflicts if you're already working in an environment with numpy pinned to an older build. Here's the workflow that actually works in practice, not what the readme claims: First, load your source data. The library supports CSV, JSON, and direct pulls from the NUFORC API. The NUFORC endpoint is rate-limited, so if you're pulling the full dataset you'll want to add delays between requests. I usually run it with a ten-second sleep between batches of fifty records. Without that, you'll get throttled and lose whatever progress you had mid-request.
Next, run the normalizer. This is where the library does its actual work — parsing dates that might be written as "July 4th 1997" or "07/04/97" or just "sometime in the 90s," extracting latitude and longitude from freeform location descriptions, and flagging records that are clearly duplicates. The deduplication is heuristic-based, so it's not perfect. I found about twelve percent of what it flagged as duplicates were actually separate reports of the same event from different witnesses who happened to file independently. You'll want to review those flags manually if accuracy matters for your use case. Then there's the geographic cleaning step, which is honestly the most useful part of the whole thing. Raw sighting data has coordinates that range from slightly wrong to completely impossible — I've seen sightings place in the middle of the Atlantic Ocean that were clearly meant to be coastal. The library has a validation pass that catches these, but it's aggressive. It will discard anything more than fifty kilometers from the nearest plausible landmass, which means you might lose legitimate offshore reports. My workaround was to increase the tolerance parameter to about two hundred kilometers and then manually review the outliers instead of trusting the automated filter. The output is a cleaned dataframe with standardized columns. You can export it to parquet, CSV, or feed it directly into a SQL database. The parquet format is significantly more efficient if you're doing repeated analysis — a cleaned dataset that's three hundred megabytes as CSV drops to about forty megabytes as parquet.
Get the Full Details

There are some real limitations you should know about before committing to this. The library doesn't handle non-English reports well. If your dataset includes international sightings, a lot of the date parsing and location extraction will fail silently, which is worse than it failing loudly because you won't immediately know which records are broken. I ended up writing a separate preprocessing step in Spanish and Portuguese before running the normalizer, which added maybe twenty minutes to my pipeline but caught a bunch of silently corrupted records. The timeline coverage is also uneven. The library includes a historical backfill feature that pulls archived data, but the records before 1980 are sparse across all sources. You'll get a lot of gaps in your temporal analysis that aren't the library's fault, but it still feels like a problem when you first notice them. For most people doing casual analysis or visualization projects, this library will save you about three days of data wrangling. For production work or research where data integrity is critical, I'd recommend using it as a starting point rather than a final solution. The normalization logic is sound in principle, but the edge cases in real-world sighting data are endless, and no library can handle them all automatically. You'll still need to inspect your results.
The project is maintained on GitHub. The documentation is adequate but not great — the API reference covers what the methods do, but it doesn't always explain the defaults or the gotchas. I'd recommend reading through the issue tracker before you start. People have already hit the same rate-limiting problems and geocoding edge cases that you will, and the maintainers have posted workarounds in the comments.