Introduction
Open data API for fuel prices, currency rates and vanlife events. Free to use with attribution (CC BY 4.0).
Welcome to the **OpenVan.camp Public API** — open data for fuel prices, currency rates and vanlife events. The fuel endpoint returns the current country count in `meta.total_countries`.
## License
All data is available under **[CC BY 4.0](https://creativecommons.org/licenses/by/4.0/)**.
You are free to use it in your apps, bots, and articles — please cite **OpenVan.camp (openvan.camp)** as the source.
## Base URL
All endpoints are available at `https://openvan.camp`.
## Authentication
No authentication required. All endpoints are public.
## Rate Limits
Public endpoints are limited to **120 requests/minute per IP** (some helper endpoints 30–60/min).
Watch the `X-RateLimit-Limit` / `X-RateLimit-Remaining` response headers; exceeding the limit returns **HTTP 429**.
Data is cached — please don't poll more often than every 10 minutes.
## Attribution
Every response includes an `_attribution` object with license and attribution details:
```json
"_attribution": {
"data_source": "openvan.camp",
"license": "CC BY 4.0",
"attribution_url": "https://openvan.camp/",
"attribution_html": "Data: <a href=\"https://openvan.camp/\">OpenVan.camp</a> (CC BY 4.0)"
}
```
Pass `?source=yoursite.com` to identify your integration — no registration required. Your value is echoed back as `_attribution.your_source`:
```
GET /api/fuel/prices?source=myapp.com
```
<aside>Code examples are shown in the right panel. Switch language with the tabs at the top right.</aside>
Authenticating requests
This API is not authenticated.
Currency Rates
Get currency rates
Returns EUR-base exchange rates. By default the response is limited to ~165 ISO 4217 active fiat currencies that are realistic for travel/fuel-price use cases.
Source chain: national central banks (RUB/TRY/GEL/UAH/BYN/UZS) for precision, then Fawaz API / ExchangeRate-API / Frankfurter / ECB for the long tail.
Refresh: Redis-backed cache stored forever; refreshed by cron every 6 hours.
A separate hourly watchdog alerts in Telegram if cache > 25h old.
HTTP cache: Cache-Control: public, max-age=1800 (edge X-Accel-Expires: 1800).
Edge кеш 30 минут — соответствует фактической частоте обновления (раз в 6 часов).
Note: PHP-уровневый Cache-Control затирается nginx через fastcgi_hide_header +
cache-control-map.conf, поэтому фактический header в ответе всегда задаётся nginx.
Query parameters:
include=all— return the raw upstream set (~340 codes), including crypto, metals and historical/legacy codes (BTC, ETH, USDT, XAU, ATS, DEM, VEF, TRL, ZWL, etc.). Without this parameter, those are filtered out.
Market overrides: the meta.overrides field lists codes whose rate is a manual
market-rate override rather than an official feed. Currently only CUP (Cuba uses
a parallel market rate of ~120 CUP/USD; the official fixing of ~24 CUP/USD does not
reflect retail fuel pricing).
License: CC BY 4.0 — free to use with attribution to OpenVan.camp.
Example request:
curl --request GET \
--get "https://openvan.camp/api/currency/rates" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"https://openvan.camp/api/currency/rates"
);
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());import requests
import json
url = 'https://openvan.camp/api/currency/rates'
headers = {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers)
response.json()Example response (200, Success):
{
"success": true,
"rates": {
"EUR": 1,
"USD": 1.16698,
"GBP": 0.855238,
"RUB": 98.2897,
"TRY": 56.1398,
"GEL": 3.0551
},
"meta": {
"count": 152,
"base": "EUR",
"updated_at": "2026-08-27T03:05:15+00:00",
"refreshed_every_hours": 6,
"max_age_seconds": 90000,
"scope": "fiat",
"overrides": {
"CUP": {
"reason": "dual_exchange_rate",
"source": "market_rate_120_cup_per_usd",
"note": "Куба: официальный фиксинг ~24 CUP/USD расходится с реальным рынком ~120 CUP/USD."
}
}
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Endpoints
POST api/route-cost
Example request:
curl --request POST \
"https://openvan.camp/api/route-cost" \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "{
\"waypoints\": [
\"Волгоград\",
\"Грузия\",
\"Анталия\"
],
\"tank\": 80,
\"cons\": 10,
\"fuel\": \"diesel\",
\"currency\": \"RUB\",
\"locale\": \"ru\"
}"
const url = new URL(
"https://openvan.camp/api/route-cost"
);
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"waypoints": [
"Волгоград",
"Грузия",
"Анталия"
],
"tank": 80,
"cons": 10,
"fuel": "diesel",
"currency": "RUB",
"locale": "ru"
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());import requests
import json
url = 'https://openvan.camp/api/route-cost'
payload = {
"waypoints": [
"Волгоград",
"Грузия",
"Анталия"
],
"tank": 80,
"cons": 10,
"fuel": "diesel",
"currency": "RUB",
"locale": "ru"
}
headers = {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('POST', url, headers=headers, json=payload)
response.json()Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
VanSky weather for all countries
Все страны — сводные данные для таблицы / карты. Ответ ~900 КБ, кешируется на 1 час.
Example request:
curl --request GET \
--get "https://openvan.camp/api/vansky/weather" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"https://openvan.camp/api/vansky/weather"
);
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());import requests
import json
url = 'https://openvan.camp/api/vansky/weather'
headers = {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers)
response.json()Example response (200, Success (truncated)):
{
"data": [
{
"country_code": "DE",
"country_slug": "germany",
"van_score": 90,
"temp_day": 24.1,
"temp_night": 14.3,
"solar_kwh": 2.8,
"sea_temp": 18.2,
"is_coastal": true,
"label": "ideal"
}
],
"count": 162,
"updated_at": "2026-08-27T02:00:00+03:00",
"source": "Open-Meteo (api.open-meteo.com)"
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
VanSky weather for one country
Подробные данные одной страны (ISO 3166-1 alpha-2).
Example request:
curl --request GET \
--get "https://openvan.camp/api/vansky/weather/DE" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"https://openvan.camp/api/vansky/weather/DE"
);
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());import requests
import json
url = 'https://openvan.camp/api/vansky/weather/DE'
headers = {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers)
response.json()Example response (200):
Show headers
cache-control: max-age=3600, public
content-type: application/json
x-ratelimit-limit: 120
x-ratelimit-remaining: 116
x-seo: Liked our optimization? We do technical SEO + GEO. Let's talk -> https://openvan.camp/en/about#contacts
access-control-allow-origin: *
{
"data": {
"code": "DE",
"marine": {
"sea_temp": 19.2,
"wave_dir": 283,
"swell_dir": 334,
"wave_height": 1.28,
"wave_period": 4.3,
"swell_height": 0,
"swell_period": 4.6,
"wind_wave_dir": 282,
"wind_wave_height": 1.28,
"wind_wave_period": 4.3
},
"region": "europe",
"weather": {
"sunset": "2026-09-01T17:55",
"uv_max": null,
"sunrise": "2026-09-01T04:17",
"humidity": 81,
"temp_day": 23,
"wind_dir": 263,
"wind_max": 15.9,
"dew_point": 12.9,
"precip_sum": 0.7,
"temp_night": 15.1,
"cloud_cover": 90,
"temp_current": 16.2,
"weather_code": 51,
"feels_like_day": 21.7,
"peak_sun_hours": 4.1,
"sunshine_hours": 11.2,
"wind_gusts_max": 41.4,
"precip_prob_max": 0,
"feels_like_night": 14.5,
"feels_like_current": 15.1,
"shortwave_radiation_sum": 14.75
},
"forecast": [
{
"date": "2026-09-01",
"temp_day": 23,
"solar_kwh": 1.7,
"van_score": 92,
"temp_night": 15.1,
"drive_score": 100,
"score_label": "ideal",
"sleep_score": 90,
"weather_code": 51
},
{
"date": "2026-09-02",
"temp_day": 21.6,
"solar_kwh": 1.3,
"van_score": 92,
"temp_night": 13.5,
"drive_score": 100,
"score_label": "ideal",
"sleep_score": 88,
"weather_code": 3
},
{
"date": "2026-09-03",
"temp_day": 22.5,
"solar_kwh": 1.3,
"van_score": 92,
"temp_night": 14.9,
"drive_score": 100,
"score_label": "ideal",
"sleep_score": 86,
"weather_code": 3
},
{
"date": "2026-09-04",
"temp_day": 26.6,
"solar_kwh": 1.2,
"van_score": 88,
"temp_night": 17.2,
"drive_score": 94,
"score_label": "ideal",
"sleep_score": 82,
"weather_code": 65
},
{
"date": "2026-09-05",
"temp_day": 21.2,
"solar_kwh": 1.5,
"van_score": 89,
"temp_night": 14.1,
"drive_score": 98,
"score_label": "ideal",
"sleep_score": 87,
"weather_code": 51
},
{
"date": "2026-09-06",
"temp_day": 24.3,
"solar_kwh": 2,
"van_score": 92,
"temp_night": 11.9,
"drive_score": 100,
"score_label": "ideal",
"sleep_score": 84,
"weather_code": 2
},
{
"date": "2026-09-07",
"temp_day": 26.7,
"solar_kwh": 1.6,
"van_score": 92,
"temp_night": 15.2,
"drive_score": 100,
"score_label": "ideal",
"sleep_score": 84,
"weather_code": 2
}
],
"sea_score": null,
"solar_kwh": 1.7,
"van_score": 92,
"confidence": "high",
"fetched_at": "2026-09-01T03:12:05.773449Z",
"is_coastal": true,
"week_score": 88,
"band_counts": {
"hard": 0,
"ideal": 1698,
"extreme": 0,
"acceptable": 1,
"comfortable": 76
},
"drive_score": 100,
"score_label": "ideal",
"sleep_score": 85,
"solar_score": 36,
"drive_window": null,
"awning_status": "caution",
"best_move_day": {
"date": "2026-09-01",
"temp_day": 23,
"solar_kwh": 1.7,
"van_score": 94,
"temp_night": 15.1,
"drive_score": 100,
"score_label": "ideal",
"sleep_score": 90,
"weather_code": 51
},
"sample_points": 1775,
"typical_score": 87,
"ideal_coverage": 100,
"sample_regions": 16,
"best_area_score": 89,
"recommendations": [
{
"key": "vansky.rec.awning_caution",
"type": "warning",
"params": {
"gusts": 41.4
}
},
{
"key": "vansky.rec.night_ideal",
"type": "success",
"params": {
"temp": 16.1
}
},
{
"key": "vansky.rec.day_comfortable",
"type": "success",
"params": {
"temp": 23.6
}
}
],
"condensation_risk": "medium",
"aggregation_version": 2,
"comfortable_coverage": 100
},
"updated_at": "2026-09-01T03:12:58+03:00",
"source": "Open-Meteo (api.open-meteo.com)",
"_attribution": {
"data_source": "openvan.camp",
"license": "CC BY 4.0",
"attribution_url": "https://openvan.camp/",
"attribution_html": "Data: <a href=\"https://openvan.camp/\">OpenVan.camp</a> (CC BY 4.0)"
}
}
Example response (404, Not found):
{
"error": "Country not found or not supported"
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Events
List events
Returns a paginated list of vanlife events (exhibitions, festivals, meetups, road trips). Filter by status, type, country, or search by name.
Example request:
curl --request GET \
--get "https://openvan.camp/api/events?locale=en&status=upcoming&type=festival&country=DE&search=Adventure+Northside&page=1&limit=30" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"https://openvan.camp/api/events"
);
const params = {
"locale": "en",
"status": "upcoming",
"type": "festival",
"country": "DE",
"search": "Adventure Northside",
"page": "1",
"limit": "30",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());import requests
import json
url = 'https://openvan.camp/api/events'
params = {
'locale': 'en',
'status': 'upcoming',
'type': 'festival',
'country': 'DE',
'search': 'Adventure Northside',
'page': '1',
'limit': '30',
}
headers = {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers, params=params)
response.json()Example response (200, Success):
{
"events": [
{
"id": 960,
"slug": "adventure-northside-2026",
"event_name": "Adventure Northside 2026",
"event_type": "festival",
"event_type_emoji": "🎉",
"start_date": "2026-09-18",
"end_date": "2026-09-20",
"city": "Basthorst",
"country_code": "DE",
"country": {
"code": "de",
"slug": "germany",
"name": "Germany",
"flag_emoji": "🇩🇪"
},
"status": "upcoming",
"url": "https://openvan.camp/en/event/adventure-northside-2026"
}
],
"pagination": {
"total": 48,
"page": 1,
"limit": 30,
"pages": 2
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Get event details
Returns full details for a single vanlife event by slug, including location, description, and social links.
Example request:
curl --request GET \
--get "https://openvan.camp/api/event/adventure-northside-2026?locale=en" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"https://openvan.camp/api/event/adventure-northside-2026"
);
const params = {
"locale": "en",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());import requests
import json
url = 'https://openvan.camp/api/event/adventure-northside-2026'
params = {
'locale': 'en',
}
headers = {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers, params=params)
response.json()Example response (200, Success):
{
"id": 960,
"slug": "adventure-northside-2026",
"event_name": "Adventure Northside 2026",
"event_type": "festival",
"event_type_emoji": "🎉",
"start_date": "2026-09-18",
"end_date": "2026-09-20",
"city": "Basthorst",
"country_code": "DE",
"description": "Northern Germany's Overland & Self-build expo and festival...",
"official_url": "https://adventurenorthside.de/",
"image_url": "https://...",
"status": "upcoming",
"url": "https://openvan.camp/en/event/adventure-northside-2026"
}
Example response (404, Not found):
{
"message": "No query results for model [App\\Models\\Event]."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Get event articles
Returns source articles linked to this event.
When locale is provided, attempts to return only articles in that language.
If no articles match the requested locale, all articles are returned as a fallback
(they may be in the original source language, e.g. Japanese or German).
The language field on each article indicates the actual language of the source.
Example request:
curl --request GET \
--get "https://openvan.camp/api/event/adventure-northside-2026/articles?locale=en" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"https://openvan.camp/api/event/adventure-northside-2026/articles"
);
const params = {
"locale": "en",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());import requests
import json
url = 'https://openvan.camp/api/event/adventure-northside-2026/articles'
params = {
'locale': 'en',
}
headers = {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers, params=params)
response.json()Example response (200, Success):
[
{
"id": 1001,
"title": "Adventure Northside 2026 opens its doors",
"image_url": "https://...",
"published_at": "2026-03-11T10:00:00+00:00",
"source_name": "CamperVan Magazine",
"original_url": "https://...",
"language": "en"
}
]
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Fuel Prices
Get fuel prices
Returns current retail fuel prices for all supported countries.
Price keys follow FuelGradeCatalog::ORDER plus the legacy premium alias.
Data is updated weekly from 45+ official government sources and independent aggregators.
Prices are weighted averages from multiple sources per country. The sources array
lists all contributing data providers (sorted by trust weight, highest first).
License: CC BY 4.0 — free to use with attribution to OpenVan.camp.
Example request:
curl --request GET \
--get "https://openvan.camp/api/fuel/prices?country=tr%0A%0A%D0%9F%D0%BE%D0%BB%D0%B5+%60unit%60+%E2%80%94+%D0%B5%D0%B4%D0%B8%D0%BD%D0%B8%D1%86%D0%B0%2C+%D0%B2+%D0%BA%D0%BE%D1%82%D0%BE%D1%80%D0%BE%D0%B9+%D0%BE%D0%BF%D1%83%D0%B1%D0%BB%D0%B8%D0%BA%D0%BE%D0%B2%D0%B0%D0%BD%D0%B0+%D1%86%D0%B5%D0%BD%D0%B0+%D1%81%D1%82%D1%80%D0%B0%D0%BD%D1%8B%3A%0A%60liter%60%2C+%60gallon%60+%28US%2C+3.78541+%D0%BB%29+%D0%B8%D0%BB%D0%B8+%60imperial_gallon%60+%284.54609+%D0%BB%2C%0A%D0%B1%D1%8B%D0%B2%D1%88%D0%B8%D0%B5+%D0%B1%D1%80%D0%B8%D1%82%D0%B0%D0%BD%D1%81%D0%BA%D0%B8%D0%B5+%D1%82%D0%B5%D1%80%D1%80%D0%B8%D1%82%D0%BE%D1%80%D0%B8%D0%B8%29.+%D0%97%D0%BD%D0%B0%D1%87%D0%B5%D0%BD%D0%B8%D1%8F+%D0%BE%D1%82%D0%B4%D0%B0%D1%8E%D1%82%D1%81%D1%8F+%D0%B2+%D1%82%D0%BE%D0%BC+%D0%B2%D0%B8%D0%B4%D0%B5%2C+%D0%B2+%D0%BA%D0%B0%D0%BA%D0%BE%D0%BC+%D0%B8%D1%85%0A%D0%BF%D1%83%D0%B1%D0%BB%D0%B8%D0%BA%D1%83%D0%B5%D1%82+%D0%B8%D1%81%D1%82%D0%BE%D1%87%D0%BD%D0%B8%D0%BA%2C+%E2%80%94+%D0%BF%D1%80%D0%B8%D0%B2%D0%BE%D0%B4%D0%B8%D1%82%D1%8C+%D0%BA+%D0%BB%D0%B8%D1%82%D1%80%D0%B0%D0%BC+%D0%B4%D0%BE%D0%BB%D0%B6%D0%B5%D0%BD+%D0%BF%D0%BE%D1%82%D1%80%D0%B5%D0%B1%D0%B8%D1%82%D0%B5%D0%BB%D1%8C." \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"https://openvan.camp/api/fuel/prices"
);
const params = {
"country": "tr
Поле `unit` — единица, в которой опубликована цена страны:
`liter`, `gallon` (US, 3.78541 л) или `imperial_gallon` (4.54609 л,
бывшие британские территории). Значения отдаются в том виде, в каком их
публикует источник, — приводить к литрам должен потребитель.",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());import requests
import json
url = 'https://openvan.camp/api/fuel/prices'
params = {
'country': 'tr
Поле `unit` — единица, в которой опубликована цена страны:
`liter`, `gallon` (US, 3.78541 л) или `imperial_gallon` (4.54609 л,
бывшие британские территории). Значения отдаются в том виде, в каком их
публикует источник, — приводить к литрам должен потребитель.',
}
headers = {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers, params=params)
response.json()Example response (200, Success):
{
"success": true,
"data": {
"DE": {
"country_code": "DE",
"country_name": "Germany",
"region": "europe",
"currency": "EUR",
"local_currency": "EUR",
"unit": "liter",
"prices": {
"gasoline_regular": null,
"gasoline": 2.1313,
"gasoline_premium": null,
"gasoline_super": null,
"diesel_regular": null,
"diesel": 2.2845,
"diesel_premium": null,
"lpg": 1.113,
"cng": null,
"e85": null,
"kerosene": null,
"premium": null
},
"price_changes": {
"gasoline_regular": null,
"gasoline": -0.02,
"gasoline_premium": null,
"gasoline_super": null,
"diesel_regular": null,
"diesel": 0.01,
"diesel_premium": null,
"lpg": 0,
"cng": null,
"e85": null,
"kerosene": null,
"premium": null
},
"fetched_at": "2026-08-22T08:50:59+03:00",
"sources": [
"Fuelo.net",
"EU Weekly Oil Bulletin",
"Cargopedia.net"
],
"sources_count": 3,
"is_excluded": false
}
},
"meta": {
"total_countries": 142,
"updated_at": "2026-08-22 08:51:00",
"cache_ttl_hours": 6
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Stories
Vanlife news stories — clustered and translated into 7 languages. Each story aggregates multiple source articles from different publishers.
List stories
Returns a paginated list of vanlife news stories in the requested language.
Titles and summaries are translated; sources are original-language articles.
Example request:
curl --request GET \
--get "https://openvan.camp/api/stories?locale=en&category=festival&country=DE&search=vanlife+festival&page=1&limit=20" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"https://openvan.camp/api/stories"
);
const params = {
"locale": "en",
"category": "festival",
"country": "DE",
"search": "vanlife festival",
"page": "1",
"limit": "20",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());import requests
import json
url = 'https://openvan.camp/api/stories'
params = {
'locale': 'en',
'category': 'festival',
'country': 'DE',
'search': 'vanlife festival',
'page': '1',
'limit': '20',
}
headers = {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers, params=params)
response.json()Example response (200, Success):
{
"stories": [
{
"slug": "adac-camper-des-jahres-der-wettbewerb-fuer",
"title": "ADAC launches 'Camper of the Year' competition for motorhome drivers",
"summary": "ADAC is holding the national 'Camper des Jahres 2026' competition for motorhome owners...",
"image_url": "https://...",
"category": {
"slug": "festival",
"name": "Festivals"
},
"countries": [
{
"code": "de",
"name": "Germany",
"flag_emoji": "🇩🇪"
}
],
"first_published_at": "2026-01-22T11:00:00+03:00",
"articles_count": 5,
"url": "https://openvan.camp/en/news/festival/adac-camper-des-jahres-der-wettbewerb-fuer"
}
],
"pagination": {
"total": 120,
"page": 1,
"limit": 20,
"pages": 6
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Get story details
Returns full details for a single news story including all source articles.
The sources array contains original publisher articles with direct links.
Example request:
curl --request GET \
--get "https://openvan.camp/api/story/adac-camper-des-jahres-der-wettbewerb-fuer?locale=en" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"https://openvan.camp/api/story/adac-camper-des-jahres-der-wettbewerb-fuer"
);
const params = {
"locale": "en",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());import requests
import json
url = 'https://openvan.camp/api/story/adac-camper-des-jahres-der-wettbewerb-fuer'
params = {
'locale': 'en',
}
headers = {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers, params=params)
response.json()Example response (200, Success):
{
"slug": "adac-camper-des-jahres-der-wettbewerb-fuer",
"title": "ADAC launches 'Camper of the Year' competition for motorhome drivers",
"summary": "ADAC is holding the national 'Camper des Jahres 2026' competition for motorhome owners...",
"image_url": "https://...",
"category": {
"slug": "festival",
"name": "Festivals"
},
"countries": [
{
"code": "de",
"name": "Germany",
"flag_emoji": "🇩🇪"
}
],
"first_published_at": "2026-01-22T11:00:00+03:00",
"last_updated_at": "2026-07-04T04:16:47+03:00",
"articles_count": 5,
"url": "https://openvan.camp/en/news/festival/adac-camper-des-jahres-der-wettbewerb-fuer",
"sources": [
{
"title": "Nürburgring: Bundesweiter Wettbewerb «Camper des Jahres»",
"original_url": "https://www.blick-aktuell.de/Berichte/...",
"source_name": "BLICK aktuell",
"published_at": "2026-04-30T09:24:20+03:00",
"language": "de",
"image_url": "https://..."
}
]
}
Example response (404, Not found):
{
"error": "Story not found."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Semantic news search (RAG)
Semantic search over news via the same engine as the /{locale}/search page (SemanticSearchService: Jina embeddings + pgvector). Returns matching news stories ranked by similarity. Story summary shape matches /stories.
Example request:
curl --request GET \
--get "https://openvan.camp/api/news/search?q=ford+camper+2026&locale=ru&limit=8" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"https://openvan.camp/api/news/search"
);
const params = {
"q": "ford camper 2026",
"locale": "ru",
"limit": "8",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());import requests
import json
url = 'https://openvan.camp/api/news/search'
params = {
'q': 'ford camper 2026',
'locale': 'ru',
'limit': '8',
}
headers = {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers, params=params)
response.json()Example response (200):
Show headers
cache-control: no-cache, private
content-type: application/json
x-ratelimit-limit: 120
x-ratelimit-remaining: 117
x-seo: Liked our optimization? We do technical SEO + GEO. Let's talk -> https://openvan.camp/en/about#contacts
access-control-allow-origin: *
{
"results": [
{
"slug": "panoramic-ford-f-camper-truck-costs-fraction",
"title": "Supertramp представила кемпер Paragon на базе Ford F-350",
"summary": "Компания Supertramp анонсировала новую модель кемпера Paragon на шасси Ford F-350. Базовая цена составляет 199 000 долларов, что значительно ниже конкурентов, таких как Earthroamer (от 825 000 долларов) и Winnebago Arka (330 000 долларов). Модель оснащена композитным модулем с вакуумной инфузией, спальными местами на четыре человека и опциональной палаткой на крыше для шести человек. Стандартная комплектация включает литий-ионную батарею на 270 А·ч, инвертор на 3000 Вт и солнечную систему на 660 Вт.",
"image_url": "https://assets.newatlas.com/dims4/default/00d5475/2147483647/strip/true/crop/3276x1720+0+514/resize/1200x630!/quality/85/?url=https%3A%2F%2Fnewatlas-brightspot.s3.amazonaws.com%2Fb8%2F31%2F5eb910d0450196ac9000fdc083c3%2F699761506-18360938500231222-6098785867930289037-n.jpg&na.image_optimisation=0",
"category": {
"slug": "industry",
"name": "Индустрия"
},
"countries": [],
"first_published_at": "2026-06-05T04:35:09+03:00",
"articles_count": 1,
"url": "https://openvan.camp/ru/news/industry/panoramic-ford-f-camper-truck-costs-fraction",
"score": 0.562
},
{
"slug": "notable-camper-vans-weve-found-2026-far-gear",
"title": "Самые заметные кемперы 2026 года: обзор",
"summary": "В 2026 году сегмент кемперов активно развивается: появляются новые электрические модели от Hyundai, Kia и Volkswagen, а также многочисленные доработки от сторонних производителей. Среди традиционных платформ по-прежнему популярны Mercedes Sprinter и Ram ProMaster.",
"image_url": "https://www.gearpatrol.com/wp-content/uploads/sites/2/2026/08/Camper-Vans-Roundup-Lead.webp",
"category": {
"slug": "industry",
"name": "Индустрия"
},
"countries": [],
"first_published_at": "2026-08-12T20:04:31+03:00",
"articles_count": 1,
"url": "https://openvan.camp/ru/news/industry/notable-camper-vans-weve-found-2026-far-gear",
"score": 0.547
},
{
"slug": "new-carinspired-fiberglass-camper-almost-color",
"title": "Новый стеклопластиковый кемпер Outranger можно заказать в любом цвете RAL",
"summary": "Компания Motsmann Engineering из Орегона представила кемпер Outranger, который отличается стеклопластиковым кузовом, собираемым из шести панелей, и модульной рамой на болтах. Кемпер весит менее 1500 фунтов, что позволяет буксировать его даже компактным пикапам, таким как Ford Maverick. Доступен в 216 цветах RAL, включая нестандартные. Базовая комплектация включает литий-железо-фосфатную батарею на 200 А·ч, а опционально доступны солнечные панели, инвертор и кондиционер.",
"image_url": "https://images-stag.jazelc.com/uploads/theautopian-m2en/Fiberglass_Camper_TS-1.jpg",
"category": {
"slug": "industry",
"name": "Индустрия"
},
"countries": [
{
"code": "us",
"name": "США",
"flag_emoji": "🇺🇸"
}
],
"first_published_at": "2026-08-24T18:55:42+03:00",
"articles_count": 1,
"url": "https://openvan.camp/ru/news/industry/new-carinspired-fiberglass-camper-almost-color",
"score": 0.543
},
{
"slug": "el-mejor-motorhome-de-2026-que-tiene-de-especial",
"title": "Carado T457 на базе Ford Transit признан лучшим автодомом 2026 года по версии Red Dot Awards",
"summary": "Модель Carado T457, полуинтегрированный автодом на платформе Ford Transit, получил награду Red Dot Design Awards 2026 в категории автодомов. Длина автомобиля составляет около 7,4 м, ширина — 2,32 м, высота — 2,93 м. Внутреннее пространство рассчитано на четырёх человек, включает гостиную с поворотными сиденьями, кухню, раздельный санузел и спальню с двумя односпальными кроватями, которые можно объединить. Базовая версия стоит от 71 690 евро.",
"image_url": "https://supertruck.com.ar/wp-content/uploads/2026/07/mejor-motorhome-de-2026-carado-t457-5.jpg",
"category": {
"slug": "industry",
"name": "Индустрия"
},
"countries": [
{
"code": "de",
"name": "Германия",
"flag_emoji": "🇩🇪"
}
],
"first_published_at": "2026-07-26T08:10:47+03:00",
"articles_count": 1,
"url": "https://openvan.camp/ru/news/industry/el-mejor-motorhome-de-2026-que-tiene-de-especial",
"score": 0.529
}
],
"timing_ms": 568,
"_attribution": {
"data_source": "openvan.camp",
"license": "CC BY 4.0",
"attribution_url": "https://openvan.camp/",
"attribution_html": "Data: <a href=\"https://openvan.camp/\">OpenVan.camp</a> (CC BY 4.0)"
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Daily news digest (raw content)
Returns the structured evening news digest for the requested locale, assembled once per evening by the site. Consumers (site social channels,
Example request:
curl --request GET \
--get "https://openvan.camp/api/news/digest?locale=ru" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"https://openvan.camp/api/news/digest"
);
const params = {
"locale": "ru",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());import requests
import json
url = 'https://openvan.camp/api/news/digest'
params = {
'locale': 'ru',
}
headers = {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers, params=params)
response.json()Example response (200, Success):
{
"locale": "ru",
"date": "2026-07-25",
"intro": "Дороги сегодня подкинули всё…",
"groups": [
{
"key": "incident",
"title": "⚠️ Происшествия",
"items": [
{
"id": 123,
"flag": "🇩🇪",
"hook": "Заголовок-зацепка",
"detail": "Деталь сюжета",
"url": "https://openvan.camp/ru/news/incident/slug"
}
]
}
]
}
Example response (204, No digest yet):
Empty response
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
VanBasket Food Price Index
VanBasket Index shows how expensive food is in a country relative to the world average (World = 100). Based on World Bank ICP 2021 data, adjusted with IMF CPI.
Get all countries with VanBasket index.
Returns food price index for 90+ countries. World average = 100. Above 100 = more expensive, below 100 = cheaper.
Example request:
curl --request GET \
--get "https://openvan.camp/api/vanbasket/countries" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"https://openvan.camp/api/vanbasket/countries"
);
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());import requests
import json
url = 'https://openvan.camp/api/vanbasket/countries'
headers = {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers)
response.json()Example response (200):
{
"success": true,
"data": {
"DE": {
"country_code": "DE",
"country_name": "Germany",
"vanbasket_index": 124.9,
"pct_vs_world": 24.9
}
},
"meta": {
"total_countries": 92
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Compare food prices between two countries.
Example request:
curl --request GET \
--get "https://openvan.camp/api/vanbasket/compare?from=DE&to=TR" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"https://openvan.camp/api/vanbasket/compare"
);
const params = {
"from": "DE",
"to": "TR",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());import requests
import json
url = 'https://openvan.camp/api/vanbasket/compare'
params = {
'from': 'DE',
'to': 'TR',
}
headers = {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers, params=params)
response.json()Example response (200):
{
"success": true,
"data": {
"from": {
"country_code": "DE",
"country_name": "Germany",
"vanbasket_index": 124.9
},
"to": {
"country_code": "TR",
"country_name": "Turkey",
"vanbasket_index": 88
},
"diff_percent": -29.5,
"budget_100": 70,
"cheaper": true
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Get VanBasket country details
Detailed data for a single country including historical snapshots.
Example request:
curl --request GET \
--get "https://openvan.camp/api/vanbasket/countries/DE" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"https://openvan.camp/api/vanbasket/countries/DE"
);
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());import requests
import json
url = 'https://openvan.camp/api/vanbasket/countries/DE'
headers = {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers)
response.json()Example response (200):
{
"success": true,
"data": {
"country": {
"country_code": "DE",
"vanbasket_index": 124.9
},
"snapshots": [
{
"snapshot_date": "2021-01-01",
"vanbasket_index": 124.9
}
]
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Visa & Border Rules
How long you may stay and how the days are counted — for all 199 × 199 passport/destination pairs, plus temporary vehicle import rules.
Every answer carries confidence and source_url, and says which layer it
came from: curated (hand-verified), window (counting window applied on
top of the dataset), zone (Schengen-style shared counter) or dataset.
Treat confidence: low as "the number of days is right, the way they are
counted is an assumption" — verify at the border.
Check a passport against a destination.
Returns the stay rule (days, counting window, whether a visa run resets the counter), the entry mode, and the vehicle import rule for the same destination.
If the country belongs to a shared-counter zone, the stay rule is the
zone's — entering Spain spends Schengen days, not "Spanish" ones — and
counted_against names the place the counter belongs to.
Example request:
curl --request GET \
--get "https://openvan.camp/api/visa/check?passport=RU&destination=TR&weight=le35&plate=third&locale=en" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"https://openvan.camp/api/visa/check"
);
const params = {
"passport": "RU",
"destination": "TR",
"weight": "le35",
"plate": "third",
"locale": "en",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());import requests
import json
url = 'https://openvan.camp/api/visa/check'
params = {
'passport': 'RU',
'destination': 'TR',
'weight': 'le35',
'plate': 'third',
'locale': 'en',
}
headers = {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers, params=params)
response.json()Example response (200):
{
"success": true,
"data": {
"passport": {
"code": "RU",
"name": "Russia"
},
"destination": {
"code": "TR",
"name": "Turkey",
"kind": "country",
"zone": null
},
"entry_mode": "visa_free",
"stay": {
"visa_type": "visa_free",
"max_continuous": 90,
"max_total": 90,
"window": "rolling",
"window_days": 180,
"visa_run": false,
"confidence": "high",
"source_url": "https://www.mfa.gov.tr/visa-information-for-foreigners.en.mfa",
"note": "Безвиз 90/180.",
"layer": "curated"
},
"vehicle": {
"max_days": null,
"basis": "tied_to_person",
"green_card": "required",
"confidence": "high"
}
}
}
Example response (422):
{
"success": false,
"error": "Parameters \"passport\" and \"destination\" are required."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Visa and vehicle rules for a whole route, in one request.
Built for the fuel-cost calculator: it knows the countries a trip crosses but nothing about the traveller, so the answer has to cover several passports at once. Rows are collapsed by rule — passports that get the same treatment share one row instead of repeating the same numbers.
Countries sharing a counter (Schengen and the like) are merged into one leg: adding up "Bulgarian" and "Croatian" days would be plainly wrong.
Example request:
curl --request GET \
--get "https://openvan.camp/api/visa/route?t=RU%2CGE%2CTR&p=RU%2CBY%2CKZ&w=le35&plate=third&locale=ru" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"https://openvan.camp/api/visa/route"
);
const params = {
"t": "RU,GE,TR",
"p": "RU,BY,KZ",
"w": "le35",
"plate": "third",
"locale": "ru",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());import requests
import json
url = 'https://openvan.camp/api/visa/route'
params = {
't': 'RU,GE,TR',
'p': 'RU,BY,KZ',
'w': 'le35',
'plate': 'third',
'locale': 'ru',
}
headers = {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers, params=params)
response.json()Example response (200):
{
"success": true,
"data": {
"legs": [
{
"code": "GE",
"name": "Georgia",
"slug": "georgia",
"flag": "ge",
"zone": null,
"members": null,
"rows": [
{
"passports": [
"RU",
"BY"
],
"names": [
"Russia",
"Belarus"
],
"mode": "visa_free",
"mode_label": "visa free",
"days": 365,
"days_label": "365 d.",
"duration_label": "1 full year",
"total": null,
"window_label": "resets on every entry",
"visa_run": true,
"confidence": "high",
"note": null,
"source_url": "https://matsne.gov.ge/en/document/view/2867361"
}
],
"vehicle": {
"max_days": 90,
"days_label": "90 d.",
"basis_label": "on every entry",
"green_card_label": "insurance is bought at the border",
"confidence": "high",
"source_url": "https://georgiacb.com/en/articles/customs-procedure-temporary-importation-admission-im-53",
"note": null
},
"registration_hours": null,
"warnings": null
}
],
"bottleneck": {
"code": "GE",
"name": "Georgia",
"kind": "vehicle",
"days": 90
},
"passports": [
{
"code": "RU",
"name": "Russia"
}
]
},
"meta": {
"route": [
"RU",
"GE",
"TR"
],
"weight": "le35",
"assumed_passports": false
}
}
Example response (422):
{
"success": false,
"error": "Parameter \"t\" must list at least one known destination."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Passport ranking by destinations reachable without a prior visa.
Score = visa free + visa on arrival + eTA. The breakdown is returned alongside so the number can be checked rather than taken on faith. Ties share a rank.
Example request:
curl --request GET \
--get "https://openvan.camp/api/visa/rank?limit=10&locale=en" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"https://openvan.camp/api/visa/rank"
);
const params = {
"limit": "10",
"locale": "en",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());import requests
import json
url = 'https://openvan.camp/api/visa/rank'
params = {
'limit': '10',
'locale': 'en',
}
headers = {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers, params=params)
response.json()Example response (200):
{
"success": true,
"data": [
{
"rank": 1,
"passport": "SG",
"name": "Singapore",
"score": 168,
"visa_free": 130,
"visa_on_arrival": 30,
"eta": 8,
"e_visa": 20,
"visa_required": 10
}
],
"meta": {
"total": 199,
"scoring": "visa_free + visa_on_arrival + eta"
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Change log of visa and vehicle rules.
One row per changed field, written both by imports and by editors. Commercial passport APIs charge for rule history; this one is open.
Example request:
curl --request GET \
--get "https://openvan.camp/api/visa/history?subject_type=requirement&subject_key=RU%3ETR&since=2026-01-01&limit=50" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"https://openvan.camp/api/visa/history"
);
const params = {
"subject_type": "requirement",
"subject_key": "RU>TR",
"since": "2026-01-01",
"limit": "50",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());import requests
import json
url = 'https://openvan.camp/api/visa/history'
params = {
'subject_type': 'requirement',
'subject_key': 'RU>TR',
'since': '2026-01-01',
'limit': '50',
}
headers = {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers, params=params)
response.json()Example response (200):
{
"success": true,
"data": [
{
"subject_type": "requirement",
"subject_key": "RU>TR",
"field": "days",
"old_value": "60",
"new_value": "90",
"source": "import",
"changed_at": "2026-07-27T10:00:00+00:00"
}
],
"meta": {
"total": 1
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
All destinations for one passport.
199 rows resolved through the full chain, sorted by destination name.
Counts by entry mode are in meta.
Example request:
curl --request GET \
--get "https://openvan.camp/api/visa/passport/DE?mode=visa_free&locale=en" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"https://openvan.camp/api/visa/passport/DE"
);
const params = {
"mode": "visa_free",
"locale": "en",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());import requests
import json
url = 'https://openvan.camp/api/visa/passport/DE'
params = {
'mode': 'visa_free',
'locale': 'en',
}
headers = {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers, params=params)
response.json()Example response (200):
{
"success": true,
"data": [
{
"destination": "TR",
"name": "Turkey",
"slug": "turkey",
"mode": "visa_free",
"days": 90,
"stay": {
"window": "rolling",
"window_days": 180,
"confidence": "medium",
"layer": "window"
}
}
],
"meta": {
"passport": "DE",
"total": 199,
"by_mode": {
"visa_free": 130
}
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Compact payload for colouring a world map by one passport.
Same data as /passport/{code} stripped to what a choropleth needs:
m — entry mode, d — visa-free days if known.
Example request:
curl --request GET \
--get "https://openvan.camp/api/visa/map/US" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"https://openvan.camp/api/visa/map/US"
);
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());import requests
import json
url = 'https://openvan.camp/api/visa/map/US'
headers = {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers)
response.json()Example response (200):
{
"success": true,
"data": {
"TR": {
"m": "visa_free",
"d": 90
},
"RU": {
"m": "visa_required",
"d": null
}
},
"meta": {
"passport": "US",
"legend": [
"visa_free",
"voa",
"eta",
"e_visa",
"visa_required",
"no_admission",
"self"
]
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Temporary vehicle import rules for a country.
Only rules we can stand behind are returned: an official source, or two independent research runs agreeing, or manual verification. A country with nothing trustworthy returns an empty list rather than a guess — the cost of being wrong here is a vehicle held at the border.
Example request:
curl --request GET \
--get "https://openvan.camp/api/visa/vehicle/georgia?locale=en" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"https://openvan.camp/api/visa/vehicle/georgia"
);
const params = {
"locale": "en",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());import requests
import json
url = 'https://openvan.camp/api/visa/vehicle/georgia'
params = {
'locale': 'en',
}
headers = {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers, params=params)
response.json()Example response (200):
{
"success": true,
"data": [
{
"weight_class": "le35",
"plate_group": null,
"max_days": 90,
"basis": "per_entry",
"window_days": null,
"carnet_required": false,
"extension_possible": null,
"green_card": "border_insurance",
"confidence": "high",
"source_url": "https://georgiacb.com/en/articles/customs-procedure-temporary-importation-admission-im-53",
"curated": true
}
],
"meta": {
"place": "GE",
"name": "Georgia",
"green_card_by_plate": {
"eu": "plate_ok"
}
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.