Autenticazione e modalità
Ogni richiesta richiede la tua chiave nell’intestazione Authorization: Bearer …. Assegniamo le chiavi dopo un breve colloquio – non è prevista la registrazione autonoma.
https://api.listnride.com/v1Bici e prezzi reali, prenotazioni simulate: il link apre una pagina di test, nessuno paga e il noleggiatore non viene avvisato.
Prenotazioni reali tramite il checkout di ListNRide.
curl "https://api.listnride.com/v1/rides?lat=52.52&lng=13.40" \
-H "Authorization: Bearer lnr_test_…"Come funziona una prenotazione
Non ti serve un checkout tuo: il cliente prenota e paga da noi e poi torna sul tuo sito.
Trovare le bici
GET /v1/ridesper luogo, periodo e categoria.Creare una sessione di checkout
POST /v1/checkout/sessions– ricevi prezzo e link.Reindirizzare il cliente
Prenotazione e pagamento nel checkout di ListNRide, poi ritorno da te.
Ricevere lo stato
Tramite webhook o con
GET /v1/bookings/{id}.
La modalità test passo dopo passo
Con una chiave di test tutto funziona come dal vivo – solo che nessuno paga e nessun noleggiatore viene coinvolto. Prova l’intero percorso una volta prima di andare live.
Creare una sessione
POST /v1/checkout/sessionsconlnr_test_…. Prezzi e disponibilità sono reali.Aprire il link
L’
urlapre una pagina di test invece del checkout. Scegli «Complete test booking».Verificare il ritorno
Il cliente arriva sulla tua
return_urlconbooking=bkg_test_…estatus=success.Verificare il webhook
Se è impostato un URL webhook,
booking.confirmedcon"mode": "test"arriva entro un minuto.
Endpoint
Bici vicino a un luogo o di un noleggiatore. Con un periodo, availability.units_available indica le unità libere.
| Parametro | Descrizione |
|---|---|
| lat, lng | Centro della ricerca (oppure lister) |
| radius_km | 0,1–100, predefinito 15 |
| bbox | Area della mappa minLng,minLat,maxLng,maxLat invece di lat/lng |
| from, to | Periodo, data o data-ora ISO |
| quantity | Numero di bici uguali, predefinito 1 |
| category | ad es. e-bike,cargo-bike |
| price_min price_max | Prezzo al giorno in centesimi, commissione di servizio e assicurazione base incluse |
| rider_height | Altezza del ciclista in cm (150–210) – trova bici della misura giusta e unisize |
| frame_size brand | Misura del telaio o marca esatta, ad es. M o Gazelle |
| accessories | Dotazione inclusa, deve esserci tutto: helmet,lock,lights,basket,childseat,trailer,gps,repair_kit |
| instant_booking top_booked | true: solo conferma immediata / solo le più prenotate |
| min_rating lister_type type | Valutazione minima, business o private, bike o accessory |
| sort | recommended · distance · price_asc · price_desc · rating · popular |
| limit, cursor | Paginazione, al massimo 100 per pagina |
{
"object": "list",
"data": [{
"id": "ride_oEbNgLPApUJf52PkgHU49A",
"name": "KRZBERG – Single Speed",
"category": { "id": "single-speed" },
"price": { "currency": "EUR", "per_day": 2500 },
"lister": { "id": "lis_VTknvPc20LBQbXE9p7dt6A" },
"instant_booking": true,
"distance_km": 1.46
}],
"has_more": true, "next_cursor": "eyJvIjoyMH0"
}Una bici con descrizione, tutte le foto, dotazione, cauzione e assicurazione.
In più tutto ciò che serve a una pagina prodotto: prezzi totali da 1 a 28 giorni, altezza del ciclista, regole di prenotazione, orari, condizioni di cancellazione ed extra prenotabili con zone di consegna.
{
"price": { "per_day": 2500, "tiers": [
{ "days": 1, "total": 2500 },
{ "days": 7, "total": 15599, "discount": 1400 } ] },
"size": { "rider_height": { "range": "175-185" } },
"booking_rules": { "min_days": 1, "time_slot_minutes": 30 },
"opening_hours": { "monday": ["09:00-18:00"], … },
"cancellation_policy": { "by_customer": [
{ "hours_before_pickup_min": 72, "refund_percent_of_rental": 100 }, … ] },
"extras": [{ "code": "insurance_plus" }, { "code": "delivery" }]
}Unità libere per giorno e fasce di ritiro e riconsegna in ora locale, fino a 92 giorni per richiesta. Giorni di chiusura e orari sono già considerati – ideale per il tuo selettore di date. La verifica vincolante avviene con POST /v1/quotes e nel checkout.
{
"object": "availability",
"units_total": 4,
"days": [{
"date": "2026-11-17",
"units_available": 3,
"closed": false,
"pickup_windows": ["09:00-18:00"],
"return_windows": ["09:00-18:00"]
}, …]
}Recensioni verificate di una bici, prima le più recenti, con media e numero totale. Gli autori compaiono con nome e iniziale. Tutte le recensioni di un noleggiatore: GET /v1/listers/{id}/reviews.
Profilo pubblico di un noleggiatore: nome, località, valutazione, prenotazioni concluse, numero di bici, conferma immediata, regole di prenotazione, orari e zone di consegna. I privati compaiono con il nome e senza via.
Le condizioni di cancellazione valide per ogni prenotazione: da 72 ore prima del ritiro viene rimborsato tutto, sotto una quota del noleggio in base alla fascia. Se cancella il noleggiatore, il cliente riceve tutto indietro.
Tutte le categorie con nome tedesco e inglese. Una categoria principale come e-bike include le sue sottocategorie nella ricerca.
Tutte le località con bici prenotabili, prima quelle con più bici. Con q cerchi in tutte le lingue (ad es. q=monaco), con country filtri per paese e locale sceglie la lingua dei nomi. Singola località: GET /v1/places/{id}.
Invece di una bici precisa, il tuo cliente prenota una classe – ad es. «E-bike, fascia media» a Berlino – e ListNRide trova un noleggiatore adatto. Interroga con place o lat/lng. Si prenota con una sessione di checkout con class e size (altezza del ciclista).
{
"id": "berlin.e-bike.mid",
"object": "class",
"category": { "id": "e-bike" },
"tier": { "id": "mid" },
"price": { "currency": "EUR", "per_day": 3900 },
"sizes": ["150-165", "165-175", "175-185", "185-205", "Unisize"],
"listers_nearby": 12
}Calcola il prezzo esatto come il checkout di ListNRide: noleggio, sconto, commissione di servizio, assicurazione base, extra (insurance_plus) e consegna a un indirizzo, senza prenotare nulla. Un preventivo comprende bici di un solo noleggiatore o una classe.
curl -X POST https://api.listnride.com/v1/quotes \
-H "Authorization: Bearer lnr_test_…" \
-H "Content-Type: application/json" \
-d '{
"from": "2026-11-17", "to": "2026-11-19",
"items": [{ "ride": "ride_jK6n…", "quantity": 2,
"extras": ["insurance_plus"] }],
"delivery_address": "Torstraße 1, 10119 Berlin"
}'
# → "amounts": { "rental": 6000, "service_fee": 1100,
# "insurance_plus": 900, "delivery": 1500, "total": 9600 }Crea una sessione di checkout. Il checkout di ListNRide verifica prezzo e disponibilità. Il link è valido 24 ore; quantità e orari sono preselezionati nel checkout.
| Campo | Descrizione |
|---|---|
| ride * | ID della bici ride_… |
| class, size | Invece di ride: una classe da GET /v1/classes e l’altezza del ciclista (165-175 … o Unisize) |
| from, to * | Giorno di ritiro e di riconsegna, YYYY-MM-DD |
| pickup_time, return_time | HH:MM, predefinito 10:00 / 17:00 |
| quantity | Quantità, 1–50, predefinita 1 |
| return_url * | Dove torna il cliente dopo la prenotazione (https), con checkout_session, booking e status=success |
| cancel_url | Facoltativo, in caso di annullamento (https) |
| locale | de · en · fr · es · it · nl |
| client_reference | Il tuo riferimento |
curl -X POST https://api.listnride.com/v1/checkout/sessions \
-H "Authorization: Bearer lnr_test_…" \
-H "Content-Type: application/json" \
-d '{
"ride": "ride_jK6nvpDczET3Adx8AKKn1w",
"from": "2026-11-17", "to": "2026-11-18",
"pickup_time": "09:00", "return_time": "17:00",
"return_url": "https://tuo-sito.it/grazie",
"locale": "it"
}'
# → { "id": "cs_O8t1…", "amount_total": 7800,
# "url": "https://www.listnride.it/api/go/p/BHTm…" }Stato della sessione: open, booked (con ID booking) o expired.
| status | Descrizione |
|---|---|
| awaiting_payment | Il cliente è nel checkout, non ha ancora pagato |
| requested | Pagata o riservata, in attesa di conferma del noleggiatore |
| accepted | Accettata dal noleggiatore, pagamento in corso |
| confirmed | Confermata |
| completed | Noleggio concluso |
| canceled_by_lister · canceled_by_customer · canceled | Annullata |
| expired | Non pagata o non accettata in tempo |
Tutte le prenotazioni delle tue sessioni di checkout, prima le più recenti. Filtra per status o per la tua client_reference. Una chiave di test elenca le prenotazioni di test, una chiave live quelle reali. Le prenotazioni annullate contengono cancellation con importo e stato del rimborso.
Annulla una prenotazione per conto del tuo cliente – con le stesse regole di listnride.com: da 72 ore prima del ritiro viene rimborsato tutto, sotto una quota del noleggio. Il noleggiatore viene avvisato, ricevi booking.canceled_by_customer e il rimborso torna sul metodo di pagamento del cliente.
Verifica prima con GET /v1/bookings/{id}/cancellation: mostra se l’annullamento è possibile e il rimborso esatto, senza modificare nulla. Annullare richiede lo scope bookings:cancel, che attiviamo su richiesta. Facoltativi: reason (changed_mind, found_better, timing_issue, price_too_high, personal_reasons, other) e comment.
# 1. Anteprima: si può annullare e quanto riceve indietro il cliente?
curl https://api.listnride.com/v1/bookings/bkg_gr9z…/cancellation \
-H "Authorization: Bearer lnr_live_…"
# → "cancelable": true, "refund": { "amount": 7300 },
# "breakdown": { "rental_refunded_percent": 75 }
# 2. Annullare
curl -X POST https://api.listnride.com/v1/bookings/bkg_gr9z…/cancel \
-H "Authorization: Bearer lnr_live_…" \
-H "Content-Type: application/json" \
-d '{ "reason": "timing_issue" }'
# → "status": "canceled_by_customer"Aggiungi ?aff=TUOCODICE a qualsiasi link di ListNRide – ogni pagina, ogni lingua. Se il cliente prenota entro il periodo concordato, la prenotazione conta per te; vince l’ultimo clic. Le prenotazioni dalle tue sessioni di checkout contano sempre per te, anche senza clic.
La provvigione è concordata individualmente e si basa sulle commissioni che ListNRide guadagna sulla prenotazione. Conta quando il noleggio è terminato; se annulla il noleggiatore, non c’è provvigione. GET /v1/commissions mostra clic, prenotazioni, livello e provvigione per mese – senza dati dei clienti. La liquidazione è mensile tramite nota di accredito.
# Link partner – funziona su ogni pagina
https://www.listnride.de/berlin/e-bike?aff=NEEDABIKE
# La tua provvigione di un mese
curl "https://api.listnride.com/v1/commissions?month=2026-09" \
-H "Authorization: Bearer lnr_live_…"
# → "clicks": 4812, "bookings": 91, "tier": "Stufe 3 · 30 %",
# "commission_net": { "amount": 54120 }, "status": "approved"Webhook
Quando lo stato di una prenotazione cambia, inviamo un POST al tuo URL webhook, ad es. booking.confirmed. Rispondi con 2xx – altrimenti riproviamo dopo 1 min, 5 min, 30 min e fino a 24 h. Usa ping per testare la connessione; l’id dell’evento resta uguale nei nuovi tentativi.
Verifica ogni messaggio con l’intestazione LNR-Signature: t=…,v1=…: HMAC-SHA256 su t + "." + body con il tuo segreto whsec_…. Usa il corpo originale non modificato e rifiuta i messaggi il cui t ha più di 5 minuti.
POST https://tuo-sito.it/hooks/listnride
LNR-Event: booking.confirmed
LNR-Signature: t=1791210000,v1=5f2c…
{
"id": "evt_Qm3xT0bWl2kZ8yQe",
"object": "event",
"type": "booking.confirmed",
"created": "2026-11-02T09:14:03.000Z",
"data": { "object": {
"id": "bkg_gr9z-CSUh4I8UrC9WEwW2Q",
"object": "booking",
"mode": "live",
"status": "confirmed",
"reference": "LNR-w4kKb",
"checkout_session": "cs_O8t1fc2rdEG2GyYTqghvfA",
"ride": "ride_jK6nvpDczET3Adx8AKKn1w",
"from": "2026-11-17T08:00:00.000Z",
"to": "2026-11-18T16:00:00.000Z",
"quantity": 1,
"amount_total": 7800,
"currency": "EUR",
"client_reference": "NB-1042"
} }
}const crypto = require("crypto");
// rawBody: il corpo originale, così come è arrivato
function verify(rawBody, header, secret) {
const p = Object.fromEntries(header.split(",").map(x => x.split("=")));
if (Math.abs(Date.now() / 1000 - Number(p.t)) > 300) return false;
const expected = crypto.createHmac("sha256", secret)
.update(`${p.t}.${rawBody}`).digest("hex");
return typeof p.v1 === "string" && p.v1.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(p.v1));
}
function lnr_verify(string $rawBody, string $header, string $secret): bool {
$p = [];
foreach (explode(",", $header) as $part) {
[$k, $v] = array_pad(explode("=", $part, 2), 2, "");
$p[$k] = $v;
}
if (abs(time() - (int)($p["t"] ?? 0)) > 300) return false;
$expected = hash_hmac("sha256", $p["t"] . "." . $rawBody, $secret);
return hash_equals($expected, $p["v1"] ?? "");
}
// $raw = file_get_contents("php://input");
// lnr_verify($raw, $_SERVER["HTTP_LNR_SIGNATURE"] ?? "", "whsec_…");
import hashlib, hmac, time
# raw_body: il corpo originale, così come è arrivato
def verify(raw_body: bytes, header: str, secret: str) -> bool:
p = dict(x.split("=", 1) for x in header.split(",") if "=" in x)
if abs(time.time() - int(p.get("t", 0))) > 300:
return False
expected = hmac.new(secret.encode(), p["t"].encode() + b"." + raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, p.get("v1", ""))Errori e limiti
Gli errori hanno sempre la stessa forma. Con il request_id troviamo subito il tuo caso.
Limiti per chiave: un limite al minuto e una quota giornaliera (intestazioni X-Quota-Limit, X-Quota-Remaining). Una ricerca si può sfogliare al massimo fino a 500 risultati: restringila per località, raggio, categoria o periodo. La raccolta massiva e la rivendita dei dati non sono consentite.
Ripetizioni sicure: invia l’intestazione Idempotency-Key con POST /v1/checkout/sessions e POST /v1/bookings/{id}/cancel. Una ripetizione con la stessa chiave e lo stesso contenuto restituisce la prima risposta (intestazione Idempotent-Replayed: true) invece di agire due volte. Le chiavi restano valide 24 ore.
| HTTP | code |
|---|---|
| 400 | parameter_missing, parameter_invalid, outside_opening_hours, duration_too_short, duration_too_long, multiple_listers, delivery_unavailable, pagination_limit |
| 401 | missing_api_key, invalid_api_key |
| 403 | missing_scope |
| 404 | resource_missing, route_not_found |
| 409 | ride_unavailable, already_canceled, processing, already_started |
| 429 | rate_limited, daily_quota_exceeded, too_many_failed_attempts – X-RateLimit-*, X-Quota-*, Retry-After |
{ "error": {
"type": "invalid_request_error",
"code": "ride_unavailable",
"message": "The ride is not available for this period and quantity.",
"param": "ride",
"request_id": "req_UqrIw8P5wzwT" } }SDK e strumenti
Client ufficiali in un unico file, senza dipendenze – copiali nel tuo progetto. Gestiscono autenticazione, paginazione, nuovi tentativi in caso di 429/5xx, Idempotency-Key e verifica dei webhook.
const { ListNRide } = require("./listnride");
const lnr = new ListNRide({ apiKey: process.env.LNR_API_KEY });
for await (const ride of lnr.rides.all({ lat: 52.52, lng: 13.40, category: "e-bike" })) {
console.log(ride.name, ride.price.per_day);
}
const session = await lnr.checkoutSessions.create({ ride: "ride_…", from: "2026-11-17",
to: "2026-11-19", return_url: "https://your-site.com/thanks" }); // l’Idempotency-Key viene aggiunta automaticamenteVersioni
La versione fa parte del percorso. In /v1 aggiungiamo solo campi – nulla viene rimosso o rinominato. Le modifiche sostanziali usciranno come /v2, e /v1 resterà attiva almeno 12 mesi. La descrizione leggibile dalle macchine (OpenAPI 3.1) è su /openapi.json e https://api.listnride.com/v1/openapi.json.
Modifiche
Idempotency-Key per le chiamate di scrittura, collezione Postman, client ufficiali per Node.js, PHP e Python.
Affiliazione: link partner con
?aff=CODEeGET /v1/commissions.GET /v1/bookings, anteprima di cancellazione ePOST /v1/bookings/{id}/cancel(scopebookings:cancel).Filtri di ricerca (prezzo, altezza, dotazione, area della mappa …), scaglioni di prezzo, orari, condizioni di cancellazione, calendario di disponibilità, recensioni e profili dei noleggiatori.
Località, classi di veicoli,
POST /v1/quotes, prenotazione per classe e quota giornaliera per chiave.Lancio di v1: bici, categorie, sessioni di checkout, prenotazioni, webhook e modalità test.
Richiedi accesso
Raccontaci del tuo sito e di cosa hai in mente. Ricevi prima una chiave di test – il passaggio al live segue dopo una breve chiamata.
partner@listnride.com