API Documentation
The Skateparks.world REST API provides programmatic access to skatepark data. All endpoints require authentication.
Authentication Required
All API endpoints are protected. You must include a valid API key with every request. Contact api@skateparks.world to request access.
On this page
Authentication
Include your API key in every request using one of these methods:
Header (recommended)
X-API-Key: your_api_key_hereBearer Token
Authorization: Bearer your_api_key_hereExample request
curl -X GET "https://skateparks.world/api/v1/parks" \
-H "X-API-Key: your_api_key_here"Base URL
All API endpoints are relative to:
https://skateparks.world/api/v1Error Handling
The API uses standard HTTP status codes. Errors return a JSON object with an error field.
| Status | Meaning |
|---|---|
200 | Success |
201 | Resource created |
400 | Bad request — missing or invalid parameters |
401 | Unauthorized — invalid or missing API key |
404 | Resource not found |
500 | Internal server error |
Error response example
{
"error": "Unauthorized"
}Park Object
A park has the following fields. All fields except the three marked required are optional when creating or updating.
Identity
| Parameter | Type | Required | Description |
|---|---|---|---|
name | string | Required | Name of the skatepark. |
country | string | Required | Country name (e.g. "Denmark"). |
city | string | Required | City name (e.g. "Copenhagen"). |
slug | string | Optional | URL slug. Auto-generated from name + city if omitted. |
alias | string | Optional | Alternative/local name for the park. |
Location
| Parameter | Type | Required | Description |
|---|---|---|---|
streetAddress | string | Optional | Street address. |
zipcode | string | Optional | Postal/zip code. |
lat | number | Optional | Latitude coordinate. |
lng | number | Optional | Longitude coordinate. |
Classification
| Parameter | Type | Required | Description |
|---|---|---|---|
type | string | Optional | "indoor", "outdoor", "both", or "unknown" (default). |
status | string | Optional | "open", "closed", "under_construction", or "unknown" (default). |
ownership | string | Optional | Who owns the park (e.g. "Municipal", "Private"). |
management | string | Optional | Who manages/operates the park. |
builtBy | string | Optional | Construction company or builder. |
designedBy | string | Optional | Park designer. |
Features (arrays of strings)
| Parameter | Type | Required | Description |
|---|---|---|---|
obstacles | string[] | Optional | e.g. ["halfpipe", "quarterpipe", "rail", "ledge", "bowl", "funbox"]. |
facilities | string[] | Optional | e.g. ["lights", "toilets", "parking", "water", "shop", "rental"]. |
sports | string[] | Optional | e.g. ["skateboarding", "bmx", "scooter", "inline"]. |
surface | string[] | Optional | e.g. ["concrete", "wood", "asphalt", "metal"]. |
requirements | string[] | Optional | e.g. ["helmet", "pads", "membership"]. |
Size & Year
| Parameter | Type | Required | Description |
|---|---|---|---|
sizeSqm | number | Optional | Park area in square meters. |
yearBuilt | number | Optional | Year the park was built. |
Contact & Socials
| Parameter | Type | Required | Description |
|---|---|---|---|
website | string | Optional | Park website URL. |
phone | string | Optional | Phone number. |
email | string | Optional | Contact email address. |
facebook | string | Optional | Facebook page URL. |
instagram | string | Optional | Instagram handle or URL. |
youtube | string | Optional | YouTube channel URL. |
Pricing
| Parameter | Type | Required | Description |
|---|---|---|---|
priceAmount | number | Optional | Entry price (e.g. 10.00). Null if free. |
priceCurrency | string | Optional | ISO 4217 currency code (e.g. "USD", "EUR", "DKK"). |
paymentMethods | string[] | Optional | e.g. ["cash", "card", "mobilepay"]. |
membershipAvailable | boolean | Optional | Whether membership/season pass is available. |
Operations
| Parameter | Type | Required | Description |
|---|---|---|---|
supervised | boolean | Optional | Whether staff supervision is available. |
surveilled | boolean | Optional | Whether the park has camera surveillance. |
hours | object | Optional | Opening hours. See format below. |
// hours format — each day is { open, close } or null (closed)
{
"mon": { "open": "08:00", "close": "22:00" },
"tue": { "open": "08:00", "close": "22:00" },
"wed": { "open": "08:00", "close": "22:00" },
"thu": { "open": "08:00", "close": "22:00" },
"fri": { "open": "08:00", "close": "23:00" },
"sat": { "open": "10:00", "close": "23:00" },
"sun": null // closed on Sunday
}Content
| Parameter | Type | Required | Description |
|---|---|---|---|
shortDescription | string | Optional | Brief summary of the park. |
description | string | Optional | Full description. Supports newlines. |
Read-only fields (returned in responses)
| Parameter | Type | Required | Description |
|---|---|---|---|
id | uuid | Optional | Unique park ID. Auto-generated. |
countrySlug | string | Optional | Auto-generated from country. |
citySlug | string | Optional | Auto-generated from city. |
averageRating | number | Optional | Computed average rating (1-5). |
ratingCount | number | Optional | Total number of ratings. |
viewCount | number | Optional | Page view count. |
featured | boolean | Optional | Whether the park is featured. |
verified | boolean | Optional | Whether location has been verified. |
createdAt | timestamp | Optional | ISO 8601 creation date. |
updatedAt | timestamp | Optional | ISO 8601 last update date. |
images | array | Optional | Related images (included in GET by ID). |
videos | array | Optional | Related videos (included in GET by ID). |
Park Endpoints
/api/v1/parksList all parks with pagination, or search by query.
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
q | string | Optional | Search query. If provided, returns matching parks instead of all. |
page | number | Optional | Page number (default: 1). |
limit | number | Optional | Results per page. Min 1, max 100 (default: 50). |
Response
{
"parks": [ { "id": "...", "name": "...", ... } ],
"pagination": {
"page": 1,
"limit": 50,
"total": 1234,
"totalPages": 25
}
}/api/v1/parksCreate a new skatepark. Only name, country, and city are required. All other park fields (see Park Object above) can be included.
Required fields
| Parameter | Type | Required | Description |
|---|---|---|---|
name | string | Required | Name of the skatepark. |
country | string | Required | Country name. |
city | string | Required | City name. |
Plus any optional fields from the Park Object reference above (obstacles, facilities, phone, email, hours, pricing, etc.).
Example request
{
"name": "Faelledparken Skatepark",
"country": "Denmark",
"city": "Copenhagen",
"lat": 55.6945,
"lng": 12.5728,
"type": "outdoor",
"status": "open",
"obstacles": ["bowl", "quarterpipe", "rail", "ledge"],
"facilities": ["lights", "parking"],
"sports": ["skateboarding", "scooter"],
"surface": ["concrete"],
"phone": "+45 12345678",
"email": "info@example.com",
"instagram": "@faelledskatepark",
"description": "A popular outdoor skatepark in the heart of Copenhagen."
}Response (201)
{
"park": { "id": "...", "name": "Faelledparken Skatepark", "slug": "faelledparken-skatepark-copenhagen", ... }
}/api/v1/parks/:idGet a single park by ID with all details, images, and videos.
Response
{
"park": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Faelledparken Skatepark",
"slug": "faelledparken-skatepark-copenhagen",
"country": "Denmark",
"city": "Copenhagen",
"streetAddress": "Øster Allé 1",
"zipcode": "2100",
"lat": 55.6945,
"lng": 12.5728,
"type": "outdoor",
"status": "open",
"obstacles": ["bowl", "quarterpipe", "rail", "ledge"],
"facilities": ["lights", "parking"],
"sports": ["skateboarding", "scooter"],
"surface": ["concrete"],
"phone": "+45 12345678",
"email": "info@example.com",
"website": "https://example.com",
"instagram": "@faelledskatepark",
"facebook": "",
"youtube": "",
"priceAmount": null,
"priceCurrency": "",
"paymentMethods": [],
"membershipAvailable": null,
"supervised": false,
"surveilled": false,
"hours": null,
"sizeSqm": 1200,
"yearBuilt": 2018,
"shortDescription": "Popular concrete park in central Copenhagen.",
"description": "A popular outdoor skatepark in the heart of Copenhagen...",
"averageRating": "4.20",
"ratingCount": 15,
"verified": true,
"images": [
{ "id": "...", "url": "https://...", "caption": "...", "credit": "...", "order": 0 }
],
"videos": [],
"createdAt": "2024-01-15T10:30:00.000Z",
"updatedAt": "2025-11-20T14:22:00.000Z"
}
}/api/v1/parks/:idUpdate an existing park. Send only the fields you want to change. Accepts any field from the Park Object.
Example: update contact & features
{
"phone": "+45 87654321",
"email": "new@example.com",
"instagram": "@newhandle",
"obstacles": ["bowl", "halfpipe", "rail", "ledge", "funbox"],
"hours": {
"mon": { "open": "10:00", "close": "20:00" },
"tue": { "open": "10:00", "close": "20:00" },
"wed": { "open": "10:00", "close": "20:00" },
"thu": { "open": "10:00", "close": "20:00" },
"fri": { "open": "10:00", "close": "22:00" },
"sat": { "open": "10:00", "close": "22:00" },
"sun": null
},
"priceAmount": 5.00,
"priceCurrency": "EUR"
}Response
{
"park": { "id": "...", "name": "...", ... }
}/api/v1/parks/:idPermanently delete a park and all related images, videos, and ratings.
Response
{ "success": true }Search
/api/v1/searchSearch parks by name, city, or country.
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
q | string | Required | Search query (must be non-empty). |
limit | number | Optional | Max results. Min 1, max 50 (default: 20). |
Response
{
"parks": [ { "id": "...", "name": "...", ... } ],
"query": "copenhagen",
"count": 5
}Coordinates
/api/v1/parks/coordinatesGet coordinates for all parks. Useful for rendering map markers. Cached for 1 hour.
Response
{
"parks": [
{ "id": "...", "name": "...", "lat": 55.67, "lng": 12.56, ... },
...
]
}Ratings
/api/v1/parks/:id/ratingsSubmit a rating for a park. Rate-limited by IP address.
Request Body
| Parameter | Type | Required | Description |
|---|---|---|---|
rating | number | Required | Rating value, 1 to 5. |
displayName | string | Required | Name to display with the review. |
comment | string | Optional | Optional review text. |
email | string | Optional | Optional email (not displayed publicly). |
createdAt | string | Optional | ISO 8601 timestamp. Defaults to now. Use this to backdate ratings found online (e.g. "2023-06-15T12:00:00Z"). |
Response (201)
{
"rating": {
"id": "...",
"parkId": "...",
"rating": 4,
"displayName": "Alex",
"comment": "Great park!",
...
}
}Images
/api/v1/parks/:id/imagesUpload a photo for a park. Uses multipart/form-data. New uploads start with pending status (moderation required).
Request Body (multipart/form-data)
| Parameter | Type | Required | Description |
|---|---|---|---|
image | File | Required | Image file. Must be an image/* MIME type, max 10 MB. |
email | string | Required | Uploader email (for moderation contact). |
caption | string | Optional | Optional image caption/alt text. |
credit | string | Optional | Photographer credit / copyright attribution. |
Example (curl)
curl -X POST "https://skateparks.world/api/v1/parks/:id/images" \
-H "X-API-Key: your_api_key_here" \
-F "image=@photo.jpg" \
-F "email=user@example.com" \
-F "caption=Front view of the park" \
-F "credit=John Doe"Response (201)
{
"image": {
"id": "...",
"parkId": "...",
"url": "https://...",
"caption": "Front view of the park",
"credit": "John Doe",
"status": "pending",
...
}
}Suggestions
/api/v1/suggestionsList all pending suggestions.
Response
{
"suggestions": [
{
"id": "...",
"parkId": "...",
"changeType": "update",
"changes": { ... },
"source": "user",
"status": "pending",
...
}
]
}/api/v1/suggestionsSubmit a suggestion for a new park or changes to an existing one.
Request Body
| Parameter | Type | Required | Description |
|---|---|---|---|
changes | object | Required | Object describing the proposed changes. |
changeType | string | Required | Type of change, e.g. "update", "new_park", "correction". |
parkId | string | Optional | ID of the existing park (if updating). |
source | string | Optional | Source of the suggestion (default: "user"). |
sourceUrl | string | Optional | URL reference for the data source. |
submittedBy | string | Optional | Name of the submitter. |
email | string | Optional | Contact email. |
Response (201)
{
"suggestion": {
"id": "...",
"changeType": "update",
"changes": { ... },
"source": "user",
"status": "pending",
...
}
}Cache Revalidation
/api/v1/revalidateBust the Next.js cache for specific pages or the entire site. Call this after creating or updating parks to see changes immediately.
Request Body
Provide either path or all, not both.
| Parameter | Type | Required | Description |
|---|---|---|---|
path | string | Optional | Specific page path to revalidate, e.g. "/denmark/copenhagen/park-name". |
all | boolean | Optional | Set to true to revalidate all pages. |
Examples
// Revalidate a specific park page
{ "path": "/denmark/copenhagen/faelledparken" }
// Revalidate the homepage
{ "path": "/" }
// Revalidate everything
{ "all": true }Response
// Specific path
{ "revalidated": true, "path": "/denmark/copenhagen/faelledparken" }
// All pages
{ "revalidated": true, "scope": "all" }Notes
Rate limiting
Rating submissions are rate-limited by IP address to prevent abuse.
Image moderation
Uploaded images start with a pending status and require approval before they appear publicly.
Cache behavior
Park pages are statically generated and cached. After creating or updating data, call the revalidation endpoint to see changes reflected on the site immediately.
Content-Type
All endpoints accept and return application/json, except the image upload endpoint which uses multipart/form-data.
Need an API key or have questions? api@skateparks.world