Strip GPS location from photos with one API call
Phone photos store GPS coordinates in their EXIF block. Our image optimize call removes that block by default. Here is the request and a check for each output.
A phone photo can show where it was taken. Our image optimize call removes that location by default, so the file you publish does not carry it. Keep strip_metadata on, and check each output before you store it or send it on.
A site visit photo, a listing photo or a damage report often leaves your system as a file. The coordinates are not visible in the picture. They sit in metadata that most image viewers hide, and anyone who downloads the original can read them with a free tool.
This guide covers what the location data is, the request that removes it, the orientation setting that must run first, and a short check you can run on every output.
What the location data is
A camera writes metadata into the file next to the pixels. The EXIF block holds the camera model, the capture time, the orientation and, when location is on, a GPS block with the latitude and longitude. The GPS block sits inside the EXIF data, so removing the EXIF data removes the coordinates too. The ExifTool list of GPS tags names each field, and the Exif standard, CIPA DC-008 defines the format.
Metadata also lives in XMP and IPTC blocks, and those can carry text about a place. By default the output keeps none of the input's metadata blocks. The sharp documentation describes the same default for the library our pipeline uses.
The request
curl https://api.justapi.tech/v1/image/optimize \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 3b9f0c4e-6a1d-4f7e-9b2a-8d5c1e7f0a42" \
-d '{
"source": { "url": "https://files.example.com/site-photo.jpg" },
"options": { "width": 1600, "format": "jpeg", "strip_metadata": true, "auto_orient": true }
}'
The response is a 200 with a result object. It holds a signed url, the bytes, the content_type, and the width and height of the output. The link expires, so copy the file into your own storage. The file retention guide gives the windows for each plan.
Two options decide what the output keeps:
strip_metadata: trueis the default. It removes the metadata blocks from the output. Set it tofalseonly for a private archive copy that you never publish, because that keeps the GPS block and every other tag.auto_orient: trueis also the default, and it must run before the metadata is removed. Phones record the rotation of a photo in the EXIF orientation tag, not in the pixels. The service turns the pixels first and then removes the tag. If it removed the tag first, a landscape photo would come out sideways. The image pipeline tests check this case: the output is upright and has no orientation tag.
Check each output
This client sends the request, downloads the output and refuses any file that still has GPS fields. It needs pip install requests pillow.
import io
import os
import requests
from PIL import Image
API = "https://api.justapi.tech"
GPS_POINTER = 0x8825 # the EXIF tag that points to the GPS block
def gps_fields(data: bytes) -> dict:
"""Return the GPS fields in an image, or an empty dict when there are none."""
with Image.open(io.BytesIO(data)) as img:
return dict(img.getexif().get_ifd(GPS_POINTER))
def optimize_without_location(source_url: str, idempotency_key: str) -> bytes:
res = requests.post(
f"{API}/v1/image/optimize",
headers={
"Authorization": f"Bearer {os.environ['JUSTAPI_KEY']}",
"Content-Type": "application/json",
"Idempotency-Key": idempotency_key,
},
json={
"source": {"url": source_url},
"options": {"width": 1600, "format": "jpeg", "strip_metadata": True, "auto_orient": True},
},
timeout=120,
)
res.raise_for_status()
out = requests.get(res.json()["result"]["url"], timeout=60) # signed link, no key needed
out.raise_for_status()
if gps_fields(out.content):
raise RuntimeError("GPS fields are still in the output")
return out.content
The check reads the output bytes, not a file on disk, so it tests what you would send. We ran this client against a local stub that serves one photo with coordinates and one without. The check refused the first and passed the second. We did not call the live service for this article, so run the request once with a real key and a test photo before you rely on the client.
The check covers the GPS block. A stricter check also reads the XMP and IPTC blocks, which the same library can open.
What stripping does not remove
- Anything visible in the frame. A street sign, a house number or a map on a screen stays in the picture. Removing metadata does not blur pixels.
- The source you uploaded. When the source is a file you sent with a
file_id, the service deletes it after processing, unless you setkeep_source: true. A file you send by URL stays where it is on your server. - Your own copies. The original on your side is yours to delete, and your retention rule decides when.
Before you publish
- Keep
strip_metadataandauto_orienton. - Send an
Idempotency-Keywith each photo, so a retry does not process it again or charge twice. The idempotency guide shows how to create the key. - Run the check on every output, and stop when it fails.
- Store the output in your storage. The signed link expires.
Next step
Put the check in the same step that stores the file, so a failed check stops the upload. If your photos come from iPhones in HEIC format, read the HEIC to JPEG guide first. It covers orientation and colour checks for that input.