Data
Public data API
A small, free, read-only API for coarse places, weather, land height, the sky and how many people are on the map, with every source credited. Version 1.
The short version
I've opened a small public data API. It is free, needs no key, is read-only and returns JSON. It covers coarse public places, the facts Together holds about them, weather, land height, the sky, a count of how many people are on the map and the list of sources. Every answer says where it came from, under what licence and what credit to show.
The base address is https://to.gether.earth/api/v1/public. The version is in the address: v1 will not change under you, and anything that would break a caller will arrive as v2.
curl -s 'https://to.gether.earth/api/v1/public/places?q=sheffield'
Endpoints
| Endpoint | Returns | Cached for |
|---|---|---|
GET /places?q=&limit= | Places matching a name | 1 day |
GET /places/near?lat=&lon= | The nearest places to a point | 1 day |
GET /places/<key> | One place, with its atlas facts | 1 day |
GET /weather/<key> | The weather there now | 5 minutes |
GET /elevation?lat=&lon=, POST /elevation | Land height in metres | 1 week (POST: never) |
GET /sky/<key> | Local time, daylight, Moon | 30 seconds |
GET /live/summary | How many people are on the map now, by place | 30 seconds |
GET /moments | The last two hours as a quiet, aggregate feed | 30 seconds |
GET /sources | Every dataset, with licence | 1 hour |
Places
A place key looks like city:2638077 (a GeoNames city of 100,000 people or more), settlement:2641783 (a town or village of 500 or more) or country:GB. city-2638077 works too. Find keys with a search. Coordinates are never finer than two decimals, about a kilometre.
| Parameter | Meaning |
|---|---|
q | Search text, 2 to 60 characters. Case and accents are ignored for countries and cities. |
limit | How many to return: 1 to 50 (default 10) for a search, 1 to 25 (default 5) for nearby. |
kind | A comma-separated list of country, city, settlement. Search defaults to country,city; nearby to city,settlement. A settlement name search needs 4 characters, ignores case but not accents, and counts as 20 requests. |
country | Search only: an ISO code such as GB. |
lat, lon | Nearby only: at most three decimals. More precise coordinates are refused, not rounded. |
radius_km | Nearby only: 1 to 300 (default 50). |
include=trade | Place detail only: add the large country trade table. |
GET https://to.gether.earth/api/v1/public/places?q=sheffield
{
"query": "sheffield",
"places": [
{
"key": "city:2638077",
"label": "Sheffield, United Kingdom",
"country": "United Kingdom",
"country_code": "GB",
"lat": 53.38,
"lon": -1.47,
"tz": "Europe/London",
"kind": "city",
"population": 560000
}
],
"source": {
"name": "GeoNames",
"url": "https://www.geonames.org/",
"licence": "CC BY 4.0",
"attribution": "Place data from GeoNames (geonames.org), CC BY 4.0"
}
}
GET https://to.gether.earth/api/v1/public/places/near?lat=53.381&lon=-1.47&limit=2
{
"near": {
"lat": 53.381,
"lon": -1.47,
"radius_km": 50
},
"places": [
{
"key": "city:2638077",
"label": "Sheffield, United Kingdom",
"country": "United Kingdom",
"country_code": "GB",
"lat": 53.38,
"lon": -1.47,
"tz": "Europe/London",
"kind": "city",
"population": 560000,
"distance_km": 0
},
{
"key": "settlement:11776857",
"label": "Burngreave, United Kingdom",
"country": "United Kingdom",
"country_code": "GB",
"lat": 53.39,
"lon": -1.46,
"tz": "Europe/London",
"kind": "settlement",
"population": 27481,
"distance_km": 1
}
],
"source": {
"name": "GeoNames",
"url": "https://www.geonames.org/",
"licence": "CC BY 4.0",
"attribution": "Place data from GeoNames (geonames.org), CC BY 4.0"
}
}
GET /places/<key> returns the place and the atlas facts Connect reads, each namespaced by the dataset that supplied it: facts.place for the city (climate, geology, twin towns, clubs, founding) and facts.country for its country (languages, holidays, birds, food, World Bank figures). The source.datasets list credits every dataset the facts came from. A settlement has no facts of its own yet, so it returns its place and its country's facts.
GET https://to.gether.earth/api/v1/public/places/city:2638077
{
"place": {
"key": "city:2638077",
"label": "Sheffield, United Kingdom",
"country": "United Kingdom",
"country_code": "GB",
"lat": 53.38,
"lon": -1.47,
"tz": "Europe/London",
"kind": "city",
"population": 560000
},
"facts": {
"place": {
"climate": {
"t": [
3.5,
3.5,
5.5,
8,
11.5,
14.5,
16,
16,
13.5,
10,
6.5,
4
],
"rain": 740,
"cloud": 74,
"kg": "Cfb"
},
"geonames": {
"pop": 560000,
"elev": 80,
"seat": true
}
},
"country": {
"languages": "…"
}
},
"source": {
"name": "Together",
"url": "https://to.gether.earth/developers",
"licence": "Each dataset keeps its own licence, listed under datasets and at https://to.gether.earth/sources",
"attribution": "Place facts compiled by Together from the open datasets listed here. Credit each dataset you show. (Together, to.gether.earth)",
"datasets": [
{
"name": "GeoNames",
"url": "https://www.geonames.org/",
"licence": "CC BY 4.0",
"attribution": "Place data from GeoNames (geonames.org), CC BY 4.0"
},
{
"id": "koppen",
"name": "…",
"url": "…",
"licence": "…",
"attribution": "…"
}
]
}
}
(shortened: the real answer lists every dataset and every fact)
Weather
The weather now at a place, from MET Norway. The temperature is to the nearest degree. The answer comes from Together's own cache, which refreshes about every 15 minutes, so asking often does not call MET Norway more often.
GET https://to.gether.earth/api/v1/public/weather/city:2638077
{
"place": {
"key": "city:2638077",
"label": "Sheffield, United Kingdom",
"country": "United Kingdom",
"country_code": "GB",
"lat": 53.38,
"lon": -1.47,
"tz": "Europe/London",
"kind": "city",
"population": 560000
},
"weather": {
"temperature_c": 11,
"kind": "rain",
"words": "light rain",
"night": false,
"forecast_for": "2026-10-19T18:00:00Z"
},
"source": {
"id": "met-norway",
"name": "MET Norway (Norwegian Meteorological Institute)",
"url": "https://api.met.no/",
"licence": "CC BY 4.0",
"attribution": "Weather data from MET Norway (api.met.no), CC BY 4.0"
}
}
(values illustrative)
kind is one of clear, cloud, fog, rain, sleet, snow. If MET Norway is unavailable you get 503 with Retry-After.
Elevation
Land height in metres above sea level (negative below it) for the map cell that holds each point, read from open terrain tiles at a fixed zoom (10, cells of about 150 m at the equator). It is accurate to the cell, not the point, so a sharp summit reads lower than its true height. Send one point, a comma-separated list of up to 50 (lat=53.381,53.4&lon=-1.47,-1.5), or POST JSON. Coordinates have at most three decimals. Points must fall in no more than 10 map tiles (each about 39 km across); a larger spread returns 422 too_many_tiles. Latitudes beyond 85 degrees return 422.
GET https://to.gether.earth/api/v1/public/elevation?lat=53.381&lon=-1.47
{
"points": [
{
"lat": 53.381,
"lon": -1.47,
"elevation_m": 88
}
],
"zoom": 10,
"cell_metres_at_equator": 153,
"note": "Metres above sea level (negative below it) for the map cell holding each point, read from open terrain tiles. Accurate to the cell, not to the point.",
"source": {
"id": "terrain-tiles",
"name": "Terrain Tiles (Mapzen, on AWS Open Data)",
"url": "https://registry.opendata.aws/terrain-tiles/",
"licence": "Open data from many sources, with attribution",
"attribution": "Terrain Tiles (Mapzen, on AWS Open Data): …the full credit text…"
}
}
(shortened)
POST https://to.gether.earth/api/v1/public/elevation
Content-Type: application/json
{
"points": [
{
"lat": 53.381,
"lon": -1.47
},
{
"lat": 53.4,
"lon": -1.5
}
]
}
Sky
Local time, whether it is day, twilight or night, the length of the day and the Moon, worked out on our server from the place's centre and its time zone. No provider is called. Day length is rounded to 15 minutes and is null with polar set to day or night near the poles. There is no sunrise or sunset to the minute. The Moon is good to about a day.
GET https://to.gether.earth/api/v1/public/sky/city:2638077
{
"place": {
"key": "city:2638077",
"label": "Sheffield, United Kingdom",
"country": "United Kingdom",
"country_code": "GB",
"lat": 53.38,
"lon": -1.47,
"tz": "Europe/London",
"kind": "city",
"population": 560000
},
"sky": {
"tz": "Europe/London",
"local_time": "2026-10-19T18:31+01:00",
"local_date": "2026-10-19",
"utc_offset_minutes": 60,
"day_part": "evening",
"solar_state": "twilight",
"twilight": "civil",
"day_length_minutes": 630,
"polar": null,
"moon": {
"phase": "first quarter",
"age_days": 8.3,
"illumination": 0.6
}
},
"rounding": "Computed from the place centre. Day length is rounded to 15 minutes and the Moon is good to about a day.",
"source": {
"name": "Together",
"url": "https://to.gether.earth/developers",
"licence": "…",
"attribution": "…",
"datasets": [
"GeoNames",
"IANA Time Zone Database"
]
}
}
(shortened)
Live now
Only totals: how many people are on the map now, grouped by coarse place. A place with fewer than 3 people is left out, and a total of one or two reads "a few". Names, ids, trails and members are never included. It is the same count the map already shows, with nothing that identifies anyone. If Live now is paused you get 503.
GET https://to.gether.earth/api/v1/public/live/summary
{
"people": 14,
"places": [
{
"key": "city:2643743",
"label": "London, United Kingdom",
"country": "United Kingdom",
"lat": 51.51,
"lon": -0.13,
"count": 5
},
{
"key": "city:2638077",
"label": "Sheffield, United Kingdom",
"country": "United Kingdom",
"lat": 53.38,
"lon": -1.47,
"count": 3
}
],
"minimum_shown": 3,
"note": "…",
"as_of": "2026-10-19T18:31:00Z",
"source": {
"name": "Together",
"url": "https://to.gether.earth/developers",
"licence": "…",
"attribution": "Aggregate counts from Together (Live now); place names from GeoNames. Free to use with attribution. (Together, to.gether.earth)"
}
}
(numbers illustrative)
World moments
The same quiet, aggregate picture as live.to.gether.earth: what happened on Together in the last two hours, and the world as it is now. It is built only from places, never people. Arrivals and waves are batched per minute, and only once that minute has ended; a batch of fewer than 3 reads “A few people arrived” and names no place. Larger batches name places alphabetically, never ranked and never with a count of their own. Sunrise and sunset reaching large cities are rounded down to 15 minutes; every other time is rounded to the minute. If Live now is paused, the items about people are left out and paused is true.
now holds the shared Moon, airline flights and ships counted from the caches, and the warmest and coldest large cities from cached forecasts. moments is newest first; each item has type, at, a sentence in text, the places it names with a url to open each on the full map, and its source. There is deliberately no event stream, because it would hold one server worker open for every listening tab. The answer is cached for 30 seconds, so poll about every 30 to 60 seconds.
GET https://to.gether.earth/api/v1/public/moments
{
"as_of": "2026-10-19T18:31:00Z",
"paused": false,
"window_minutes": 120,
"minimum_shown": 3,
"now": [ { "type": "moon", "at": "2026-10-19T18:31:00Z", "text": "The Moon is a waning crescent, about 20% lit: the same Moon for everyone.", "places": [], "source": "…" } ],
"moments": [
{ "type": "arrivals", "at": "2026-10-19T18:30:00Z", "text": "3 people arrived, from Lagos, Lima and Sheffield.", "places": [ { "key": "city:2332459", "label": "Lagos, Nigeria", "url": "https://to.gether.earth/?from=city%3A2332459" } ], "source": "Together (Live now)" }
],
"source": { "name": "Together", "…": "…" }
}
(shortened; places and numbers illustrative)
Sources
The same list of datasets as the sources page, as JSON.
GET https://to.gether.earth/api/v1/public/sources
{
"count": 33,
"sources": [
{
"id": "geonames",
"name": "GeoNames",
"url": "https://www.geonames.org/",
"licence": "CC BY 4.0",
"use": "The places you can choose, their populations, heights, capitals, currencies and neighbours"
}
],
"source": {
"name": "Together",
"url": "https://to.gether.earth/developers",
"licence": "…",
"attribution": "…"
}
}
(shortened)
The rules
Credit every source
Every answer has a top-level source object with name, url, licence and attribution. Where it lists datasets, each one has the same four fields. Show the attribution text wherever you show the data, with a link to source.url. A credits screen is fine in a game or an app, as long as people can find it. The licences are those of the datasets, not new ones from me.
Rate limit
60 requests a minute per client. Past that you get 429 and a Retry-After header with the seconds to wait. The limit is counted against a keyed hash of your address that changes every day. The address itself is never stored. Caching helps both of us: honour Cache-Control and send If-None-Match with the ETag to get a 304.
Browsers and caching
Every answer allows any origin (Access-Control-Allow-Origin: *), so a web page can call the API directly. Answers send Cache-Control: public with a lifetime that suits the data, an ETag and Vary. Only GET, HEAD, OPTIONS and the elevation POST are accepted.
Errors
Errors are JSON with an error code, a message and a link here, never cached. 400 is a request I cannot read, 404 an unknown place or path, 405 the wrong method, 415 the elevation POST without JSON, 422 a request I understand but will not run, 429 too many requests and 503 a provider or Live now being unavailable.
What it will never expose
- Presence ids, chosen names, member ids, emblems, trails or anything else about a person on the map. Live counts only.
- Private responses, accounts, chats or anything from the closed beta.
- Anything finer than a coarse place. Places are published to about a kilometre, live counts only for places with 3 or more people, and coordinates you send with more than three decimals are refused.
- Anyone's address. Nothing here fetches a provider on your behalf except weather and terrain, through Together's own cached adapters.
Licence summary
The data is open and keeps its own licence: GeoNames places under CC BY 4.0, MET Norway weather under CC BY 4.0, terrain under the credits in each answer, and the rest as listed by /sources. The sky and the Live now counts are worked out by Together and free to use with attribution. Nothing here is for navigation, safety or emergencies, and the facts can be wrong. There is no warranty, and I may slow, change or pause the API.
Using it from Roblox
local HttpService = game:GetService("HttpService")
local url = "https://to.gether.earth/api/v1/public/places/near?lat=53.381&lon=-1.47&limit=3"
local body = HttpService:GetAsync(url)
local data = HttpService:JSONDecode(body)
print(data.places[1].label, data.source.attribution)
What is coming
Sheff.events venue data will arrive through a separate contract later, with its own permissions. It is not part of this API.