# Image Metadata & EXIF Extractor - Camera, GPS, Size (`eliai/image-metadata-exif-extractor`) Actor

Read any image URL and get dimensions, format, camera make/model, lens, ISO, shutter, aperture, focal length, capture date and GPS coordinates with a map link. Flags photos that still carry their location. Bulk up to 50. $0.003 per image; non-images are free.

- **URL**: https://apify.com/eliai/image-metadata-exif-extractor.md
- **Developed by:** [Broke to Built](https://apify.com/eliai) (community)
- **Categories:** Developer tools, Automation, Other
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$3.00 / 1,000 analyzed images

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Image Metadata & EXIF Extractor — Camera, GPS, Size

Point it at an image URL and get back everything the file knows about itself: **dimensions and
format**, the **camera and lens** that took it, **ISO / shutter / aperture / focal length**, the
**capture date**, and the **GPS coordinates** if they are still embedded.

No API key, no upload step, no account. **$0.003 per image analysed** — anything that is not a
readable image is recorded and **not charged**.

### The one people actually need: does this photo leak where it was taken?

Phone and camera photos carry GPS in EXIF, and most tools silently pass it through. Every record
has a plain `hasGps` boolean, and when it is `true` you also get the coordinates and an
`openstreetmap.org` link so you can see the location rather than decode a decimal.

Run it over a folder of images before they go on a website, a listing, or a profile.

### Input

```json
{ "url": "https://example.com/photo.jpg" }
```

Bulk:

```json
{ "urls": ["https://example.com/a.jpg", "https://example.com/b.png"], "maxImages": 50 }
```

| Field | Type | Notes |
|---|---|---|
| `url` | string | One direct image link. |
| `urls` | string\[] | Up to 50 per run. |
| `maxImages` | integer | 1–50, default 25. |
| `includeRawExif` | boolean | Adds every tag in the file, not just the common ones. Verbose; default false. |

Supported: **JPEG, PNG, WebP, GIF, TIFF, HEIC, AVIF, BMP** and other common containers. EXIF itself
exists mainly in JPEG/TIFF/HEIC.

### Output

```json
{
  "ok": true,
  "url": "https://example.com/photo.jpg",
  "format": "jpg",
  "width": 640,
  "height": 480,
  "megapixels": 0.31,
  "aspectRatio": "4:3",
  "bytes": 161713,
  "orientation": "Horizontal (normal)",
  "hasExif": true,
  "hasGps": true,
  "camera":  { "make": "NIKON", "model": "COOLPIX P6000", "lens": null, "software": "Nikon Transfer 1.1 W" },
  "capture": { "dateTaken": "2008-10-22T16:28:39.000Z", "iso": 64, "fNumber": 5.9,
               "exposureSeconds": 0.0133, "shutter": "1/75", "focalLengthMm": 24,
               "flash": "Flash did not fire, compulsory flash mode" },
  "gps": { "latitude": 43.4674, "longitude": 11.8851,
           "mapUrl": "https://www.openstreetmap.org/?mlat=43.4674&mlon=11.8851#map=15/43.4674/11.8851" }
}
```

### What it gets right

- **Dimensions come from the container, not from EXIF.** Most web images have no EXIF at all — a
  PNG from a design tool, anything a social platform re-encoded. Those still return width, height,
  format and size, with `hasExif: false`. "No EXIF" is a true answer, not an error.
- **Shutter speed is given both ways.** EXIF stores `0.0133`; every camera displays `1/75`. You get
  `exposureSeconds` and `shutter`.
- **Aspect ratio stays readable.** 640×480 reduces to `4:3`. An odd crop that would reduce to
  `3871:2579` is returned as `1.5:1` instead, because the exact fraction tells you nothing.
- **`orientation` is EXIF's own label** (`"Horizontal (normal)"`), not a number of degrees — the
  field is named so it cannot be mistaken for one.
- **A non-image is honest about it.** An HTML error page served at a `.jpg` URL produces
  `ok: false` with a reason, and is not charged.

### Pricing

**$0.003 per image analysed.** No start fee. Failed fetches and non-images are recorded free.

### Limits

- 50 images per run, 20 MB per image, 25 s fetch timeout each.
- Reads metadata only. It does not resize, convert, or strip EXIF — to convert formats see
  [Image Converter](https://apify.com/eliai/image-converter).
- GPS is only present if the camera recorded it and nothing has stripped it since. Absence is not
  proof the photo has no location history.

### FAQ

**Do I need an API key?** No.

**Can it strip the GPS data it finds?** No — this one reports. Stripping changes the file, which is
a different job.

**Are my images stored?** The bytes are fetched, parsed in memory, and discarded. Only the metadata
record goes to your own run's dataset.

**Why does a photo from Instagram/Twitter show no EXIF?** Those platforms re-encode uploads and drop
metadata. That is the platform, not this Actor.

### Changelog

- **0.1 (2026-09-10)** — first release. Dimensions, format, camera, lens, exposure, capture date,
  GPS with map link, optional full raw EXIF block.

### Who uses it

- Privacy reviewers checking whether user-uploaded photos still leak GPS coordinates
- Journalists and OSINT researchers reading capture date and camera model from images in a story
- Photographers auditing which lens and settings were used across a portfolio
- Marketplace moderators checking listing photos for capture dates that do not match the claim
- Developers reading image dimensions and format before deciding how to resize

# Actor input Schema

## `url` (type: `string`):

A direct link to one image (JPEG, PNG, WebP, GIF, TIFF, HEIC, AVIF, BMP).

## `urls` (type: `array`):

Up to 50 image links in one run.

## `maxImages` (type: `integer`):

Cap on how many images this run processes.

## `includeRawExif` (type: `boolean`):

Adds every tag the file carries, not just the common ones. Verbose.

## Actor input object example

```json
{
  "url": "https://raw.githubusercontent.com/ianare/exif-samples/master/jpg/gps/DSCN0010.jpg",
  "urls": [],
  "maxImages": 25,
  "includeRawExif": false
}
```

# Actor output Schema

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

Every item this run produced, as JSON.

## `resultsCsv` (type: `string`):

The same items as a spreadsheet-ready CSV.

# 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 = {
    "url": "https://raw.githubusercontent.com/ianare/exif-samples/master/jpg/gps/DSCN0010.jpg",
    "urls": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("eliai/image-metadata-exif-extractor").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 = {
    "url": "https://raw.githubusercontent.com/ianare/exif-samples/master/jpg/gps/DSCN0010.jpg",
    "urls": [],
}

# Run the Actor and wait for it to finish
run = client.actor("eliai/image-metadata-exif-extractor").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 '{
  "url": "https://raw.githubusercontent.com/ianare/exif-samples/master/jpg/gps/DSCN0010.jpg",
  "urls": []
}' |
apify call eliai/image-metadata-exif-extractor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,eliai/image-metadata-exif-extractor"
        }
    }
}
```

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/hRrItwZyL1mE30TAq/builds/JVzdZE22GkUYWUA8t/openapi.json
