Geotagger
Docs upload Upload Image

Docs

Writes latitude and longitude into a JPEG and returns the file. No accounts. Use the website, or send the same job from your code with the URLs below.

Limits

  • No cap on how many images you process.
  • Each file, or each image downloaded from a URL, must be 5MB or smaller.
  • Tagging allows 10 POST requests per 60 seconds per IP, shared by both tagging URLs.
  • Inspect allows 20 POST requests per 60 seconds per IP.
  • Naming a location allows 30 POST requests per 60 seconds per IP.
  • Over the limit, the response is HTTP 429 with an error string. HTTP 429 means too many requests. Wait, then retry.
  • GET pages, /docs/, /static/, and /media/ are not limited.
  • Enforced by nginx across workers. Django does not throttle.

Storage

  • If you keep the file, it lasts 30 days, then cleanup removes it.
  • If you send delete_after=true, the image is returned as image_base64 and then deleted. There is no URL.
  • Oldest-first deletion when the disk fills is not implemented.

Output

By default, JPEGs are tagged without re-encoding the pixels. PNG, WebP, HEIC, and GIF convert to JPEG first, and the file you get is a .jpg. Send keep_format=true to keep PNG or WebP in that format. JPEG is unchanged either way. GIF and HEIC cannot keep their format; sending keep_format=true for those returns an error instead of converting. Omit keep_format, or send false, to convert those to JPEG. JPEG GPS is still what most maps and apps read. PNG eXIf support in consumers is uneven.

Tag an image

These two URLs do the same job. Send a POST to either one.

https://geotagger.binaryplane.com/
https://geotagger.binaryplane.com/api/geotag/

Use a multipart request to upload a file. Multipart is a file upload form, the same kind a browser sends when you choose a file. Use JSON to send an image URL as text. JSON here means a text body with a Content-Type of application/json.

Fields

Field What it does
image The file to tag. Send it in a multipart request. Required unless you send url. Do not send both. Maximum 5MB.
url A public image URL. Send it as JSON text, or in a multipart form. Required unless you send image. Do not send both. The downloaded file must be 5MB or smaller.
latitude Required. North-south coordinate from -90 to 90.
longitude Required. East-west coordinate from -180 to 180.
filename Optional. Stored as that name with an extension that matches the output: .jpg by default, or .png or .webp when keep_format keeps those formats.
delete_after Optional. Omit it and the default is false: the file is kept and a URL is returned. The website checkbox defaults to on, so the UI usually sends true. When true, the image is returned as image_base64 and then deleted.
keep_format Optional. Omit it and the default is false, so non-JPEG files convert to JPEG. true keeps PNG or WebP. true on GIF or HEIC returns an error. It does not silently convert.

Example: upload a file

This curl -F command sends a multipart file upload with coordinates.

curl -F "image=@photo.jpg" -F "latitude=40.7128" -F "longitude=-74.006" https://geotagger.binaryplane.com/api/geotag/

Example: pass an image URL

POST this JSON to either tagging URL. Set the request header Content-Type to application/json.

{
  "url": "https://example.com/photo.jpg",
  "latitude": 40.7128,
  "longitude": -74.006
}

Success (keep the file)

HTTP 200 means the request succeeded. geotagged_image is an absolute URL you can fetch. Extra keys can be ignored.

{
  "geotagged_image": "https://geotagger.binaryplane.com/media/photo.jpg",
  "filename": "photo.jpg",
  "content_type": "image/jpeg",
  "coordinates": { "latitude": 40.7128, "longitude": -74.006, "latitude_ref": "N", "longitude_ref": "W" },
  "maps_url": "https://www.google.com/maps/search/?api=1&query=40.712800,-74.006000",
  "previous_coordinates": null,
  "converted_to_jpeg": false,
  "source": "file",
  "deleted_after_processing": false,
  "geotag_count": 12
}

Success (delete after)

HTTP 200 means the request succeeded. geotagged_image is null. The image bytes are in image_base64. content_type is image/jpeg, or image/png or image/webp when keep_format kept those formats.

{
  "geotagged_image": null,
  "image_base64": "<base64 JPEG bytes>",
  "content_type": "image/jpeg",
  "filename": "photo.jpg",
  "deleted_after_processing": true
}

Errors

Failed requests return JSON with an error string. Read that string. HTTP 400 means the request was rejected, which is what the website and every /api/ URL use for bad input. HTTP 200 on POST / can still contain an error body, so Zapier can read it. Rate limits are always HTTP 429.

Inspect

Read GPS that is already on an image. This does not tag the file and nothing is stored. Send a POST to this URL.

https://geotagger.binaryplane.com/api/inspect/

Fields

Field What it does
image The file to read. Send it in a multipart request. Required unless you send url. Do not send both. Maximum 5MB. Do not send latitude or longitude.
url A public image URL. Send it as JSON text, or in a multipart form. Required unless you send image. Do not send both. The downloaded file must be 5MB or smaller.

Response

Field What it does
filename The source filename. Nothing is stored.
source file or url, matching how you sent the image.
format Detected format, such as JPEG, PNG, WEBP, or HEIC.
can_keep_format true for PNG and WebP. false for JPEG, GIF, and HEIC.
coordinates Existing GPS when present, otherwise null.
maps_url A Google Maps link for the coordinates found, otherwise null. Open it to check the location is right.
datetime EXIF datetime when present, otherwise null.
camera Make and model when present, otherwise null.
has_gps true when coordinates were found.

Example: upload a file

curl -F "image=@photo.jpg" https://geotagger.binaryplane.com/api/inspect/

Example: pass an image URL

POST this JSON with Content-Type: application/json.

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

Success

{
  "filename": "photo.jpg",
  "source": "file",
  "format": "JPEG",
  "can_keep_format": false,
  "coordinates": { "latitude": 40.7128, "longitude": -74.006, "latitude_ref": "N", "longitude_ref": "W" },
  "maps_url": "https://www.google.com/maps/search/?api=1&query=40.712800,-74.006000",
  "datetime": "2024:08:14 12:00:00",
  "camera": "Apple iPhone",
  "has_gps": true
}

Bad input returns HTTP 400 with an error string. Over the inspect limit is HTTP 429.

Name a location

Turn coordinates into a place name a person can read, plus a Google Maps link. No image is involved and nothing is stored. Send a POST to this URL.

https://geotagger.binaryplane.com/api/location/

Fields

Field What it does
latitude Required. North-south coordinate from -90 to 90.
longitude Required. East-west coordinate from -180 to 180.

Response

Field What it does
place A short name such as Lahore, Punjab, Pakistan. It is null when the lookup finds nothing or the name service is unreachable. Treat a name as a convenience, not a guarantee.
maps_url A Google Maps link for the coordinates. Always present, because building it needs no lookup.
latitude, longitude The coordinates you sent, echoed back.

Example

{
  "latitude": 31.5204,
  "longitude": 74.3587
}

Success

{
  "latitude": 31.5204,
  "longitude": 74.3587,
  "place": "Lahore, Punjab, Pakistan",
  "maps_url": "https://www.google.com/maps/search/?api=1&query=31.520400,74.358700"
}

Names come from OpenStreetMap Nominatim and are cached for 30 days. Bad input returns HTTP 400 with an error string. Over the limit is HTTP 429.