Developer documentation
Travel API
Beta · free to useA read-only JSON API over what Vibe of the City already publishes: a host’s guide and a city’s week. It exists so your own website can show the same live city data your guests see in the guide, without scraping a page or embedding an iframe.
What you get
- A host guide as data — welcome text, practical facts such as check-in and check-out times, house information, and the places the host recommends with coordinates.
- A city week — the forecast from today in the city’s own calendar, plus what is on during that window, with the source behind each entry.
The API carries no guest and no booking data. It returns what a host has published, and nothing that is still a draft.
The key belongs to you, not to a property
A key is bound to your Vibe of the City account. One key reads every published host page and every live city.
That is deliberate. Agencies and developers work on several properties, and a host who hands their website to one should be able to revoke that agency’s key without breaking their own. The key says who is calling and what they have spent — never what they may see. What may be seen is decided by what the host published.
Creating a key
There is no settings screen yet. While the beta runs, sign in on vibeofthe.city and call the endpoint directly; the session cookie authenticates you.
// Signed in on vibeofthe.city, in your browser's console:
await fetch("/api/me/api-keys", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ label: "my website" }),
}).then((r) => r.json());
// → { "key": { "id": "…", "keyPrefix": "votc_live_ab", … }, "plaintextKey": "votc_live_…" }The plaintext key is in that response only. We store a SHA-256 hash, so it cannot be shown again. Keep it on your server — never in a browser bundle, a mobile app or a public repository. Lost a key? Revoke it and mint a new one.
| Endpoint | What it does |
|---|---|
GET /api/me/api-keys | Your live keys, with what each has spent today. Never the secrets. |
POST /api/me/api-keys | Mints a key. Body: { "label": "my website" }. |
DELETE /api/me/api-keys/{id} | Revokes one, immediately and permanently. |
At most five live keys per account.
Limits during the beta
The beta is free. It is still limited, because the weather and places data behind a call costs us money per lookup — a free thing with no ceiling stops being free.
| Limit | Value | Scope |
|---|---|---|
| Daily allowance | 1000 calls | Per key, per UTC day |
| Burst | 60 calls, refilling 60 per minute | Per key |
Every successful response carries the state of your allowance, both as headers (X-RateLimit-Limit, X-RateLimit-Remaining,X-RateLimit-Reset) and in a meta block. Over the daily allowance the API answers 429 with Retry-After pointing at the next midnight UTC.
Need more? Write to us and say what you are building. Raising a key’s allowance is a one-line change on our side during the beta.
Calling it
Every call takes Authorization: Bearer votc_live_…. Responses are cached for five minutes at the edge, and answer in the language you ask for: lang=de,en or es, defaulting to English.
curl -H "Authorization: Bearer $VOTC_API_KEY" \
"https://www.vibeofthe.city/api/public/v1/city/oldenburg-de/week?days=7&lang=de"GET/api/public/v1/host/{slug}
A published host page. The slug is the one in its guide URL —/de/h/ferienwohnung-hindenburgstr-18. Draft and archived pages answer 404, whoever asks.
Links come back tagged ?via=api:<key prefix>, so a host can see which integration actually sends people back to the guide.
{
"host": {
"slug": "ferienwohnung-hindenburgstr-18",
"name": "Ferienwohnung Hindenburgstr. 18",
"citySlug": "oldenburg-de",
"cityName": "Oldenburg",
"kind": "stay",
"accentHex": "#7c2f35",
"logoUrl": "https://…/logo.png",
"guideUrl": "https://www.vibeofthe.city/de/h/ferienwohnung-hindenburgstr-18?t=house&via=api:votc_live_ab",
"welcome": "Herzlich willkommen in der Hindenburgstraße 18.",
"facts": [
{ "label": "Check-in", "value": "ab 15:00" },
{ "label": "Check-out", "value": "bis 10:00" }
],
"house": [
{ "id": "wifi", "phase": "stay", "title": "WLAN", "body": "Netz und Passwort liegen auf dem Esstisch." }
],
"tips": [
{
"name": "Schlossgarten",
"kind": "park",
"note": "Runde vor oder nach der Arbeit.",
"lat": 53.1358,
"lng": 8.2085,
"url": "https://www.vibeofthe.city/de/places/schlossgarten?via=api:votc_live_ab"
}
],
"languages": ["de", "en"]
},
"meta": {
"plan": "beta-free",
"dailyLimit": 1000,
"remaining": 987,
"resetAt": "2026-09-23T00:00:00.000Z",
"docsUrl": "https://www.vibeofthe.city/docs/api"
}
}GET/api/public/v1/city/{slug}/week
The forecast and what is on. days defaults to 7 and is clamped to 1–14. A day with no forecast yet is left out rather than zero-filled, so you can tell a missing day from a cold one.
Ranking signals are not in the response. Score, relevance and “for you” only mean something next to a traveller, and a key is a machine.
{
"city": {
"citySlug": "oldenburg-de",
"cityName": "Oldenburg",
"timezone": "Europe/Berlin",
"from": "2026-09-22",
"days": [
{
"date": "2026-09-22",
"tempMax": 17,
"tempMin": 10,
"precipitationMm": 0.4,
"precipitationProbability": 20,
"weatherCode": 3
}
],
"events": [
{
"title": "Wochenmarkt am Rathausmarkt",
"detail": "Regionale Stände in der Innenstadt.",
"startsOn": "2026-09-24",
"endsOn": "2026-09-24",
"startsAt": "07:00",
"location": "Rathausmarkt",
"category": "market",
"sourceName": "Stadt Oldenburg",
"sourceUrl": "https://…"
}
],
"generatedAt": "2026-09-22T05:00:00.000Z"
},
"meta": { "plan": "beta-free", "dailyLimit": 1000, "remaining": 986, "resetAt": "2026-09-23T00:00:00.000Z", "docsUrl": "https://www.vibeofthe.city/docs/api" }
}Errors
Every error is JSON in the shape { "error": "invalid_api_key" }, sometimes with extra fields such as resetAt or docsUrl.
| Code | Status | Meaning |
|---|---|---|
missing_api_key | 401 | No Authorization header, or it is not a Bearer token. |
invalid_api_key | 401 | Unknown or revoked key. |
quota_exceeded | 429 | The key's daily allowance is spent. Retry-After points at the reset. |
rate_limited | 429 | Too many calls in a short burst. Retry-After is in seconds. |
host_not_found | 404 | No published host page with that slug. |
city_not_found | 404 | That city is not live. |
How to use it well
- Call it from your server, not from your visitors’ browsers. One request every fifteen minutes serves any number of visitors, keeps your allowance intact, and means no data about your visitors reaches us — which is usually what makes this embeddable without a consent banner.
- Keep the last good answer. If a call fails, show what you had. City data that is an hour old beats an empty box.
- Say where it comes from. A line such as “City data by Vibe of the City” next to the module, and a link back to the guide, is all we ask.
- Do not re-publish it as your own dataset. Render it for your guests; do not resell it or rebuild a competing city guide from it.
While it is a beta
Fields will be added; existing ones will not change meaning or disappear without a new version prefix. If we have to break something, key holders hear about it by email first.
Questions, a bug, or a limit that is in your way: hello@vibeofthe.city.
See also the terms of service and the privacy policy.