All posts
Tutorial7 min read

Extract EXIF and GPS metadata from a photo with ExifTool or Python

Read GPS coordinates, camera, and capture time out of a photo — one file in Preview, a whole folder to CSV with ExifTool, or in Python. Includes HEIC, RAW, and the timestamp tags that disagree.

By · Updated

Every photo from a phone or modern camera carries EXIF — a metadata block with the camera model, exposure settings, timestamp, and (often) the GPS coordinates of where the photo was taken. Extracting it is trivial. The interesting question is what you do with the data.

Quick check, no install

macOS Preview: Tools → Show Inspector → the (i) info icon → GPS tab. Windows: right-click → Properties → Details. Both surface the location if it's there, and let you remove it.

ExifTool: the canonical command-line tool

exiftool photo.jpg dumps every EXIF, IPTC, XMP, and maker-note tag. Useful flags:

  • exiftool -GPSLatitude -GPSLongitude photo.jpg — just GPS
  • exiftool -all= photo.jpg — strip all metadata in place (with -overwrite_original to skip the backup file)
  • exiftool -csv -r folder/ — dump every photo in a folder to a single CSV

Python: Pillow + piexif, or exifread

from PIL import Image from PIL.ExifTags import TAGS, GPSTAGS img = Image.open("photo.jpg") exif = img._getexif() for tag_id, value in exif.items(): tag = TAGS.get(tag_id, tag_id) print(tag, value)

GPS coordinates come back as a tuple of (degrees, minutes, seconds) — convert to decimal with d + m/60 + s/3600 and apply the hemisphere sign from GPSLatitudeRef / GPSLongitudeRef. piexif and the newer pillow-heif handle HEIC/HEIF from iPhones.

Going from coordinates to a place

Once you have lat/lon, reverse-geocode with the Nominatim API (OpenStreetMap, free) or Google Maps Geocoding (paid, more accurate for addresses). Don't try to write your own — the edge cases around country borders and disputed regions get ugly fast.

Batch: a folder of photos to one CSV

exiftool -csv -r -GPSLatitude -GPSLongitude -DateTimeOriginal -Model folder/ > photos.csv writes one row per file with only the columns you asked for. Without that tag list you get several hundred columns, most of them empty, which is unusable in a spreadsheet. -r walks subfolders, -ext jpg -ext heic limits it to the file types you care about, and -n prints GPS as signed decimal degrees instead of degrees-minutes-seconds strings — which is what you want if the CSV is going into a map or a database.

The same job in Python: walk pathlib.Path("folder").rglob("*.jpg"), read EXIF per file, and skip the ones that raise. A folder of real photos always contains a few files with no EXIF block at all, and one unhandled exception halfway through a 20,000-file run is a wasted afternoon.

HEIC, RAW, and the formats Pillow won't open

iPhones shoot HEIC by default and Pillow cannot read it without the pillow-heif plugin (pip install pillow-heif, then register_heif_opener()). RAW files carry richer metadata than their JPEG exports — lens serial, shutter count, per-shot white balance — and Pillow reads almost none of it. For anything that isn't a plain JPEG or PNG, ExifTool is the tool that actually knows the format. Treat Pillow as the convenience path for the common case, not the general solution.

Three clocks that disagree

A photo can carry DateTimeOriginal (when the shutter fired), CreateDate, ModifyDate (when the file was last written), and a GPS timestamp in UTC. They routinely disagree: an edit rewrites ModifyDate, a transfer rewrites the filesystem date, and older phones wrote local time with no offset, so the same photo reads an hour differently depending on which tag you trust. For anything that gets sorted chronologically, key on DateTimeOriginal and fall back to the GPS timestamp — it is the only one recorded in UTC.

What you can extract from a phone photo

  • GPS latitude and longitude (often), altitude, GPS timestamp
  • Camera make and model, lens
  • Date/time taken (and the time zone in newer iPhone shots)
  • Exposure: aperture, shutter speed, ISO, focal length
  • Orientation, software used ("Photos 1.0" tells you it was edited on iOS Photos)
  • Sometimes: device serial number, owner name, even Wi-Fi SSID on jailbroken phones

Privacy — both directions

If you're posting photos publicly, strip EXIF first: most social platforms do this automatically, most blog uploads don't. exiftool -all= photo.jpg or any image editor's "export without metadata" works. The risk isn't theoretical — there are well-documented cases of GPS-tagged photos revealing home addresses of people who thought they were anonymous.

If you're extracting from photos you didn't take, treat GPS metadata like personally identifying data — because that's what it is.

Edge cases

  • Screenshots have no EXIF — they're rendered, not captured.
  • Most messaging apps strip EXIF on send (WhatsApp, iMessage). Email attachments usually don't.
  • RAW files (CR2, NEF, ARW) have richer EXIF than the JPEG export. Read with exiftool, not Pillow.
  • There's no "IP address" in standard EXIF — that's a myth. The Wi-Fi SSID claim is real on some platforms; the IP one isn't.

Related reading

Stop reading, start extracting

Drop a PDF or image into ExtractFox and get structured data back in seconds.

Try a free extraction →