# EXIF & GPS Cleaner — Read and Remove Photo Metadata (Lossless) (`adorable_partial/exif-gps-cleaner`) Actor

See what your photos reveal (GPS location, phone model, serial number, author, date) and get clean copies with all hidden metadata removed, without recompressing: pixels stay identical. JPEG, PNG, WebP. Bulk API.

- **URL**: https://apify.com/adorable_partial/exif-gps-cleaner.md
- **Developed by:** [Leandro Zanatta](https://apify.com/adorable_partial) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.70 / 1,000 image processeds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## EXIF & GPS Cleaner — Read and Remove Photo Metadata (Lossless)

See what your photos reveal before you publish them, and remove it. For every image this Actor reports the **hidden metadata** (GPS location with a map link, phone or camera model, lens, serial numbers, author, date and time, editing software) with a clear list of **privacy risks**, and returns a **clean copy** with all of it removed, **without recompressing**: the pixels stay exactly the same. Works in bulk on **JPEG, PNG and WebP**.

![A phone photo with its GPS location, phone model, lens, software and date, and the same photo after cleaning with pixels verified identical](https://api.apify.com/v2/key-value-stores/P2GB8FKEd91E9bGXd/records/demo.png?signature=1oNpXvroHYaCUmFJnjGlm)

*Real output of this Actor on a Pixel 6 photo (photo: Mark Yasuda, CC BY-SA 4.0, Wikimedia Commons).*

### Why use it

- 📍 **Phones write your location into photos**: anyone who downloads the original file can see where it was taken, often your home or workplace. This Actor finds and removes it.
- 🖼️ **Truly lossless**: metadata blocks are cut out of the file; the image data is copied byte for byte. Each run can **verify that the decoded pixels are identical**. No quality loss, no size growth.
- 🕵️ **Removes what other tools miss**: EXIF (including the embedded thumbnail), XMP, IPTC, comments, **data appended after the image** such as motion-photo videos, and extra embedded images.
- 🧭 **Photos stay upright and true to color**: only the orientation tag and the color profile are kept by default (both contain no personal data).
- 📊 **Audit before you clean**: report-only mode tells you which images in a set leak GPS, serial numbers or author names.
- 🔒 **Runs inside the Actor**: images are not sent to any third-party service.

### Who uses it

| Sector | Typical use |
|---|---|
| **Marketplaces & classifieds** | Strip seller locations from listing photos before they go live |
| **Real estate** | Publish property photos without the agent's or owner's device data |
| **Journalism & NGOs** | Protect sources and staff when publishing field photos |
| **Companies under LGPD / GDPR** | Remove personal data from images on websites, reports and shared drives |
| **Apps & platforms with user uploads** | Clean every uploaded photo through the API before storing it |
| **Investigations & OSINT** | Read dates, devices and locations from photos you are allowed to analyse |
| **Photographers & agencies** | Audit and remove serial numbers and GPS before delivering files |

### What it reports

| Field | Meaning |
|---|---|
| `privacyRisks` | Plain list: GPS location, device serial number, author or owner name, date and time taken, camera / phone model, tagged people / face regions |
| `gps` | Latitude, longitude, altitude and a Google Maps link |
| `camera` | Make, model, lens and software |
| `dateTaken` | Original date and time |
| `serialNumbers` | Body / lens serial numbers, when present |
| `authorInfo` | Artist, copyright, owner, XMP creator, IPTC by-line, XMP city / location |
| `hasExif`, `hasXmp`, `hasIptc`, `hasIccProfile` | Which metadata blocks the file contains |
| `allTags` | Every EXIF, GPS and IPTC tag (optional) |

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| **Image URLs** (`imageUrls`) | array | — | Photos to process (JPEG, PNG, WebP; reading also works for TIFF and others). Up to 100 MB each. Google Drive and Dropbox links work |
| **What to do** (`mode`) | string | `report-and-clean` | `report-and-clean`, `report` or `clean` |
| **Keep orientation** (`keepOrientation`) | boolean | `true` | Keep only the rotation tag so photos are not shown sideways |
| **Keep color profile** (`keepColorProfile`) | boolean | `true` | Keep the ICC profile so colors look the same |
| **Verify pixels are identical** (`verifyPixels`) | boolean | `true` | Decode both files and confirm the image did not change |
| **Include all tags** (`includeAllTags`) | boolean | `false` | Add every raw EXIF, GPS and IPTC tag |

```json
{
  "imageUrls": ["https://example.com/listing-01.jpg", "https://example.com/listing-02.webp"],
  "mode": "report-and-clean"
}
```

### Output

```json
{
  "sourceUrl": "https://example.com/listing-01.jpg",
  "format": "JPEG", "width": 2587, "height": 1455,
  "hasGps": true,
  "gps": { "latitude": 47.369681, "longitude": -122.3108, "altitudeMeters": 98.4, "mapsUrl": "https://www.google.com/maps?q=47.369681,-122.3108" },
  "camera": { "make": "Google", "model": "Pixel 6", "lensModel": "Pixel 6 back camera 6.81mm f/1.85", "software": "HDR+ 1.0.414775603zd" },
  "dateTaken": "2022:01:01 09:27:56",
  "privacyRisks": ["GPS location", "date and time taken", "camera / phone model"],
  "cleanImageUrl": "https://api.apify.com/v2/key-value-stores/.../records/clean-00001.jpg",
  "originalBytes": 377989, "cleanBytes": 292298, "metadataBytesRemoved": 85691,
  "remainingRisks": [],
  "pixelsIdentical": true
}
```

Clean copies are stored in the run's key-value store and linked in `cleanImageUrl`. Failed images return an `error` field and are free.

### Pricing

Pay per **image processed** (report and clean copy included), plus a tiny per-run start fee. No subscription. Apify plan discounts apply automatically (see the **Pricing** tab).

| Example | Images billed |
|---|---|
| 1 photo | 1 |
| 500 marketplace listing photos | 500 |
| 10,000 user uploads per month | 10,000 |

### Use it from code or an AI agent

**Python: audit a folder of public URLs, then use the clean copies**

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("adorable_partial/exif-gps-cleaner").call(run_input={
    "imageUrls": ["https://example.com/a.jpg", "https://example.com/b.jpg"],
})
for img in client.dataset(run["defaultDatasetId"]).iterate_items():
    if img.get("hasGps"):
        print("Leaked location:", img["gps"]["mapsUrl"])
    print("Clean copy:", img.get("cleanImageUrl"), "identical pixels:", img.get("pixelsIdentical"))
```

**JavaScript**

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('adorable_partial/exif-gps-cleaner').call({
    imageUrls: ['https://example.com/upload.jpg'],
    mode: 'clean',
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items[0].cleanImageUrl);
```

**HTTP**

```bash
curl -X POST "https://api.apify.com/v2/acts/adorable_partial~exif-gps-cleaner/run-sync-get-dataset-items?token=<YOUR_APIFY_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"imageUrls": ["https://example.com/photo.jpg"], "mode": "report"}'
```

**No-code and agents:** clean every new upload with **Make**, **Zapier** or **n8n**, trigger runs from **webhooks**, or let an AI agent check photos through the **Apify MCP server**.

### FAQ

**Does cleaning reduce quality?** No. The image data is copied unchanged; only metadata blocks are removed. With *Verify pixels* on, every row confirms it with `pixelsIdentical`.

**Why is the clean file smaller?** Phones store a lot besides the picture: a preview thumbnail, XMP, sometimes an HDR gain map or a short motion-photo video. All of it is removed.

**Will my photos appear rotated?** No, the orientation tag is kept by default. Turn *Keep orientation* off only if you rotate images yourself.

**Which formats?** Cleaning: JPEG, PNG and WebP. Reading: also TIFF and most formats Pillow opens. HEIC/HEIF is not supported yet; convert to JPEG first.

**Does it remove faces or license plates?** No, that is visual content. Use the anonymizer below for that, and this Actor for the hidden data.

**Is it legal to read other people's photo metadata?** Reading metadata of images you are allowed to process is common practice (e.g. moderation, journalism, investigations). Respect privacy laws when you use the locations you find.

### Related Actors

- [Image & Video Anonymizer](https://apify.com/adorable_partial/image-anonymizer) — blur faces and license plates in the same photos
- [PII Masker](https://apify.com/adorable_partial/pii-masker) — mask CPF, e-mails, phones and names in texts and datasets
- [Face Compare](https://apify.com/adorable_partial/face-compare) — face verification and embeddings
- [Video Optimizer](https://apify.com/adorable_partial/video-optimizer) — removes metadata from videos while compressing

### Support

A file that was not fully cleaned? Open an issue in the **Issues** tab with a sample (without personal data).

### Need a different tool? Request it

Missing a feature, an input or output format, or a whole different tool for your workflow? **Open an issue in the Issues tab** and describe what you need:

- what goes in (files, URLs, data) and what should come out,
- roughly how much you process (per day or month),
- one example, if you can share it.

Requests with a real use case get built, either as a new option in this Actor or as a new Actor in [this collection](https://apify.com/adorable_partial), and you get notified in the issue when it's live. Every request is read.

# Actor input Schema

## `imageUrls` (type: `array`):

Photos to check and clean (JPEG, PNG, WebP; reading also works for TIFF and others). Up to 100 MB each. Public Google Drive and Dropbox links work.

## `mode` (type: `string`):

Report = what each photo reveals. Clean = a copy without hidden metadata, pixels untouched.

## `keepOrientation` (type: `boolean`):

Keep only the rotation tag so photos are not displayed sideways. Contains no personal data.

## `keepColorProfile` (type: `boolean`):

Keep the color profile so colors look the same. Contains no personal data.

## `verifyPixels` (type: `boolean`):

Decode original and clean copy and confirm the image itself did not change.

## `includeAllTags` (type: `boolean`):

Add every EXIF, GPS and IPTC tag found (raw), in addition to the summary.

## Actor input object example

```json
{
  "imageUrls": [
    "https://upload.wikimedia.org/wikipedia/commons/9/9d/2022_Maury_Island.jpg",
    "https://upload.wikimedia.org/wikipedia/commons/d/db/2021_Seward_Park_Torii_Back.jpg"
  ],
  "mode": "report-and-clean",
  "keepOrientation": true,
  "keepColorProfile": true,
  "verifyPixels": true,
  "includeAllTags": false
}
```

# Actor output Schema

## `results` (type: `string`):

One row per photo: privacy risks, GPS, camera, date and the clean copy link.

## `files` (type: `string`):

Copies without metadata.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "imageUrls": [
        "https://upload.wikimedia.org/wikipedia/commons/9/9d/2022_Maury_Island.jpg",
        "https://upload.wikimedia.org/wikipedia/commons/d/db/2021_Seward_Park_Torii_Back.jpg"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("adorable_partial/exif-gps-cleaner").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "imageUrls": [
        "https://upload.wikimedia.org/wikipedia/commons/9/9d/2022_Maury_Island.jpg",
        "https://upload.wikimedia.org/wikipedia/commons/d/db/2021_Seward_Park_Torii_Back.jpg",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("adorable_partial/exif-gps-cleaner").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "imageUrls": [
    "https://upload.wikimedia.org/wikipedia/commons/9/9d/2022_Maury_Island.jpg",
    "https://upload.wikimedia.org/wikipedia/commons/d/db/2021_Seward_Park_Torii_Back.jpg"
  ]
}' |
apify call adorable_partial/exif-gps-cleaner --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,adorable_partial/exif-gps-cleaner"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/jbl4yKZUKWq5aw0V2/builds/QEI68FhR4GAy0XPDg/openapi.json
