lat/lng are both given, ordering by distance instead of name and carrying distance_km on every row. Replaces GET /api/v1/utility/dive-centers, which returned every active centre unpaginated and leaked email in a searchable list - both fixed here (D-C3, S9 in docs/api/_alignment/contract-baseline.md).App\Http\Controllers\Api\V1\Public\DiveCenterController::index()api.v1.public.dive-centers.indexthrottle:public. See docs/api/README.md §14.GET/api/v1/public/dive-centers| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
search | string | No | none | Matches name, city, raid_id only - never email |
country | string(2) | No | none | ISO 3166-1 alpha-2, validated against the platform country list |
per_page | integer | No | 20 | Maximum 100 |
page | integer | No | 1 | |
lat | number | Required together with lng | none | -90..90. Presence of lat, lng, or radius_km requires the other two coordinate fields |
lng | number | Required together with lat | none | -180..180 |
radius_km | integer | No (only meaningful with lat/lng) | 50 | 1..500 |
200 OKGET /api/v1/diver/dive-logs: the collection sits at data.data, the pagination at data.meta, no links key. distance_km is present only in radius mode. Captured live, plain list (per_page=3, 449 centres total at capture time):{
"success": true,
"status": "success",
"message": "OK",
"data": {
"data": [
{
"id": 1132,
"raid_id": "DCMX24-609-707",
"name": "Cozumel Top Dive Experience",
"country": { "value": "MX", "label": "Mexico" },
"city": "Cozumel",
"logo": "https://example.com/dive_centers/logo.webp",
"location": null
},
{
"id": 856,
"raid_id": "DCCN22-880-975",
"name": "18M Diving",
"country": { "value": "CN", "label": "China" },
"city": "Macao SAR",
"logo": "https://example.com/dive_centers/logo.webp",
"location": { "lat": 22.20004, "lng": 113.54933 }
}
],
"meta": { "current_page": 1, "last_page": 150, "per_page": 3, "total": 449 }
}
}lat=41.9028&lng=12.4964&radius_km=50, one centre within range at capture time):{
"success": true,
"status": "success",
"message": "OK",
"data": {
"data": [
{
"id": 287,
"raid_id": "DCIT15-960-660",
"name": "ROMA SUB",
"country": { "value": "IT", "label": "Italy" },
"city": "roma",
"logo": "https://example.com/dive_centers/logo.webp",
"location": { "lat": 41.890293, "lng": 12.3827714 },
"distance_km": 9.5
}
],
"meta": { "current_page": 1, "last_page": 1, "per_page": 20, "total": 1 }
}
}| Field | Type | Nullable | Always present | Description |
|---|---|---|---|---|
data.data[].id | integer | No | Yes | Numeric primary key. This is what RegisterRequest::dive_center and UpdateDiveCenterRequest::dive_center_id take - not raid_id |
data.data[].raid_id | string | No | Yes | The path identifier for GET .../dive-centers/{raid_id} |
data.data[].name | string | No | Yes | |
data.data[].country | object | No | Yes | {value, label}, same shape as GET /api/v1/public/countries |
data.data[].city | string | Yes | Yes | |
data.data[].logo | string | Yes | Yes | null when the centre has no logo uploaded |
data.data[].location | object | Yes | Yes | {lat, lng} as floats, or null when the centre's coordinates fail validation (see §14's coordinate-coverage caveat) |
data.data[].distance_km | number | No | Only in radius mode | Rounded to 1 decimal, ascending order |
data.meta.current_page | integer | No | Yes | |
data.meta.last_page | integer | No | Yes | |
data.meta.per_page | integer | No | Yes | |
data.meta.total | integer | No | Yes | Honours every active filter, including the radius |
422 Unprocessable Entity — validationper_page above 100, unrecognised country, lat given without lng (or vice versa), radius_km above 500 or below 1.{
"success": false,
"status": "error",
"message": "Validation failed",
"errors": {
"per_page": ["The per page field must not be greater than 100."]
}
}429 Too Many Requestspublic limiter (300 requests per minute, keyed by IP). No errors key.{
"success": false,
"status": "error",
"message": "Too many requests"
}lat/lng still appears in this list with location: null. It is excluded from GET .../dive-centers/map and from every radius search, even when its garbled coordinates would numerically fall inside the requested radius.whereRaw, not having: data.meta.total correctly reflects only the centres inside the radius, because the pagination count query keeps WHERE clauses but drops SELECT-level aggregates.GET .../dive-centers/{raid_id}.GET /api/v1/utility/dive-centers (removed; see docs/api/_archive/utility/GET_dive_centers.md), which was unpaginated, had no search or radius, and exposed email on every row.curl --location 'https://test.diveraid.com/api/v1/public/dive-centers?search=&country=&per_page=&page=&lat=&lng=&radius_km='{
"status": "success",
"message": "string",
"data": {
"data": [
{
"id": 0,
"raid_id": "string",
"name": "string",
"country": {
"value": "IT",
"label": "Italy"
},
"city": "string",
"logo": "string",
"location": {
"lat": 0,
"lng": 0
},
"distance_km": 0
}
],
"meta": {
"current_page": 0,
"last_page": 0,
"per_page": 0,
"total": 0
}
}
}