Nordtinde API
Connect your website or system to Nordtinde. Display homes, manage your own prices and calendar, and receive booking updates. Guests still book on Nordtinde.
Get an API keyGet started in 3 steps
- Sign in and open Hosting → API access.
- Create a key. Choose Read only to display your homes, or Read + write to edit them.
- Copy the key once, save it securely, and include it in each request as shown below.
Your first request
curl https://YOUR-NORDTINDE-DOMAIN/api/public/v1/listings \
-H "Authorization: Bearer ns_live_YOUR_KEY"Replace ns_live_YOUR_KEY with your key. A successful response is JSON with a data list of your published homes.
JavaScript and write examples
Read example (JavaScript — run on your server)
const res = await fetch("https://YOUR-NORDTINDE-DOMAIN/api/public/v1/listings", {
headers: { Authorization: "Bearer ns_live_YOUR_KEY" },
});
const { data } = await res.json();Write example (curl, needs a read + write key)
curl -X PATCH https://YOUR-NORDTINDE-DOMAIN/api/public/v1/listings/LISTING_ID/price \
-H "Authorization: Bearer ns_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"nightly_price": 1500}'Keep keys on your server, not in public website code. Booking access is an optional permission when creating a key. Full catalog access needs a separate application and approval.
What can you do?
Base URL: https://YOUR-NORDTINDE-DOMAIN/api/public/v1. All prices are in NOK.
GET /bookings?limit=50&offset=0Booking accessGet dates, status, guest counts and prices for your host bookings.
Examples and technical details
Read reservations where you are the host, including draft homes. Requires explicit booking access on the key. Optional listing_id (UUID), status, from and to (valid YYYY-MM-DD dates). Date filters select stays overlapping [from,to); checkout on from and check-in on to are excluded. Status: requested, pending_payment, confirmed, declined, cancelled, expired, completed. limit 1–100 (default 50), offset 0–100000 (default 0). Newest first, with id as tie-breaker. Follow next_offset until null; offset pagination may shift during concurrent inserts, so deduplicate by id.
Example response (selected fields)
{ "data": [{ "id": "BOOKING_UUID", "listing_id": "LISTING_UUID", "status": "confirmed", "check_in": "2027-08-01", "check_out": "2027-08-05", "adults": 2, "children": 0, "infants": 0, "pets": 0, "nights": 4, "total": 5936, "currency": "NOK", "host_payout": 5141, "created_at": "2026-11-02T10:00:00Z", "updated_at": "2026-11-02T10:00:00Z" }], "pagination": { "limit": 50, "offset": 0, "next_offset": null } }GET /listingsRead keyGet your published homes and their basic details.
Examples and technical details
Your published listings (summary fields, no photos/amenities/house_rules).
Example response (selected fields)
{ "data": [ { "id": "130f42b8-…", "title": "Fjord-edge cabin with sauna", "city": "Balestrand", "nightly_price": 1200, "cleaning_fee": 500, "instant_book": true, "cover_photo": "https://…/cover.jpg", "booking_url": "https://nordtinde…/stays/130f42b8-…" } ] }GET /listings/{id}Read keyGet one of your homes with photos, amenities and house rules.
Examples and technical details
One listing with photos, amenities and house rules.
Example response (selected fields)
{ "data": { "id": "130f42b8-…", "title": "Fjord-edge cabin with sauna", "description": "Timber cabin on the water's edge …", "photos": [{ "url": "https://…/1.jpg", "caption": "Living room" }], "amenities": ["Sauna", "Wi-Fi", "Fireplace"], "house_rules": ["No smoking", "Quiet hours 23:00–07:00"], "booking_url": "https://nordtinde…/stays/130f42b8-…" } }GET /listings/{id}/availability?from=YYYY-MM-DD&to=YYYY-MM-DDRead keyCheck which nights are available and what they cost.
Examples and technical details
Each night with available true/false and price. Max 366 days.
Example response (selected fields)
{ "data": [ { "date": "2027-08-01", "available": true, "price": 1200 }, { "date": "2027-08-02", "available": false, "price": 1200 }, { "date": "2027-08-03", "available": true, "price": 1500 } ] }GET /listings/{id}/quote?check_in=YYYY-MM-DD&check_out=YYYY-MM-DDRead keyGet the total price for a stay, including fees.
Examples and technical details
Price breakdown for a stay, same math as Nordtinde checkout. weekly_discount and monthly_discount are savings in NOK already deducted from nights_total. For 28+ nights a nonzero monthly discount replaces the weekly discount; otherwise the weekly discount applies from 7 nights. Discounts apply to nightly prices including overrides, not cleaning or extras. Fees are calculated after the discount.
Example response (selected fields)
{ "data": { "check_in": "2027-08-01", "check_out": "2027-08-05", "available": true, "min_nights": 2, "currency": "NOK", "nights": 4, "nights_total": 4800, "weekly_discount": 0, "monthly_discount": 0, "cleaning_fee": 500, "service_fee": 636, "total": 5936, "booking_url": "https://nordtinde…/stays/130f42b8-…?check_in=2027-08-01&check_out=2027-08-05" } }PATCH /listings/{id}Read + write keyChange your home's title, description, capacity and other settings.
Examples and technical details
Edit listing properties. Body may include any of: title, description, property_type, max_guests, max_infants, max_pets, bedrooms, beds, bathrooms, cleaning_fee, min_nights, instant_book, pets_allowed, check_in_time (HH:MM), check_out_time (HH:MM), cancellation_policy, calendar_closed_by_default. Location, photos and publishing can only be changed on Nordtinde.
Request body
{ "title": "Fjord-edge cabin with sauna", "min_nights": 3, "pets_allowed": true }Example response (selected fields)
{ "data": { "id": "130f42b8-…", "updated": ["title", "min_nights", "pets_allowed"] } }PATCH /listings/{id}/priceRead + write keyChange your nightly price, weekly discount or monthly discount.
Examples and technical details
Set the base nightly price, weekly discount and/or monthly discount. All fields optional, at least one required. Price in NOK, 100–100 000; discounts are integer percentages 0–90. Weekly applies from 7 nights; a nonzero monthly discount replaces weekly from 28 nights. Set a discount to 0 to disable it.
Request body
{ "nightly_price": 1500, "weekly_discount_percent": 10, "monthly_discount_percent": 20 }Example response (selected fields)
{ "data": { "id": "130f42b8-…", "nightly_price": 1500, "weekly_discount_percent": 10, "monthly_discount_percent": 20 } }POST /listings/{id}/availabilityRead + write keyBlock or open dates, or change prices for specific nights.
Examples and technical details
Adjust the calendar. "to" is the checkout day (not included). set_price requires price (NOK, 100–100 000). Nights that are booked or synced from other sites can't be changed.
Request body
{ "action": "block", "from": "2027-08-01", "to": "2027-08-05" } // or: { "action": "set_price", "from": "2027-08-10", "to": "2027-08-12", "price": 1800 } // actions: "block" | "unblock" | "set_price" | "reset_price"Example response (selected fields)
{ "data": { "action": "block", "from": "2027-08-01", "to": "2027-08-05", "changed": 4 } }
Access the full property catalog
Want to display homes from all Nordtinde hosts? Selected partners can apply for read-only access to all published homes.
- Sign in and submit your company, website and intended use.
- Nordtinde reviews your application. Check the status under API access.
- Once approved, create a Full catalog key and keep it on your server.
Not an anonymous API. Approval can be withdrawn at any time. Catalog access never permits editing other hosts' homes or reading their bookings.
GET /catalog/listings?limit=50&offset=0Approved catalog keyGet all published Nordtinde homes, one page at a time.
Examples and technical details
All published Nordtinde homes, across hosts. Requires an approved catalog application and dedicated catalog key. Optional country and city filters use exact stored values. limit 1–100 (default 50), offset 0–100000 (default 0). Newest first, then id. Follow next_offset until null. Concurrent additions may shift pages; deduplicate by id. No drafts, suspended listings, host identities, guest data or private check-in information. Summary uses the same listing fields as GET /listings.
Example response (selected fields)
{ "data": [{ "id": "LISTING_UUID", "title": "Fjord-edge cabin", "city": "Balestrand", "nightly_price": 1200, "booking_url": "https://YOUR-NORDTINDE-DOMAIN/stays/fjord-edge-cabin-130f42b8" }], "pagination": { "limit": 50, "offset": 0, "next_offset": null } }GET /catalog/listings/{id}Approved catalog keyGet photos and details for one home in the full catalog.
Examples and technical details
One published home from any host, including photos, amenities and house rules. id must be a listing UUID returned by the catalog. Requires current approval and a dedicated catalog key. Unpublished or unknown homes return 404. No write access, booking access or private data.
Example response (selected fields)
{ "data": { "id": "LISTING_UUID", "title": "Fjord-edge cabin", "photos": [{ "url": "https://YOUR-PHOTO-DOMAIN/cabin.jpg", "caption": "Living room" }], "amenities": ["Wi-Fi"], "house_rules": ["No smoking"], "booking_url": "https://YOUR-NORDTINDE-DOMAIN/stays/fjord-edge-cabin-130f42b8" } }
Receive booking updates
A webhook is a message sent to your system when a booking is created or its status changes. You don't need to keep asking Nordtinde for updates.
- Add your HTTPS receiver under Hosting → API access → Booking webhooks.
- Save the signing secret. Your developer uses it to check that messages really come from Nordtinde.
- After Nordtinde is published, send a test and check Recent deliveries.
If a delivery fails, resend it manually. There are no automatic retries. Your system should handle the same message arriving more than once. Booking success is never affected by a failed webhook.
Advanced webhook details
Receiver requirements
Configure one enabled webhook per account in Hosting → API access. Use your own public HTTPS domain, Make, Zapier or another receiver. Port 443 only, without URL credentials, query parameters or fragments. IP literals, local/internal domains and private or special-purpose DNS answers are rejected. All A/AAAA records are checked when saving and before delivery; Delivery then connects by hostname with full certificate verification from a network with no access to private addresses. No redirects are followed. Events cover new bookings and every actual status transition, including scheduled expiry/completion. No guest names, email, phone, guest IDs, messages, addresses, Wi-Fi or access codes are sent. The signing secret is shown once; delete and recreate the webhook to rotate it.
Events and payload
booking.created, booking.status_changed, webhook.test
{
"id": "EVENT_UUID", "type": "booking.status_changed", "api_version": "v1",
"created_at": "2026-11-02T10:00:00Z",
"data": {
"id": "BOOKING_UUID", "listing_id": "LISTING_UUID", "status": "confirmed",
"previous_status": "requested", "check_in": "2027-08-01", "check_out": "2027-08-05",
"adults": 2, "children": 0, "infants": 0, "pets": 0, "total": 5936, "currency": "NOK"
}
}Delivery guarantees, timeouts and retries
The booking transaction stores an event and queues an authenticated callback to Nordtinde's published delivery relay, which sends the signed HTTP POST. Publication is required for automatic and test delivery. Connection/response timeout: 5 seconds after DNS checks. Booking success never depends on the receiver. Delivery is best effort, not exactly-once and not guaranteed: failed or missing deliveries can be replayed manually from Recent deliveries, up to 10 attempts per event; there are no automatic retries. Disabled or suspended accounts send no events; paused events are not backfilled. Receiver HTTP status and failure state are stored with durable event history. A 2xx response means success. Respond quickly after storing the event. Duplicate or out-of-order events are possible: deduplicate by event id and reconcile against GET /bookings. Deleting a webhook deletes its event history but cannot retract HTTP requests already queued.
Signature verification (Node.js)
// Node.js receiver: keep the signing secret in your server's secret store.
import { createHmac, timingSafeEqual } from "node:crypto";
function verifyNordtinde(rawBody, headers, secret) {
const stamp = headers["x-nordistay-timestamp"];
const signature = headers["x-nordistay-signature"];
if (!/^\d+$/.test(stamp ?? "") || !/^v1=[0-9a-f]{64}$/.test(signature ?? "")) return false;
if (Math.abs(Date.now() / 1000 - Number(stamp)) > 300) return false;
const expected = createHmac("sha256", secret).update(stamp + "." + rawBody).digest();
const received = Buffer.from(signature.slice(3), "hex");
return expected.length === received.length && timingSafeEqual(expected, received);
}
// Verify BEFORE parsing JSON. Use the exact raw UTF-8 request body.
// Header: X-Nordistay-Event-Id matches payload.id.
// Save payload.id atomically; duplicates should return 2xx without reprocessing.
// webhook.test has data: {}; booking.created has previous_status: null.Advanced reference
All prices are in NOK. Guest identities, exact addresses, door codes and Wi-Fi are never returned.
Listing field reference
| Field | Type | Meaning |
|---|---|---|
id | string | Unique listing id. |
title | string | Listing title. |
description | string | null | Full description. |
property_type | string | cabin, apartment, rorbu, … |
city | string | null | City or town. |
region | string | Region / area. |
country | string | null | Country. |
lat | number | null | Approximate latitude. |
lng | number | null | Approximate longitude. |
max_guests | number | Max adults + children. |
bedrooms | number | Number of bedrooms. |
beds | number | Number of beds. |
bathrooms | number | Number of bathrooms. |
nightly_price | number | Base nightly price in NOK. |
cleaning_fee | number | Cleaning fee in NOK. |
weekly_discount_percent | number | Nightly subtotal discount for 7+ nights (0–90%). Used if no monthly discount applies. |
monthly_discount_percent | number | Nightly subtotal discount for 28+ nights (0–90%). When nonzero, replaces rather than stacks with the weekly discount. Cleaning and extras are not discounted; fees use the discounted subtotal. |
min_nights | number | Minimum nights per stay. |
instant_book | boolean | Guests can book without approval. |
pets_allowed | boolean | Pets are welcome. |
check_in_time | string | null | Check-in from (HH:MM). |
check_out_time | string | null | Check-out by (HH:MM). |
cancellation_policy | string | flexible, moderate, strict or non_refundable. |
cover_photo | string | null | URL of the cover photo. |
photos | { url, caption }[] | All photos in display order (detail endpoint only). |
amenities | string[] | Amenity names (detail endpoint only). |
house_rules | string[] | House rules (detail endpoint only). |
booking_url | string | Link where guests book on Nordtinde. |
Booking field reference
| Field | Type | Meaning |
|---|---|---|
id | string | Booking UUID; stable identity for deduplication. |
listing_id | string | Listing UUID, including owned draft homes. |
status | string | requested, pending_payment, confirmed, declined, cancelled, expired or completed. |
check_in | string | Arrival date (YYYY-MM-DD). |
check_out | string | Departure date (YYYY-MM-DD), excluded from nights. |
adults | number | Booked count. |
children | number | Booked count. |
infants | number | Booked count. |
pets | number | Booked count. |
nights | number | Booked nights. |
total | number | Guest total including fees and selected add-ons; not a payment confirmation. |
currency | string | Booking currency (NOK). |
host_payout | number | Booking's original calculated payout snapshot after saved stay and add-on host fees (currently 3% on accommodation/cleaning and 20% on add-ons; admin-adjustable). Existing bookings retain saved rates. Not proof of a transfer or a currently payable amount. Unpaid payouts stop on cancellation; host cancellation fees and support cases are private. |
created_at | string | ISO timestamp. |
updated_at | string | ISO timestamp. |
Errors, limits and troubleshooting
Limit: 60 requests per minute per key. A 401 usually means a missing or deleted key; 403 means the key or account lacks permission. On 429, slow down and wait before retrying.
{ "error": { "code": "forbidden", "message": "An approved catalog application and catalog key are required" } }400invalid_requestMissing or malformed parameters.
401unauthorizedMissing, invalid or deleted API key.
403forbiddenAccount is suspended, or a read-only key tried to make changes.
404not_foundListing doesn't exist or isn't yours/published.
409conflictNights are booked or synced from other sites and can't be changed.
429rate_limitedMore than 60 requests per minute for this key.
500internal_errorCould not read reservations. Retry with backoff.
503unavailableAccount or rate-limit verification temporarily unavailable. Retry with backoff.
External requests require a published site. Use the published site's base URL, not an editor-only preview. Keep booking and catalog keys server-side; each has separate permissions. Catalog keys cannot be used on host endpoints.
API versions and compatibility
The version is part of the URL (/v1/). Responses may gain new fields. Breaking changes use a new API version; the previous version stays available for at least 6 months. See the changelog for every update.