Skip to content
onde

Find the city at a position

Reverse lookup returns the city at a latitude and longitude, for one position or for a batch. It prefers a large city or a capital to a small neighbor.

Use reverse lookup when you have a position and need the city: the position of a phone, the GPS data of a photo, or a column of coordinates in your database. City API does not store the positions that you send.

Fill in the city from the device position

A form can suggest the city of the person from the position of the device. The browser asks the person for permission first, and works only on a page with HTTPS or on localhost.

JavaScript
const REVERSE_URL = 'https://api.onde.earth/v1/cities/reverse'

navigator.geolocation.getCurrentPosition(async ({ coords }) => {
  const url = new URL(REVERSE_URL)
  url.searchParams.set('latitude', coords.latitude)
  url.searchParams.set('longitude', coords.longitude)
  url.searchParams.set('key', PUBLIC_KEY)
  const response = await fetch(url)
  const body = await response.json()
  if (response.ok && body.data !== null) {
    selectCity(body.data)
  }
})

PUBLIC_KEY and selectCity come from Add city autocomplete to a form. Let the person change the city, because the position of a device can be wrong by some kilometers.

Send one position

Send the latitude and the longitude to Reverse lookup.

Shell
curl "https://api.onde.earth/v1/cities/reverse?latitude=35.02&longitude=135.75" \
  -H "Authorization: Bearer $ONDE_KEY"

data is a city with the extra field distanceKm, the distance from the position to the city in kilometers. If no city is near the position, data is null, for example in the ocean. This response has status 200, so it costs one unit.

Which city reverse lookup returns

Reverse lookup returns the city that people use as the name of the place, and not always the nearest city:

  • It prefers a large city to a small neighbor municipality.
  • It prefers a capital or the seat of a region within 6 km of the position.
  • It returns a city and never a district. For a position in a district, it returns the city of the district.
  • It prefers a city in the country that contains the position.

Near a border, the country can be wrong. timeZone is the time zone of the city. Near the border of a time zone, this time zone can differ from the time zone at the position.

Add cities to many positions

To add a city to each row of a table, such as a table of photos or of check-ins, send up to 100 positions in one request to Batch reverse lookup. data has the result of each position, at the index of the position. The result of a position with no city near it is null.

Shell
curl "https://api.onde.earth/v1/cities/reverse/batch" \
  -H "Authorization: Bearer $ONDE_KEY" \
  -H "Content-Type: application/json" \
  -d '{"positions":[{"latitude":35.02,"longitude":135.75},{"latitude":38.72,"longitude":-9.14}]}'

A batch costs one unit for each position, and counts as one request for the limit of requests per second. The API refuses the full batch in these conditions:

  • One position is not valid. The latitude must be from -90 to 90, and the longitude from -180 to 180. The error is invalid_request.
  • The batch has more positions than the units left in the month. The error is quota_exceeded, and the detail gives the units left.

Check each position before you send the batch. This function sends a long list of positions in batches of 100.

JavaScript
const BATCH_URL = 'https://api.onde.earth/v1/cities/reverse/batch'
const MAX_BATCH_POSITIONS = 100

async function reverseAll(positions) {
  const cities = []
  for (let start = 0; start < positions.length; start += MAX_BATCH_POSITIONS) {
    const response = await fetch(BATCH_URL, {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${process.env.ONDE_KEY}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        positions: positions.slice(start, start + MAX_BATCH_POSITIONS)
      })
    })
    const body = await response.json()
    if (!response.ok) {
      throw new Error(body.detail)
    }
    cities.push(...body.data)
  }
  return cities
}

cities[i] is the city of positions[i], or null.