SDKs

The API is plain HTTPS and JSON, so it already works from any language. We are also building an official library for each of the languages below. Each one is deliberately small: a typed client for the eight endpoints, timeouts, retries on 429 and 503, and debouncing for autocomplete. No framework, no plugins.

Status by language

JavaScript and TypeScript In progress

npm install @mygeocode/sdk

Works in Node 18+, Deno, Bun and browsers. Ships with types.

Python In progress

pip install mygeocode

Python 3.9+. Sync and async clients, no dependencies beyond httpx.

PHP Planned

composer require mygeocode/sdk

PHP 8.1+. PSR-18 client, bring your own HTTP library.

Go Planned

go get github.com/mygeocode/mygeocode-go

Standard library only. Context aware.

Java Planned

com.mygeocode:sdk

Java 11+. Built on java.net.http, also usable from Kotlin.

C# Planned

dotnet add package MyGeocode

.NET 6+. Async throughout, System.Text.Json.

Ruby Planned

gem install mygeocode

Ruby 3.0+. Net::HTTP, no runtime dependencies.

Rust Planned

cargo add mygeocode

reqwest and serde, async by default.

The install commands above are reserved and will work the day each library is released. Want to know when? Send a blank email to [email protected] and we will reply once, when there is something to install.

What every SDK will do

One method per endpoint

client.forward("..."), client.reverse(lat, lon), client.ipv4("8.8.8.8"), and so on. Return values are typed records that mirror the JSON exactly, with the same field names.

Retries that respect the headers

A 429 with a reset time is retried after that time, up to a limit you set. A 503 is retried with backoff. Everything else is raised as an error with the API's code and message.

Key handling

Pass a key to the constructor or set MYGEOCODE_KEY in the environment. Leave both out and the client uses the free tier. Nothing is written to disk.

Use the HTTP API today

Here is a reverse geocode with error handling and a retry on 429, in the languages the first SDKs will cover. This is roughly what the libraries will do internally.

const BASE = "https://api.mygeocode.com/v1";

async function reverse(lat, lon, { key = process.env.MYGEOCODE_KEY, retries = 2 } = {}) {
  const url = new URL(BASE + "/reverse");
  url.searchParams.set("lat", lat);
  url.searchParams.set("lon", lon);

  const headers = key ? { "X-API-Key": key } : {};
  const res = await fetch(url, { headers });

  if (res.status === 429 && retries > 0) {
    const reset = Number(res.headers.get("X-Quota-Reset")) * 1000;
    const wait = Math.min(Math.max(reset - Date.now(), 1000), 60_000);
    await new Promise((r) => setTimeout(r, wait));
    return reverse(lat, lon, { key, retries: retries - 1 });
  }

  const data = await res.json();
  if (data.status !== "ok") throw new Error(`${data.error.code}: ${data.error.message}`);
  return data.result;
}

const place = await reverse(48.8584, 2.2945);
console.log(place.formatted, place.precision);
import os
import time
import requests

BASE = "https://api.mygeocode.com/v1"


class MyGeocodeError(Exception):
    pass


def reverse(lat, lon, key=os.environ.get("MYGEOCODE_KEY"), retries=2):
    headers = {"X-API-Key": key} if key else {}
    r = requests.get(f"{BASE}/reverse", params={"lat": lat, "lon": lon}, headers=headers, timeout=10)

    if r.status_code == 429 and retries > 0:
        reset = int(r.headers.get("X-Quota-Reset", 0))
        time.sleep(min(max(reset - time.time(), 1), 60))
        return reverse(lat, lon, key=key, retries=retries - 1)

    data = r.json()
    if data["status"] != "ok":
        raise MyGeocodeError(f'{data["error"]["code"]}: {data["error"]["message"]}')
    return data["result"]


place = reverse(48.8584, 2.2945)
print(place["formatted"], place["precision"])
<?php
const BASE = "https://api.mygeocode.com/v1";

function reverse(float $lat, float $lon, ?string $key = null, int $retries = 2): array
{
    $key ??= getenv("MYGEOCODE_KEY") ?: null;
    $ch = curl_init(BASE . "/reverse?" . http_build_query(["lat" => $lat, "lon" => $lon]));
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT => 10,
        CURLOPT_HTTPHEADER => $key ? ["X-API-Key: $key"] : [],
    ]);
    $body = curl_exec($ch);
    $status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
    curl_close($ch);

    if ($status === 429 && $retries > 0) {
        sleep(5);
        return reverse($lat, $lon, $key, $retries - 1);
    }

    $data = json_decode($body, true);
    if ($data["status"] !== "ok") {
        throw new RuntimeException($data["error"]["code"] . ": " . $data["error"]["message"]);
    }
    return $data["result"];
}

$place = reverse(48.8584, 2.2945);
echo $place["formatted"], " (", $place["precision"], ")\n";
package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"time"
)

const base = "https://api.mygeocode.com/v1"

type Result struct {
	Formatted string  `json:"formatted"`
	Lat       float64 `json:"lat"`
	Lon       float64 `json:"lon"`
	Precision string  `json:"precision"`
}

type envelope struct {
	Status string  `json:"status"`
	Result *Result `json:"result"`
	Error  *struct {
		Code    string `json:"code"`
		Message string `json:"message"`
	} `json:"error"`
}

func reverse(lat, lon float64, retries int) (*Result, error) {
	q := url.Values{"lat": {strconv.FormatFloat(lat, 'f', -1, 64)}, "lon": {strconv.FormatFloat(lon, 'f', -1, 64)}}
	req, _ := http.NewRequest("GET", base+"/reverse?"+q.Encode(), nil)
	if key := os.Getenv("MYGEOCODE_KEY"); key != "" {
		req.Header.Set("X-API-Key", key)
	}
	resp, err := (&http.Client{Timeout: 10 * time.Second}).Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	if resp.StatusCode == 429 && retries > 0 {
		time.Sleep(5 * time.Second)
		return reverse(lat, lon, retries-1)
	}

	var env envelope
	if err := json.NewDecoder(resp.Body).Decode(&env); err != nil {
		return nil, err
	}
	if env.Status != "ok" {
		return nil, errors.New(env.Error.Code + ": " + env.Error.Message)
	}
	return env.Result, nil
}

func main() {
	place, err := reverse(48.8584, 2.2945, 2)
	if err != nil {
		panic(err)
	}
	fmt.Println(place.Formatted, place.Precision)
}

Using another provider's SDK?

You may not need ours. The official Google Maps, Bing, HERE, Mapbox and ipinfo client libraries mostly accept a custom base URL, and our drop-in hosts answer in those providers' formats. Point the library at the matching mygeocode.com host and keep the code you have.