Search
← Back to Blog

API integration guide: adding geolocation to your application

A developer walkthrough for integrating GeoPin's geolocation API, with code examples in Python, JavaScript and curl.

API integration guide: adding geolocation to your application

GeoPin provides a straightforward REST API that lets you submit a photo and receive a predicted location within the Netherlands. Whether you are building an internal verification tool, enriching a content management pipeline, or adding location awareness to a mobile app, integration takes just a few minutes. This guide covers authentication, making your first request, handling responses and best practices for production use.

Getting your API key

Every request to the GeoPin API requires an API key passed in the Authorization header. You can generate a key from your dashboard at geopin.nl/dashboard after creating an account. Free accounts receive 100 requests per month. Paid plans offer higher limits and priority processing.

Keep your API key secret. Never include it in client-side JavaScript shipped to end users. If you suspect a key has been compromised, revoke it immediately from the dashboard and generate a new one.

Making your first request

The core endpoint is POST /api/v1/geolocate. You send an image, and GeoPin returns a ranked list of predicted locations.

curl

curl -X POST https://api.geopin.nl/api/v1/geolocate \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -F "image=@photo.svg"

Python

import requests

API_KEY = "YOUR_API_KEY"
URL = "https://api.geopin.nl/api/v1/geolocate"

with open("photo.svg", "rb") as f:
    response = requests.post(
        URL,
        headers={"Authorization": f"Bearer {API_KEY}"},
        files={"image": ("photo.svg", f, "image/jpeg")},
    )

data = response.json()
print(f"Top prediction: {data['results'][0]['latitude']}, "
      f"{data['results'][0]['longitude']}")
print(f"Confidence: {data['results'][0]['confidence']}")

JavaScript (Node.js)

import fs from "fs";
import FormData from "form-data";
import fetch from "node-fetch";

const API_KEY = "YOUR_API_KEY";
const URL = "https://api.geopin.nl/api/v1/geolocate";

const form = new FormData();
form.append("image", fs.createReadStream("photo.svg"));

const response = await fetch(URL, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${API_KEY}`,
    ...form.getHeaders(),
  },
  body: form,
});

const data = await response.json();
console.log("Top prediction:", data.results[0]);

Understanding the response

A successful response returns JSON with the following structure:

{
  "request_id": "req_8f3a2b1c",
  "status": "success",
  "processing_time_ms": 1423,
  "results": [
    {
      "rank": 1,
      "latitude": 52.3676,
      "longitude": 4.9041,
      "confidence": 0.92,
      "radius_m": 150,
      "region": "Amsterdam",
      "province": "Noord-Holland"
    },
    {
      "rank": 2,
      "latitude": 52.3712,
      "longitude": 4.8952,
      "confidence": 0.78,
      "radius_m": 300,
      "region": "Amsterdam",
      "province": "Noord-Holland"
    }
  ]
}

Key fields to note:

  • confidence is a float between 0 and 1. Values above 0.85 generally indicate high-confidence predictions. Values below 0.5 suggest the model is uncertain and you should treat the result with caution.
  • radius_m is the estimated accuracy radius in metres. A smaller radius means the model is more spatially precise.
  • results always contains up to five ranked predictions. Returning multiple candidates allows you to apply your own re-ranking logic if your application has additional context, such as a known city or time zone.

Error handling

The API uses standard HTTP status codes. Here are the ones you should explicitly handle:

StatusMeaningAction
200SuccessProcess the results
400Bad request (missing image, unsupported format)Check your request payload
401Invalid or missing API keyVerify your key
413Image exceeds 20 MB limitResize before uploading
429Rate limit exceededWait and retry
500Internal server errorRetry with exponential backoff

For 429 responses, the Retry-After header indicates how many seconds to wait before retrying. A simple retry strategy in Python looks like this:

import time

def geolocate_with_retry(image_path, max_retries=3):
    for attempt in range(max_retries):
        response = requests.post(
            URL,
            headers={"Authorization": f"Bearer {API_KEY}"},
            files={"image": open(image_path, "rb")},
        )
        if response.status_code == 200:
            return response.json()
        if response.status_code == 429:
            wait = int(response.headers.get("Retry-After", 5))
            time.sleep(wait)
            continue
        response.raise_for_status()
    raise Exception("Max retries exceeded")

Batch processing

If you need to geolocate many images — for example, processing a backlog of archival photos — use the batch endpoint POST /api/v1/geolocate/batch. This accepts up to 50 images in a single request and processes them in parallel on our GPU cluster.

files = [
    ("images", (f"photo_{i}.svg", open(f"photo_{i}.jpg", "rb"), "image/jpeg"))
    for i in range(50)
]

response = requests.post(
    "https://api.geopin.nl/api/v1/geolocate/batch",
    headers={"Authorization": f"Bearer {API_KEY}"},
    files=files,
)

for result in response.json()["results"]:
    print(f"{result['filename']}: {result['latitude']}, {result['longitude']}")

Batch requests are billed as one request per image but benefit from reduced overhead and faster total processing time compared to sequential individual calls.

Best practices

Image quality matters. Higher-resolution images with visible landmarks, street signs or distinctive architecture yield better results. Cropped close-ups of faces or generic interior scenes will produce low-confidence results because they lack geographic context.

Strip EXIF data if privacy matters. GeoPin does not read or retain EXIF GPS tags from uploaded images. Our geolocation is purely visual. If your workflow involves sensitive images, strip metadata before uploading as an additional security layer.

Cache results on your end. If you are geolocating the same image more than once, cache the result locally rather than re-submitting. This saves both API quota and processing time.

Use confidence thresholds. Not every image can be reliably geolocated. Build your application logic to handle low-confidence responses gracefully. For example, you might display the result with a disclaimer when confidence is between 0.5 and 0.85, and suppress the result entirely below 0.5.

Use HTTPS only. All API endpoints require TLS. Requests over plain HTTP are rejected. This ensures your API key and image data are encrypted in transit.

Rate limits and pricing

PlanMonthly requestsPrice
Trial3Free
Starter100EUR 19
Pro500EUR 99
Business2,000EUR 149

All plans include access to the batch endpoint, webhook notifications and full API documentation. Enterprise plans additionally provide dedicated support, custom SLAs and on-premises deployment options.

What is coming next

We are actively developing a WebSocket endpoint for real-time video geolocation and a client SDK for Python and TypeScript that wraps the REST API with built-in retries, connection pooling and type-safe response objects. Follow our changelog at geopin.nl/changelog to stay up to date.

If you run into issues during integration, reach out at support@geopin.nl or open a discussion in our developer forum. We typically respond within a few hours on business days.