# Find the city at a position

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`.

```js
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](/docs/city-autocomplete). 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](/docs/api#reverseCity).

```bash
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](/docs/api#batchReverseCities). `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`.

```bash
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.

```js
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`.
