# Navo Warehouses API (wh.navo24.com)

Public read-only JSON API over the warehouse offers shown on https://wh.navo24.com.
Built 14.11.2026 for the global warehouse directory on navo24.com and for embeds.

- Base URL: `https://wh.navo24.com`
- No key. `GET` answers carry `Access-Control-Allow-Origin: *` and `Cache-Control: public, max-age=300`.
- UTF-8 JSON. Errors: `{"error": {"code": "...", "message": "..."}}` with HTTP 400 / 404 / 422 / 429.
- Listings are demo offers generated at real logistics hubs (ports, airports, freight villages). Prices are indicative.
- Times are UTC ISO 8601 (`2026-11-14T12:00:00Z`), dates `YYYY-MM-DD`, areas in m2.

## GET /api/warehouses

List of offers. Expired offers are hidden unless `expired=1`.

| Param | Example | Meaning |
|---|---|---|
| `country` | `PL` | ISO 3166-1 alpha-2, case-insensitive |
| `city` | `Gdansk` | exact city name as in facets (accents/case ignored) |
| `type` | `cold` | type slug, see facets |
| `temp` | `frozen` | temperature slug: `ambient`, `chilled`, `frozen`, `multi`, `controlled`, `open` |
| `id` | `2582224` | ID or ID prefix; also finds expired offers |
| `q` | `gdansk bonded` | free text over ID, title, area, city, country, UN/LOCODE, type |
| `area_min`, `area_max` | `1000` | free area bounds, m2 |
| `service` | `Customs clearance` | repeatable; or `services=A,B`. All must match |
| `expired` | `0` (default), `1` (include), `only` | expired offers |
| `sort` | `country` (default), `newest`, `expires`, `area`, `price`, `id`; prefix `-` for descending |
| `limit`, `offset` | `100`, `0` | paging, limit 1..2000 (default 100) |

Response:

```json
{
  "updated": "2026-11-14T12:00:00Z",
  "total": 27, "limit": 100, "offset": 0,
  "items": [{
    "id": 4413020,
    "type": "cold", "type_label": "Cold store / reefer",
    "title": "Cold store unit, Port of Gdansk area",
    "subtitle": "Gdansk, Poland",
    "lat": 54.37, "lng": 18.66,
    "at": {"name": "Port of Gdansk area", "city": "Gdansk", "cc": "PL", "country": "Poland", "code": "PLGDN", "hub": "port"},
    "temp": "frozen", "temp_label": "Frozen -25 to -18 C",
    "area_total": 12000, "area_free": 1850, "pallets_free": 3900, "docks": 8, "height_m": 12,
    "services": ["Pallet storage", "Blast freezing"],
    "status": "space available",
    "offer": {"price": 11.4, "currency": "PLN", "unit": "pallet/month", "label": "from PLN 11.40 per pallet per month", "indicative": true},
    "available": "2026-11-20", "created": "2026-11-02T08:00:00Z", "expires": "2026-12-30T12:00:00Z",
    "expired": false,
    "url": "https://wh.navo24.com/warehouse/4413020",
    "photo": "https://wh.navo24.com/media/photos/cold-38531605.jpg"
  }]
}
```

`at.code` is the UN/LOCODE of the city or port when there is one, otherwise `null`. `at.hub` is the kind of
logistics area: `port`, `airport`, `rail`, `park`. `offer.unit` is `pallet/month` or `m2/month` (open yards).

## GET /api/warehouses/{id}

One offer with everything above plus `specs` (display key/value pairs), `certificates`, and
`photo: {src, source, license}` (Wikimedia Commons page and licence of the illustrative photo).
Expired offers are still returned with `"expired": true`. Unknown ID: 404 `not_found`.

## GET /api/warehouses/facets

Counts over active offers (`?expired=1` to include expired):

```json
{
  "updated": "...",
  "countries": [{"cc": "PL", "name": "Poland", "count": 27, "flag": "https://wh.navo24.com/flags/pl.svg",
                 "cities": [{"name": "Gdansk", "count": 4}]}],
  "types": [{"slug": "general", "label": "General / ambient", "count": 120}],
  "temps": [{"slug": "ambient", "label": "Ambient", "count": 300}],
  "services": [{"name": "Pallet storage", "count": 210}]
}
```

Type slugs: `general`, `cold`, `bonded`, `cfs`, `fulfilment`, `hazmat`, `yard`, `crossdock`.

## Deep links

- `https://wh.navo24.com/warehouse/{id}`: the offer card (server-rendered title, description, canonical, OG).
- `https://wh.navo24.com/?country=PL&type=cold&city=Gdansk`: the map with these filters. Same params as the API
  plus `view=table`.

## POST /api/apply (site form, not for bulk use)

Body `{warehouse_id?, name, email, company?, phone?, space?, unit?, date_from?, date_to?, message?}`.
Sends the request to the Navo24 team and a copy to the applicant. Rate limited per IP. Same-site use only.
