API Changelog
All notable changes to the Nakordoni Developer API. Newest entries first. We follow additive-only v1 stability — no breaking changes without a new API version.
Developer accounts linked to a Nakordoni Partners account with the same email can now submit news and manage their NakBus fleet (NakDriver / NakManager) from partners.nakordoni.eu, with team roles (Owner, Manager, Viewer) and one-click sign-in between the two portals. The developer-portal pages, API keys, fleet endpoints and apps are unchanged.
On Fuel Prices, updated_at and the response's own age_hours/stale flag were running on different clocks: updated_at moves only when a price actually changes, but staleness was computed as if it moved on every confirmation. 46.7% of the priced index showed age_hours ≤48 and stale:false while updated_at was in fact more than 48 hours old, 12,620 stations over a week old. Each price now also carries confirmed_at — the last time we confirmed the price, hour-floored, never earlier than updated_at — and age_hours/stale are computed from it. updated_at's meaning is unchanged: use it when you want to know when the price last moved, use confirmed_at when you want to know how current it is right now. Not present on the frozen v1 contract.
Two language bugs, both silent: sending the canonical grade code as lang=pl (or any of the other 24 site languages) got fuel_type_local back in English regardless — only 10 languages were ever checked against the label table, so anything outside that list fell through to the default UI vocabulary rather than the language you asked for. Separately, lang=tr (or several other valid codes) answered station listings in Ukrainian, because an internal default of uk fired whenever the requested language did not sit in that same 10-language list. Both are fixed: the language you send is now the language you get, across all 25.
Also: brand on Nearby Fuel Stations no longer returns the raw ingest sentinel OTHER for the ~330 stations where we do not know the brand — it is null, consistent with every other unknown field in the response.
Border AI Assistant (/api/v1/data/assistant) answered 503 internal_error — "Assistant temporarily unavailable" — when the same key asked the same question twice within five minutes. Nothing was unavailable: in most of those cases the answer was already computed and ready to be handed back. It is now returned normally, with ok: true and HTTP 200.
When the repeat arrives while the first answer is still being written, the call now returns 429 with error.code of duplicate_request instead of a 503, so a retry policy can tell "ask again in a moment" apart from a real outage. Both cases had also been counted against your account error rate as server errors; they no longer are. Nothing about the request changes — no parameter, no version. duplicate_request is listed with the other error codes in the reference.
Truck Parking (/api/v2/data/truck-parkings) returned entries with name of null and an empty address — near Bensheim, 20 of 50. Those are places we hold only as a coordinate, with nothing to render and nothing to match against your own POI set. They are no longer part of this product: it now serves named locations only, currently more than 22,000 across Europe. If you were filtering out unnamed entries yourself, that code is now redundant but harmless. Answers get shorter for the same radius and limit, and every entry that comes back is usable.
Separately, about 10,000 parkings carried a raw coordinate pair as their name, for example 51.927301,10.14112, while the real label sat in address. They now carry that label — Ionity, Seesen, Rest Area A5 E35 Kaelberpfad, Bensheim — everywhere they appear, including /api/v1/data/pois. The id of each place is unchanged, so a cached mapping stays valid; only the name is different.
snapshot.updated_at on /api/v1/data/queue and /api/v1/data/multi is local time in the checkpoint's own zone (for example Europe/Istanbul, Europe/Sofia, Europe/Budapest, Europe/Warsaw, Europe/Kyiv), and until now nothing in the response said which zone that was, so a caller could not resolve it to an instant. snapshot gains an additive timezone field (IANA name) alongside updated_at. No parameter, no version, no other field changes.
Every fuel endpoint used to describe its own coverage from a list written by hand: AT, DE, DK, ES, FR, HR, IT, LU, PL, PT, SI, with Poland qualified as the Tricity area. Both claims had gone stale. Coverage is now measured from the live station index and recomputed every six hours: 39 countries carry priced stations today, Poland among them nationwide rather than three cities. Nothing about a request changes: no parameter, no version.
Nearby Fuel Stations and Cheapest Fuel: when a search comes back empty, the coverage block now carries measured station_countries, station_counts, sparse_coverage and measured_at, scoped to the grade you asked for instead of to fuel in general. A country lands in sparse_coverage when we hold 25 priced stations there or fewer, which is a count rather than a judgement.
There is a new note for a grade we recognise but nobody prices where you asked. Until now coverage.fuel_type_note appeared only when the pump name itself was unknown to us. It now also appears when the name resolves correctly and there is simply no quote for it in that country, and it names the countries where that grade is priced and the grades we do price around you. The Czech pump name Natural 100 is the clean example: it resolves, and no feed quotes it in Czechia. An empty answer stops looking like a broken request.
Fuel Grades (/api/v2/data/fuel-grades) gains priced_countries and priced_station_counts for every grade, plus priced_here when you pass ?country=. The two lists mean different things: a country under countries is one where we accept that pump name, while priced_countries is where a feed actually quotes it, so priced_here of 0 is a real answer and not a gap in the response. The payload also carries coverage_measured_at and coverage_note, and its Cache-Control drops from 24 hours to 6 to match how often the rollup is recomputed.
More local pump names resolve as well, among them Klimadiesel 90 (HVO100) and HVO Diesel, Erdgas and Metano, Autogas and Autogaz, DEF for AdBlue, and a number of premium diesel and petrol brand names. The resolution order is unchanged and matching stays exact, so no name that worked before means anything different today, and a new name can only turn an empty answer into a priced one. The reference documentation and the endpoint descriptions were corrected in all 25 site languages at the same time.
Cheapest Fuel (/api/v2/data/fuel-cheapest) returned the nearest stations in distance order instead of the cheapest ones. Because the ranking was applied before the result was cut to your limit, the cheapest forecourts inside your radius could be missing from the answer entirely. Ranking is correct again: cheapest first for the grade you asked for, closest wins a tie, and a station with no quote for that grade ranks last. Nothing about the request changes — no parameter, no version.
The v2 fuel responses are also documented as they are actually served: stations arrive under data.stations[], one entry per physical station, with every grade nested in the prices object (price, currency, local_name, updated_at, age_hours, stale) plus station_ref, grades, total_found and notices. The reference for Nearby Fuel Stations and Cheapest Fuel still described the older flat data.data[] row list.
Two add-ons are available on the Billing page (monthly tab) on top of any plan, without changing it: Extra forecast calls — +100 forecast and statistics calls a day per block, €2 a month per block, up to 10 blocks; and Extra countries — +1 declarable country per unit, €2 a month each. Changing a quantity shows an exact prorated quote before anything is charged.
From 10 November 2026 each plan includes a set number of declared countries: Explorer and Student 4, Starter 10, Pro and above unlimited. From that date a declaration longer than plan + purchased extra countries cannot be saved; the Account tab shows your allowance now, and accounts already above it see a suggestion on the dashboard. Nothing changes before 10 November.
The API sandbox now labels every endpoint in the picker with its quota class (Heavy / Standard), shows the quota cost for the selected version before you run anything, and after a call shows what that same call would have cost against your live quota — including the ceil(N ppids × M sub-products / 2) formula used for /multi-shaped calls.
This is a read-only preview: sandbox calls themselves are drawn from your separate sandbox test budget, never your live quota.
From today, anything we have publicly announced as retiring is closed to developer accounts created on or after the announcement date. If your account existed before the announcement, nothing changes — you keep the full grace period, right up to the retirement date given in the entry that announced it.
Why this rule exists. On 24 August 2026 we announced that truck-bans v1 retires on 8 September 2026. Two accounts registered days after that announcement, built their integration on v1, and came within hours of a 410 no email of ours had ever reached them: the announcement and the notification batch both predated their signup. Nothing in the API stopped them adopting a version we had already said was going away. That was our failure, and this is the fix — you cannot newly adopt something already scheduled to be removed.
What it looks like. Such a call is refused with 410 Gone and error code version_closed_to_new_accounts. The message names the retirement date, the announcement date and the version to use instead. It is deliberately a different code from version_sunset, which is what every account gets once the retirement date itself has passed — support can tell "you arrived too late to start" from "this is gone for everyone" without reading a log.
Grandfathering is by account creation date, not by first call. If you registered before the announcement but only start integrating now, you still get the full grace period: you may have been building against it all along.
In effect now for truck-bans v1 (announced 24 August 2026, retires 8 September 2026), and automatically for every retirement we announce from here on. Nothing new is required of you: every response on a retiring version already carries Deprecation, Sunset and Link: rel="successor-version" headers, so a new integration can see a retirement coming without reading this page.
Follow-on to yesterday's v4 change (dev ticket #105). v1 and v2 comma-separated destination lists keep working, but are now gated on the same clock as destination=all: both stop on 2026-10-06 (Deprecation/Sunset headers until then, 400 destination_list_removed after, naming /api/v4/ as the replacement). The existing cap-10 check on comma lists is unchanged before that date.
v4 stays one destination per call — that did not change today. What changed is only how v1/v2 are messaged: the destination=all 400 no longer suggests a comma list as a migration path (it would die on the same date), it now points straight at v4.
Docs fix: the v4 example on this site previously read /api/v4/data/border/1/2,3,4/9 — a comma list, which v4 rejects. It is now /api/v4/data/border/1/2/9. Anyone who copied the old example would have gotten a 400 on their first call; sorry.
New translated key product_border_v4_p_destination ships in all 25 site languages, stating the v4 one-destination rule explicitly rather than falling back to the v1/v2 wording.
/api/v4/data/border/{origin}/{destination}/{crossing_type} is live today. Three things change from v2, and together they are why it is a new version rather than an edit.
1. No more destination=all. Our data is licensed per country (Developer API Terms, section 7), and a wildcard that expands to "every neighbour we hold data for" returns countries your account may not be approved for — with nothing in the request to show it. In v4 you name the country.
2. One destination country per call. /api/v4/data/border/1/2/9 asks for one border. Comma lists are not accepted: send 1/2/9, 1/3/9 and 1/4/9 as separate calls. A comma list or all answers 400 and names the exact calls to send, so nothing fails silently.
3. One truck code. v1 and v2 split freight into 8 (Freight Transport) and 9 (Freight Transport up to 7.5 t). That split is real at the crossing, but no integrator can act on it: asking v2 for 9 on the UA-PL border returned 21 of the 70 truck crossings and nothing said so. v4 answers 9 with every truck lane, and accepts 8 as an alias of 9. Each row carries its own crossing_type, so a merged answer stays inspectable.
In v1 and v2, destination=all keeps working until 2026-10-06 and carries Deprecation / Sunset headers until then. From that date those versions answer 400 for all as well — the rest of v1 and v2 is untouched and stays available. The same date applies to the other all-countries shortcuts: travel-matrix without ?dest=, bus-carriers with ?ppid=all, and fuel-grades without ?country=.
v3, announced earlier today, is superseded by v4. v3 differed from v4 only in still accepting a comma list, and no integration uses that shape. v3 URLs keep answering so nothing written against them breaks, but v3 is not documented and will not be developed further — migrate to v4.
Everything else in v4 is v2: directional path order, direction{from,to}, stale, and ?max_age_min=.
The numeric ids in /border/{origin}/{destination}/{crossing_type} were never published as a table, so integrators reconstructed them from timezones and sample URLs. They are now in the docs under Country and vehicle-type codes, rendered from the same tables the API validates against — country ids with the borders each one expands to, and every crossing_type with the label the API returns.
While publishing them we found the sandbox and the endpoint metadata describing 8 as "truck<7.5t" and 9 as "truck". That is inverted: the API labels 8 Freight Transport and 9 Freight Transport up to 7.5 tons, and always has. If you picked a truck code from the parameter hint, you were filtering the opposite lane to the one you meant. Corrected everywhere, and v3 removes the choice entirely.
API Terms v1.1 replace v1.0 before it took effect, and apply from 2026-10-06. Please accept them in your dashboard.
Section 7 now says what a Market is: the country whose data you use — where the checkpoint or border you request is — not the country your users live in. Our dashboard had said both things in different places; the enforcement always meant the first.
Two changes in your favour. Countries already approved for your account stay usable while a later change is under review (adding a country no longer suspends the ones you have). And if we have not answered a market declaration within 5 business days, your full plan limits apply until we do.
Section 10.3 now matches what the dashboard actually asks for, and section 13.2 states an availability basis we measure and can show you.
Three products now carry an additive data_quality field (high or low) marking whether a reading is a real observation or a model estimate with no live counting source at that crossing: queue (on the top-level snapshot and on each historical row in data[] — absent on forecast rows), update-info (on the envelope), and multi (on both the queue and update_info sub-objects per checkpoint). This is not a new signal — the underlying flag already existed internally — but it was never exposed, so a fully modelled checkpoint looked identical to a directly measured one. is_realtime is intentionally unchanged: it still reads true for modelled rows, and changing that meaning is a v2-level breaking change we are not making here.
Also from this release: the queue-advanced product no longer redistributes raw upstream weather. weather_main, temperature and wind_speed are replaced by a derived condition_code (0–5 hazard scale, null when no weather is available), condition and severity.
From 2026-08-30 one /api/v1/data/multi request is answered for at most 5 checkpoints. A call that lists more PPIDs is not rejected: it still returns 200, but only the first 5 IDs in ?ppids= are answered. The remaining IDs are ignored, echoed back in meta.ppid_cap.ignored, and are not charged against your quota — the call is billed on what it actually returns.
While a call is over the limit the response carries an X-Devapi-Warning: multi_ppid_cap header and a meta.ppid_cap block with cap, enforced_from, enforced, ppids_asked, ppids_answered and ignored[]. Until 2026-08-30 those fields appear with enforced: false and the full result set, so you can see the change coming in your own logs.
The quota discount of one half is unchanged. Split your checkpoints into groups of 5 and send one call per group on your normal refresh cycle; for frequent polling of queue length and freshness only, update-info stays the cheaper standard-class product.
Ukraine’s computed heat ban — returned with include_ua_heat, and automatically for country=UA — now answers for the date window you ask for. It previously returned the coming seven days whatever date_from and date_to said, so a December window quietly came back with this week’s rows. The ban is computed from a weather forecast rather than read from the ban calendar, so it has two edges the calendar does not: it cannot look backwards, and it stops where the forecast stops. Your window is now intersected with what the forecast actually covers, and a new ua_heat_ban.forecast_horizon field names the last date it reaches. A window beyond that horizon returns no heat rows and says why in summary — which is not the same as “no ban”. v1 responses are unchanged.
Response shapes were previously undocumented — the only way to learn what a product returned was to call it. Every product page now shows a Response fields table underneath its parameters table, listing each field with a short description; list-element fields are shown as items[].name, envelope-level fields (usage, meta, snapshot, resolved_location) are shown bare. 40 of 42 products are documented — the two products not yet launched (weather, road-quality) intentionally have none yet. The same table is published to our public GitHub docs mirror.
Every fuel product now accepts the local name of a fuel grade, not just our internal spelling: ON in Poland, Nafta in Czechia, Gázolaj in Hungary, Motorină in Romania, ДП in Ukraine, Motorin in Turkey, Gasóleo in Portugal and Spain. Names are resolved country-first, because the same wording is not the same grade everywhere — “95” is E10 at a Danish pump and E5 at a Polish one — so send country together with a local name, or coordinates the point can be placed by. Responses echo fuel_type (canonical), fuel_type_requested (as you typed it) and fuel_type_local. A name we cannot place is never swapped for a default grade: the answer comes back empty and says so.
The full table is now a product of its own — GET /api/v2/data/fuel-grades[?country=PL][&fuel_type=ON] — listing our canonical grades and their local names in 41 European countries, including markets we quote no price for. The country and region tiers of fuel and fuel-local also gained a grades object mapping each price key to its grade and local pump name.
The truck-bans product now answers for a specific date or date range on /api/v2/data/truck-bans. Until now it always returned the coming 7 days and ignored any date you sent, so building a calendar meant one request per day — and on a two-requests-per-second plan most of those are rejected with 429 qps_exceeded.
Use ?date=YYYY-MM-DD for one day, or ?date_from= and ?date_to= for a range. Both ends are inclusive and either may be omitted: the start defaults to today, the end to the start plus 7 days. A window may cover at most 92 days — a longer one is refused with 400 date_range_too_long instead of being quietly cut. This is a forward-looking calendar: a window may start at most 7 days in the past, and anything older is refused rather than served — coverage runs forward to 31 December 2028 across 23 countries.
Every response now carries a window object naming the exact range it covers. It is additive and is sent on v1 as well, and v1 keeps its fixed 7-day window unchanged. Note that include_ua_heat always covers the coming 7 days whatever window you ask for — it comes from a weather forecast, not from the ban calendar. Remember that v1 of this product retires on 8 September 2026.
Two related improvements across the whole API: any parameter a product does not accept is now listed in ignored_params on the response instead of being dropped in silence, and validation errors from the data service now reach you as written, with the machine-readable token in error.reason.
Three response-quality fixes from a gateway audit (ticket #43).
The X-API-Key header is now accepted alongside Authorization: Bearer and ?key=. If your HTTP client sends keys via a header named X-API-Key, that now works — previously it was silently ignored and the call was rejected as missing_api_key. Authorization: Bearer remains the documented, recommended form.
The missing-key error message now names all three ways to authenticate (Bearer header, X-API-Key header, or ?key=) instead of only linking the signup page.
The checkpoints directory now carries has_day_stats on every row — an additive boolean telling you whether the Best Time to Cross (day-stats) API has data for that checkpoint. Day-stats only exists for a subset of monitored checkpoints; check this flag before polling to avoid predictable 404s. Existing fields are unchanged.
Also corrected in the docs: the road-conditions product always honoured a lang parameter for label localisation — it just was not listed.
Two fixes and one new version for the truck-bans product.
Comma-separated countries now work. ?country= accepts a list of up to 3 ISO-2 codes, for example ?country=DE,RO. A longer list is refused with 400 too_many_countries rather than silently trimmed — this is a per-country ban calendar, not a bulk feed. This previously did not work: the separator was stripped, so DE,RO was read as the single token DERO, matched nothing, and returned success: true with total_bans: 0 — a confident “no bans” for two countries that between them had 22. If you worked around it by issuing one request per country, a single request now covers all of them and costs one call instead of several.
Responses now report their own completeness. Three additive fields — returned, total_available and truncated — tell you whether an answer was capped. An unscoped call in particular returns a capped slice, and until now nothing in the payload said so. total_bans keeps its existing meaning (rows in this response), so nothing you already parse changes.
v2 is scoped per country. On /api/v2/data/truck-bans, ?country= is required and an unscoped request is refused with 400 scope_required — this product is a per-country ban calendar, not a bulk feed. v1 is unchanged today — it still accepts an unscoped call and still returns the same capped 50 rows it always did, so nothing you have running breaks right now. v1 of this product retires on 8 September 2026. It serves normally through 7 September; from 8 September a v1 request is refused with 410 Gone and a message pointing at v2. Until then every v1 response carries Deprecation: true, a Sunset header with that date, and a Link header naming the successor version, so a client library can surface the deadline without anyone reading this page. To migrate: change the version segment to /api/v2/data/truck-bans and pass ?country=.
One documentation correction: the date parameter has been removed. It was listed for a long time but was never read by the service, so any request sending it silently received the default 7-day window rather than the day it asked for. To select a day, filter the upcoming_bans array by its date field. An ISO-3 code such as DEU also no longer resolves to a country name in the summary, where it produced the misleading “No truck ban data for: Germany.”
The truck-bans product now returns country-level driving restrictions for five more countries: Belgium (BE), Belarus (BY), Montenegro (ME), North Macedonia (MK) and Sweden (SE). Existing coverage for Bulgaria, Greece and Portugal has been extended and refreshed — Greek restrictions now run to September 2027, and Portugal is populated again.
The response shape is unchanged. New rows carry the same keys as every other ban: date, time_from, time_until, restriction_type, restriction_details, min_weight_tons and details_url. Where a restriction applies only under a condition — Belarusian summer bans apply above 25 °C, for example — that condition is stated in restriction_details, so read this field before warning a driver. min_weight_tons is null when a rule targets a transport class (dangerous goods) rather than a tonnage.
The fuel-stations and fuel-cheapest products now return station-level prices in Poland. Coverage is partial — the Tricity area (Gdańsk, Gdynia, Sopot) — so Poland is reported in a new additive coverage.sparse_coverage array alongside the existing coverage.station_countries list. A country listed in sparse_coverage has station data for part of its territory only; a query elsewhere in that country returns an empty list together with the coverage note, exactly as before. Polish prices are quoted in PLN.
The bulk-query error is clearer too: when lat is missing, the scope_required message now points at the fuel product (?country=XX) for country-wide average prices.
GET /api/v2/data/fuel-local?lat=&lon= now resolves down three tiers instead of two: station, then region, then country. The new middle tier exists for Ukraine, where per-station prices exist nowhere: a Ukrainian point now answers with the average for its oblast instead of the national average, and falls back to the national average only when the oblast is not quoted.
A response from the region tier carries the oblast code (an ISO 3166-2 value such as UA-46), region_name and region_center_dist_km, plus the same price keys as the country tier. Keep branching on resolution, never on the shape of the response; station and country answers are unchanged.
New endpoint GET /api/v2/data/fuel-local?lat=&lon= returns the best available fuel price for any point in Europe. It answers with pump prices from the nearest stations where station-level data exists, and falls back to the national average of the country the point falls in — including Ukraine, where per-station prices exist nowhere.
Every response carries a resolution field naming the tier that answered: station (a list of stations with distance_km, each in its own currency) or country (one national-average object). Branch on resolution, never on the shape. Available from /api/v2/ onwards; fuel, fuel-stations and fuel-cheapest are unchanged.
The fuel-stations and fuel-cheapest products now cover far more stations in Germany, with prices refreshed throughout the day — rural areas included. The fuel_type parameter accepts 13 fuel types: diesel, e5, e10, superplus, super100, premdiesel, truckdiesel, hvo, lpg, cng, adblue, e85 and lng. When no station matches a query, the response includes a coverage object listing the countries with station data.
The radius= parameter is now accepted as a compatible alias for radius_km on every product that documents it. The fuel-stations and fuel-cheapest products return an additive coverage object (station-country list plus a note) instead of a silent empty result when no stations match. route-plan border objects now include an additive wait_basis key (car_lane vs vehicle_lane) so clients can tell when truck wait data is a car-lane stand-in. Truck border matching along a route is substantially more accurate: car-lane fallback for border pairs with no truck-lane data, a wrong-direction guard, a tighter distance gate, and de-duplication of same-position crossings. All changes are additive; no breaking changes.
The developer landing page now has anchored sections (#products, #plans, #quickstart, #integrations, #datasets, #apps, #companies, #showcase) with a jump navigation, and every product card links to its own docs page. New Mobile apps section presents Kordon Online and Truck Bans with Google Play links. Translation backfill: billing history, login errors, sandbox links and the plan-selection button are now localized in all 25 languages.
Fleet beacon response (POST /api/v1/fleet_position.php) now includes a messages array delivering pending owner→driver messages. New owner-only live JSON feed (?ajax=live) and a "Messages to drivers" card on the fleet dashboard. New driver invite landing page /{lang}/get-nakbus (25 languages).
Localized product_fleet_vehicles/live/history title, desc and fleet-history params across all 25 dev-portal languages.
/api/v1/data/truck-bans now returns the same set of top-level fields regardless of which query triggered the response. Previously a query for a country with no calendar bans, an unrecognized ppid, or a normal database match could each omit different fields (e.g. country, covered_countries, ppid). Every response now consistently includes as_of, bans_by_country, countries_not_covered, country, covered_countries, current_bans, is_ban_active, lang, page_url, ppid, relevant_countries, source, success, summary, total_bans and upcoming_bans (null or empty where not applicable), simplifying client-side parsing.
Nine new per-service products. The location ones accept lat/lon or city + country (we geocode the city for you): /api/v2/data/truck-parkings, /api/v2/data/shops, /api/v2/data/showers, /api/v2/data/restaurants, /api/v2/data/industrial, /api/v2/data/fuel-stations, /api/v2/data/fuel-cheapest (stations ranked by price for a fuel type) and /api/v2/data/internet-points; results carry distance_km and are bounded by radius. /api/v2/data/vignettes answers whether a country requires a vignette, with current prices. The existing pois product now honours lon and radius as documented, and the fuel product's mode=nearest accepts lon too. All nine are available in the sandbox.
Each ban in /api/v1/data/truck-bans now includes restriction_type (General / Local / Sunday / Holiday / Seasonal), restriction_details (exact scope or roads affected) and min_weight_tons. details_url now points to per-country pages on nakordoni.eu. A new optional lang parameter selects the language of country names and the summary; the default is now English.
A malformed ?ppid= now returns the real reason instead of a bare "Request failed": the error names the parameter, the expected id_<number> format and points to /api/v1/data/checkpoints. The parameter tables for stats, forecast, update-info, weather and bus-carriers now show the id_13 example in all 25 languages.
The redesigned portal shell (top bar, icon sidebar, KPI dashboard, card-based layouts) is now the default experience for all logged-in developer accounts, brought forward from the planned 10 August rollout date. Use ?v=1 to switch back to the classic layout at any time.
All developer portal pages — landing, docs, dashboard, AI Studio, sandbox, tickets, requests, export, fleet, news, changelog, and account pages — no longer load any ad scripts or ad slots. This applies site-wide across the portal, not just login/signup as before.
Plan a whole border trip in one call: /api/v2/data/route-plan returns the route, the crossings actually on it with a live queue or a forecast for your arrival time, and the stops a driver really makes — rest breaks, a meal, refuelling — on a single timeline.
The border is part of that timeline. A long queue counts as the break that was already due and resets the driving clock, so a three-hour wait is never reported as three hours plus a full set of breaks nobody took. Cars follow a driving-hygiene model; buses and trucks get the mandatory EU 561/2006 rest, and bus service overhead is calibrated on more than 1000 licensed international coach schedules. Add stop_places=1 to name a real rest area or fuel station for every stop, and via=lat,lon to route through a different crossing.
New in the portal menu: Presentation — a live, always-current pitch of the nakordoni data platform personalized for your market (insurance, travel, logistics, carriers, media, navigation, fuel, fintech, public sector or personal projects). It shows real 30-day platform volumes, your own API usage, response-time and limit statistics, and a plan recommendation when your calls hit the free-tier limits. Pick or confirm your market(s) on the page, in your profile — or during signup. It opens automatically on your first visit; you can turn the auto-open off on the page itself.
If an assistant has a feed enabled but the call does not carry the context that feed needs — queue without a ppid, for example — the feed is now skipped before any request is made and is not charged. Previously it was called anyway, failed, and still cost a unit. The studio shows what each feed needs, recalculates the price as you fill the context in, and marks results ✓ ran / ⊘ skipped, not charged / ✕ failed; the API returns data.feeds_skipped telling you exactly which parameter to pass.
Answers no longer mention feeds, data sources or anything technical: a missing feed is at most one plain sentence to the end user, never an internal name. Feeds with only optional filters (such as fuel narrowed to a country we have no data for) now fall back to the broad dataset instead of returning nothing.
New: /{lang}/developers/studio. Build an AI assistant that answers from your content and our live border data. Give us your markdown, or just name the pages and we fetch and index them — you only ever maintain your own files. Pick which of our feeds it may use (queue, forecast, alternatives, day-stats, fuel, truck bans, trading Sundays, holidays, road conditions, bus carriers, POIs, currency), pick a model tier (fast / balanced / pro — that is what sets the price), write your own instructions with {{feed.slug}} placeholders saying exactly where our data lands in the answer, and add a closing sentence of your own that is appended to every reply. Ready-made blueprints: personal travel assistant, work/freight assistant, insurance & Green Card sales assistant.
Test it in the studio (30 answers/day, separate from your API quota), then call it in production at GET /api/v2/data/assistant-custom?assistant_id=N&q=…. Price per answer = model tier units + 1 unit per enabled feed, returned in X-Devapi-Units. The product is v2-only — a v1 URL returns unsupported_version. The existing assistant product is unchanged.
Every assistant runs under a platform content policy that outranks your instructions: no impersonating officials, no help evading border or customs control, no invented numbers, no profanity. Instructions and answers are both screened; blocked calls are logged.
New: a real MCP server at https://nakordoni.eu/mcp, exposing a safe read-only subset of the API (status, checkpoints, border queue, live queue, forecast) as MCP tools. Same API key and quota as the REST API. Server card at /.well-known/mcp/server-card.json. See the MCP Server section in the docs.
The retitle to "Live Queue & Freshness API" below did not actually reach the docs page. The page renders each product title through a translation lookup that falls back to the endpoint's title only when no translation exists — and a translation already existed, frozen at the old name, in all 25 UI languages. It now wins over any future update to the underlying title until it is updated too.
Retitled the translation key in all 25 languages so the docs page matches. No endpoint, parameter or response change — title text only.
If you poll live queue data frequently, you may be spending heavy quota you do not need to. /update-info is standard-class and already returns the live figure:
GET /api/v1/data/update-info?ppid=id_13
It returns queue_now, freshness, age_minutes, is_realtime, status, timestamp and timezone. Use it for the frequent refresh against your standard daily quota, and keep /queue, /multi and /forecast (all heavy-class) for when you need wait_min, the trend fields or history.
Nothing changed in the endpoint itself — only its documentation. It was listed as the "Data Freshness API" and its description mentioned only the freshness rating, never queue_now, so it was easy to miss. It is now titled "Live Queue & Freshness API" with the returned fields spelled out. Thanks to the developer who raised this.
Some failed requests were returning HTTP 200 with ok: true and the error buried inside data — so the documented if (!ok) throw pattern could not detect them, and the call was still billed. Affected calls now return HTTP 400 with ok: false and a proper error.code / error.message, as documented. Seen on fuel-cities with an unsupported country and travel-matrix with malformed coordinates.
Separately, a missing required parameter returned 500 internal_error instead of 400 bad_request (an upstream 4xx body was being discarded before its status was read). It now returns 400 bad_request with the upstream message — e.g. search without ?name=.
Successful responses are byte-for-byte unchanged — same fields, same params, same quota cost. If your client already branches on ok, no change is needed. If it ignored ok and read data directly, it will now see error envelopes on calls that were always failing.
Fixed a bug where /multi could return a wrong queue count for some checkpoints — mainly Balkan and Hungary–Serbia crossings — whenever its cache was cold. The fallback read a table that, for those crossings, holds no queue data, and reported unrelated values as car counts. Measured examples: a checkpoint with 12 cars reported 6, and several with real queues reported 0.
Three changes you may notice:
found: falsenow means there is genuinely no recent queue data. Previously you could receivefound: truewith a fabricatedqueue_now: 0.wait_status,trend_percentandtrend_directionare now returned on cold requests — they werenullbefore.- The endpoint also falls back when its cached snapshot is stale (older than 24h), not only when it is missing.
No changes to request parameters, quota cost or response shape.
Fixed a bug where every /multi call was billed twice — once by a generic 1-unit check and again by the endpoint's own variable-cost (N PPIDs × sub-products) formula. Calls now cost exactly ⌈(N×M)/2⌉ units as documented, with no extra charge on top.
Also added a Standard/Heavy quota-class badge to each product on the docs page, so it's clear at a glance which daily quota an endpoint draws from.
country and countries merged into one param (1-15 comma-separated codes). New compare_to param: same-vs-different holiday comparison across countries, composes with upcoming+days. lang now accepts multiple languages (adds a names object). days=0 or omitted now means no limit in upcoming mode.
Official public holidays per European country — dates, local names and type. Backed by the same Nager.Date / OpenHolidaysAPI service (with a locally-computed Kosovo calendar) that powers the nakordoni.eu holiday calendar page and the prediction system's calendar factors.
?country=PL&year=2026— full-year holiday list for one country?upcoming=1&days=30— flat list of upcoming holidays across countries- No params — index of a core country set with each next holiday
Added the currency product — EUR-based exchange rates for PLN, CZK, HUF, USD, GBP, CHF, NOK and UAH, sourced from Frankfurter (ECB) and cached 6 hours. No parameters, always returns the full rate table. See docs.
Embed live European truck driving bans on your own website — a free iframe widget with 3 designs (light, dark, board), 5 languages (en, uk, pl, de, ru), an optional per-country filter and live «active now» status. No API key needed. Configure and copy the code at nakordoni.eu/en/for_truck_drivers/traffic_bans/widget. Prefer raw data? The truck-bans API product and the public JSON feed remain available.
border, and an interactive Sandbox
Three additions, all backward-compatible — v1 is unchanged.
Per-endpoint versioning. There is now an /api/v2/ base URL. It is per-endpoint: only endpoints that actually changed behave differently under v2; every other endpoint transparently serves its v1 response (so /api/v2/data/queue = the same data as v1, just with "api_version":"v2"). No need to migrate endpoints that work.
border v2 is directional. The path order is the travel direction:
GET /api/v2/data/border/1/2/6 → buses UA→PL (Ukrainian-side crossings) GET /api/v2/data/border/2/1/6 → buses PL→UA (Polish-side crossings)
Each checkpoint also gains a direction {from,to} object and a stale boolean, and ?max_age_min=N returns only recently-updated crossings. (v1 border still returns both sides of the border regardless of order — unchanged.)
Interactive Sandbox. Signed-in developers can now try any endpoint from the browser at Developers → Sandbox — pick an endpoint, version and one of your keys, tweak parameters and see the live response. Sandbox testing has its own separate daily budget (50 calls/day) and never touches your live API quota.
The docs are now split per endpoint (Developers → API Docs) with a version selector on endpoints that have more than one version.
queue-advanced: two new adjustment factors
Two new factors layered into the wait-time formula, alongside the existing section_mode and weather adjustments:
service_rate— measured cars/min currently being processed vs the checkpoint's configured baseline rate. Multiplicative, bounded 0.5x-1.5x.shift_change— impact of the checkpoint's own local 08:00/20:00 border-guard shift change. Additive (minutes), not multiplicative — only applied within +/-60 minutes of a shift, requires a minimum sample history, clamped to +/-120 minutes.
advanced_wait_min is now round(base_wait × section_mode × weather × service_rate) + shift_change.adjustment_min. Both factors are also reflected in driver_reported.prognosed_advanced_wait_min for historical comparisons.
queue, border, multi, update-info
As part of a security/privacy review, the following fields have been removed — they exposed internal implementation details (our upstream data-source taxonomy, DB row IDs, internal pipeline annotations, unused/dead fields) with no real product value:
idandcorrected— removed fromqueuerow objectssource(raw string, e.g."line") — removed fromqueue,multi, andupdate-info.update-infoandmulti'supdate_infoblock still carrysource_category/source_label_en(a small public vocabulary);queueandmulti'squeueblock no longer carry any source field at alltraffic_status— removed fromborder; it was alwaysnulland never populated by any part of the system
If your integration reads any of these fields, please update it — see the current field list on the relevant product's docs page.
usage.used can now be a fractional number
Daily quota usage (usage.used in every response) can now be a decimal value (e.g. 67.5) instead of always a whole integer. This is a side effect of queue-advanced being billed at a fractional rate — see below. usage.limit is unaffected and always a whole number. If your client strictly types usage.used as an integer, please widen it to accept a decimal/float.
wait_status and trend_percent/trend_direction added to border, multi, and queue-advanced
These three products now return the same live-status fields the website shows: wait_status (green/yellow/red, based on this checkpoint's own recent history) and trend_percent/trend_direction (up/up-slight/down/down-slight/stable, comparing the last 3 hours). Purely additive.
queue: wait_time now populated on every historical row
/api/v1/data/queue's data[] rows previously had wait_time: null for most sources — only a few upstream feeds report a wait time directly. Rows without one now carry our standard estimate instead, flagged with a new wait_time_estimated boolean so you can tell a real reported figure from a computed one.
queue-advanced: billed at 1.5x, response trimmed
queue-advanced now costs 1.5 units per call instead of 1 (reflecting the extra traffic/weather/driver-report lookups it does) — see usage.used above. The response also no longer includes the internal model constants or total_crossing_time, and driver_reported is now just {wait_min, ts, age_min} — the previous prognosis-vs-reality comparison fields (prognosed_wait_min, diff_min, historical_section_mode, historical_weather, etc.) have been removed. section_mode, weather, advanced_wait_min, and exceeds_crossing_time are unchanged.
active_window / next_window)
/api/v1/data/truck-bans now returns, for each country in bans_by_country, a status (active/clear) plus active_window, next_window, local_time and tz — computed in that country's own timezone, so you no longer have to evaluate raw ban windows against a clock yourself. The response also adds a top-level covered_countries list and an as_of UTC timestamp.
GET /api/v1/data/truck-bans?country=PL
Purely additive — the existing current_bans/upcoming_bans/bans_by_country fields are unchanged. An unknown ?country= now returns an empty result with countries_not_covered instead of every country's bans.
queue-advanced)
A new opt-in product that adjusts the standard wait time for live traffic flow and weather. Returns the full breakdown of each adjustment.
GET /api/v1/data/queue-advanced?ppid=id_13
Granted on request — open a Data ticket from your dashboard to enable it.
/api/v1/data/border now correctly computes wait_min for every checkpoint in the response, matching the queue and multi products. Previously this field was always null.
/api/v1/data/forecast now reliably uses the v4 ensemble model for any prediction_steps value (previously some non-standard horizons could silently fall back to an older model). The weather factor that feeds the ensemble is also fixed and now genuinely reflects live conditions (rain, snow, wind, fog) instead of always reporting unavailable.
Approved developers can now download hourly-averaged historical border-queue data for up to 5 checkpoints (rolling window up to 90 days) as CSV or NDJSON from the new Data export tab. Data is published-only and quality-checked; timestamps are UTC. Need access? Open a Data ticket.
No website yet? You can now create a developer account by describing where and how you plan to use our data, instead of being forced to enter a live page URL. Add the real URL later from your dashboard (Account & data → Your project) as soon as your site or app is live — a visible link back to nakordoni.eu on that page is required by our Terms.
Developers can now submit their own border-related news to the Nakordoni news line. If our editors publish it, you get an indexable dofollow backlink to your service (publisher byline + source line) and we translate the article into all 24 languages for free.
One article per week is free; additional articles are a paid add-on. Choose 'we may lightly edit + add internal links' or 'publish as-is'. Submit and track review status under Developers → Submit news.
The Multi-Checkpoint API (/api/v1/data/multi) now bills quota at ⌈(N PPIDs × sub-products) / 2⌉ — half the cost of equivalent individual calls. A request for 10 checkpoints with both sub-products now costs 10 units instead of 20. The X-Devapi-Units header and meta.units_consumed in the response reflect the discounted amount.
multi)
Fetch live queue status and data freshness for up to 20 checkpoints in a single API call — designed for dashboard builders who currently poll many PPIDs in a loop.
Quota counts fairly as N PPIDs × sub-products requested, so the total usage is identical to individual calls — but with one round-trip instead of many. GreenTravel-style patterns drop from 24+ calls/hour to 2.
GET /api/v1/data/multi?ppids=id_2,id_13,id_15,id_59&include=queue,update-info&lang=en
include=queue— current queue_now, estimated wait_min, data age and checkpoint nameinclude=update-info— data freshness, source classification, age in seconds/minutes- Max 20 PPIDs per request; combine both sub-products in a single call for full dashboard data
- Response includes
meta.units_consumedso you can track quota usage precisely
The queue product response now includes a top-level snapshot object with the latest real-time data and a computed prognosed wait time — the same estimate shown on the nakordoni.eu hero section:
snapshot.queue_now — current cars in queue snapshot.wait_min — prognosed wait time (minutes) snapshot.updated_at — when the queue data was recorded snapshot.age_min — minutes since last update snapshot.source — data source identifier
The data array (historical entries) is unchanged — this is a purely additive addition. Clients that do not read snapshot are unaffected.
border)
Query all checkpoints on a given border + vehicle type in a single call instead of making one request per PPID.
GET /api/v1/data/border/{origin}/{destination}/{crossing_type}
- Supports a single destination country, a comma-separated list, or
allto expand to every monitored neighbour at once. - Results sorted by
queue_nowascending (shortest queue first). - Fully localized: add
?lang=uk(or any of our 22 supported languages) to get checkpoint names in that language.
search)
Discover checkpoint PPID values by name without browsing the full directory.
GET /api/v1/data/search?name=Krakovets,Shehyni&lang=en
- Accepts a single name or comma-separated list (up to 20).
- Searches across all 24 translation languages — pass a name in Ukrainian, Polish, German or any supported language and it will match.
- Returns all PPIDs at that location grouped by vehicle type (car / bus / pedestrian / truck).
crossing_type override
The alternatives product now accepts ?lang= in all 22 supported languages (was only 12).
New crossing_type parameter lets you override the vehicle type filter — e.g. pass crossing_type=4 to get car alternatives even when querying from a bus PPID.
The crossing_type_label field in checkpoints, border, and search responses is now translated to the requested language in all 22 supported languages. Country name fields (origin_name, destination_name) follow the same locale.
The Nakordoni Developer API portal is live at /en/developers. Sign up for a free Explorer key (200 requests/day) to access border queue data, forecasts, fuel prices, driver POIs and more.
Products available at launch: checkpoints, queue, stats, day-stats, forecast, alternatives, update_info, fuel, pois, truck_bans, trading_sundays, bus_carriers, road_conditions, assistant.
This changelog covers public API changes. Internal infrastructure updates are not listed.
Updated