{"openapi":"3.1.0","info":{"title":"London Night Guide Content API","version":"1.1.0","summary":"Free, read-only content API for London nightlife information.","description":"London Night Guide is a free London nightlife concierge (guestlists, VIP tables, event planning). This spec covers the public machine surface: every content URL negotiates markdown for agents, `.md` path variants serve the same output, and discovery files (llms.txt, sitemap, RSS) are listed below. No authentication, no API keys, no rate-limit registration. There is no write API — bookings are confirmed by a human concierge via the forms at https://londonnightguide.com/book-guestlist/ and https://londonnightguide.com/book-vip-table/ or the WhatsApp links published in llms.txt. Unknown /api/* paths return a structured JSON error envelope. Versioning: the JSON endpoints are URL-path versioned under /api/v1/. Breaking changes ship as a new /api/v2/ prefix with at least 90 days of overlap; a deprecated endpoint signals it with Deprecation and Sunset response headers (RFC 8594) and is marked deprecated in this spec. Full deprecation policy: https://londonnightguide.com/developers/deprecation/ (also x-deprecation-policy above). Unversioned content URLs (/{slug}/, .md paths, llms.txt) are stable and only ever gain fields. Rate limits: /api/* requests are limited to 120 per minute per client; every response carries RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset and RateLimit-Policy headers, and a 429 includes Retry-After. Content URLs are CDN-cached and not rate limited — still, please cache politely (markdown responses send Cache-Control: s-maxage=3600). Every operation carries a unique operationId, typed parameters and response schemas, so this spec can be loaded directly into LLM function-calling and tool-use frameworks.","contact":{"name":"London Night Guide Concierge","email":"concierge@londonnightguide.com","url":"https://londonnightguide.com/contact/"},"x-deprecation-policy":"https://londonnightguide.com/developers/deprecation/"},"externalDocs":{"description":"Human-readable API and agent documentation","url":"https://londonnightguide.com/developers/"},"servers":[{"url":"https://londonnightguide.com","description":"Production"}],"tags":[{"name":"content","description":"Posts, pages and archives (HTML/markdown negotiation)"},{"name":"markdown","description":"Explicit .md representations"},{"name":"json","description":"Versioned JSON API (/api/v1/) — typed, rate limited"},{"name":"discovery","description":"Machine-readable discovery files"},{"name":"meta","description":"API index and error contract"}],"paths":{"/":{"get":{"operationId":"getHomepage","summary":"Homepage","description":"Site overview: featured venues, services, categories and FAQs. Serves text/html by default; send `Accept: text/markdown` (RFC 9110 q-values honoured) to receive a clean markdown representation of the same content. An Accept header that allows neither representation returns 406.","tags":["content"],"responses":{"200":{"description":"The page, as HTML or markdown depending on the Accept header.","headers":{"Vary":{"description":"Markdown responses always include Accept.","schema":{"type":"string"}}},"content":{"text/html":{"schema":{"type":"string","contentMediaType":"text/html"}},"text/markdown":{"schema":{"type":"string","contentMediaType":"text/markdown"}}}},"404":{"description":"Not found. The markdown body lists recovery links (homepage, llms.txt, sitemap, category hubs).","headers":{"Vary":{"description":"Always includes Accept.","schema":{"type":"string"}}},"content":{"text/markdown":{"schema":{"type":"string","contentMediaType":"text/markdown"}}}},"406":{"description":"The Accept header allows neither text/html nor text/markdown.","content":{"text/plain":{"schema":{"type":"string"}}}}}}},"/{slug}/":{"get":{"operationId":"getPage","summary":"Post or page by slug","description":"Any of the ~700 content pages: venue guestlist pages, table booking pages, guides, blog posts. Slugs are enumerated in https://londonnightguide.com/sitemap.xml and https://londonnightguide.com/llms.txt. Serves text/html by default; send `Accept: text/markdown` (RFC 9110 q-values honoured) to receive a clean markdown representation of the same content. An Accept header that allows neither representation returns 406.","tags":["content"],"parameters":[{"name":"slug","in":"path","required":true,"description":"Post or page slug, e.g. tape-club-guestlist","schema":{"type":"string"},"example":"tape-club-guestlist"}],"responses":{"200":{"description":"The page, as HTML or markdown depending on the Accept header.","headers":{"Vary":{"description":"Markdown responses always include Accept.","schema":{"type":"string"}}},"content":{"text/html":{"schema":{"type":"string","contentMediaType":"text/html"}},"text/markdown":{"schema":{"type":"string","contentMediaType":"text/markdown"}}}},"404":{"description":"Not found. The markdown body lists recovery links (homepage, llms.txt, sitemap, category hubs).","headers":{"Vary":{"description":"Always includes Accept.","schema":{"type":"string"}}},"content":{"text/markdown":{"schema":{"type":"string","contentMediaType":"text/markdown"}}}},"406":{"description":"The Accept header allows neither text/html nor text/markdown.","content":{"text/plain":{"schema":{"type":"string"}}}}}}},"/category/{slug}/":{"get":{"operationId":"getCategory","summary":"Category archive","description":"All pages in a category (clubs, events, dress-code, entry-fee, bottle-prices, …). Serves text/html by default; send `Accept: text/markdown` (RFC 9110 q-values honoured) to receive a clean markdown representation of the same content. An Accept header that allows neither representation returns 406.","tags":["content"],"parameters":[{"name":"slug","in":"path","required":true,"description":"Category slug, e.g. clubs","schema":{"type":"string"},"example":"tape-club-guestlist"}],"responses":{"200":{"description":"The page, as HTML or markdown depending on the Accept header.","headers":{"Vary":{"description":"Markdown responses always include Accept.","schema":{"type":"string"}}},"content":{"text/html":{"schema":{"type":"string","contentMediaType":"text/html"}},"text/markdown":{"schema":{"type":"string","contentMediaType":"text/markdown"}}}},"404":{"description":"Not found. The markdown body lists recovery links (homepage, llms.txt, sitemap, category hubs).","headers":{"Vary":{"description":"Always includes Accept.","schema":{"type":"string"}}},"content":{"text/markdown":{"schema":{"type":"string","contentMediaType":"text/markdown"}}}},"406":{"description":"The Accept header allows neither text/html nor text/markdown.","content":{"text/plain":{"schema":{"type":"string"}}}}}}},"/tag/{slug}/":{"get":{"operationId":"getTag","summary":"Tag archive","description":"All pages carrying a tag. Serves text/html by default; send `Accept: text/markdown` (RFC 9110 q-values honoured) to receive a clean markdown representation of the same content. An Accept header that allows neither representation returns 406.","tags":["content"],"parameters":[{"name":"slug","in":"path","required":true,"description":"Tag slug","schema":{"type":"string"},"example":"tape-club-guestlist"}],"responses":{"200":{"description":"The page, as HTML or markdown depending on the Accept header.","headers":{"Vary":{"description":"Markdown responses always include Accept.","schema":{"type":"string"}}},"content":{"text/html":{"schema":{"type":"string","contentMediaType":"text/html"}},"text/markdown":{"schema":{"type":"string","contentMediaType":"text/markdown"}}}},"404":{"description":"Not found. The markdown body lists recovery links (homepage, llms.txt, sitemap, category hubs).","headers":{"Vary":{"description":"Always includes Accept.","schema":{"type":"string"}}},"content":{"text/markdown":{"schema":{"type":"string","contentMediaType":"text/markdown"}}}},"406":{"description":"The Accept header allows neither text/html nor text/markdown.","content":{"text/plain":{"schema":{"type":"string"}}}}}}},"/index.md":{"get":{"operationId":"getHomepageMarkdown","summary":"Homepage as markdown","description":"Machine-readable overview of the site as markdown: services, the current list of open venues with guestlist links, categories and contact details. The fastest single request for orienting an agent.","tags":["markdown"],"responses":{"200":{"description":"Markdown overview of the site: services, open venues, categories, contact.","headers":{"Vary":{"description":"Always includes Accept.","schema":{"type":"string"}}},"content":{"text/markdown":{"schema":{"type":"string","contentMediaType":"text/markdown"}}}}}}},"/{slug}.md":{"get":{"operationId":"getPageMarkdown","summary":"Post or page as markdown","description":"Same content as /{slug}/ rendered as markdown. Sent with X-Robots-Tag: noindex (the canonical URL is /{slug}/).","tags":["markdown"],"parameters":[{"name":"slug","in":"path","required":true,"description":"Post or page slug","schema":{"type":"string"},"example":"tape-club-guestlist"}],"responses":{"200":{"description":"The page as markdown: title, venue facts, body, FAQs, booking links.","headers":{"Vary":{"description":"Always includes Accept.","schema":{"type":"string"}}},"content":{"text/markdown":{"schema":{"type":"string","contentMediaType":"text/markdown"}}}},"404":{"description":"Not found. The markdown body lists recovery links (homepage, llms.txt, sitemap, category hubs).","headers":{"Vary":{"description":"Always includes Accept.","schema":{"type":"string"}}},"content":{"text/markdown":{"schema":{"type":"string","contentMediaType":"text/markdown"}}}}}}},"/category/{slug}.md":{"get":{"operationId":"getCategoryMarkdown","summary":"Category archive as markdown","description":"The category's title, description and a markdown link list of every member page — the cheapest way to enumerate all pages on a topic (clubs, events, entry-fee, bottle-prices, …).","tags":["markdown"],"parameters":[{"name":"slug","in":"path","required":true,"description":"Category slug, e.g. clubs","schema":{"type":"string"},"example":"tape-club-guestlist"}],"responses":{"200":{"description":"Category title, description and a link list of member pages.","headers":{"Vary":{"description":"Always includes Accept.","schema":{"type":"string"}}},"content":{"text/markdown":{"schema":{"type":"string","contentMediaType":"text/markdown"}}}},"404":{"description":"Not found. The markdown body lists recovery links (homepage, llms.txt, sitemap, category hubs).","headers":{"Vary":{"description":"Always includes Accept.","schema":{"type":"string"}}},"content":{"text/markdown":{"schema":{"type":"string","contentMediaType":"text/markdown"}}}}}}},"/tag/{slug}.md":{"get":{"operationId":"getTagMarkdown","summary":"Tag archive as markdown","description":"The tag's title and a markdown link list of every page carrying the tag.","tags":["markdown"],"parameters":[{"name":"slug","in":"path","required":true,"description":"Tag slug","schema":{"type":"string"},"example":"tape-club-guestlist"}],"responses":{"200":{"description":"Tag title and a link list of member pages.","headers":{"Vary":{"description":"Always includes Accept.","schema":{"type":"string"}}},"content":{"text/markdown":{"schema":{"type":"string","contentMediaType":"text/markdown"}}}},"404":{"description":"Not found. The markdown body lists recovery links (homepage, llms.txt, sitemap, category hubs).","headers":{"Vary":{"description":"Always includes Accept.","schema":{"type":"string"}}},"content":{"text/markdown":{"schema":{"type":"string","contentMediaType":"text/markdown"}}}}}}},"/llms.txt":{"get":{"operationId":"getLlmsTxt","summary":"Agent guide (llmstxt.org format)","description":"When to use this site, how to call it, and curated links to every key page.","tags":["discovery"],"responses":{"200":{"description":"The llms.txt file.","content":{"text/plain":{"schema":{"type":"string","contentMediaType":"text/markdown"}}}}}}},"/openapi.json":{"get":{"operationId":"getOpenApiSpec","summary":"This specification","description":"The OpenAPI 3.1 document you are reading — fetch it to discover the full public surface programmatically.","tags":["discovery"],"responses":{"200":{"description":"OpenAPI 3.1 document.","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/sitemap.xml":{"get":{"operationId":"getSitemap","summary":"XML sitemap of all indexable URLs","description":"Every indexable URL on the site (~900 entries) with last-modified dates — the authoritative slug enumeration for getPage/getPageMarkdown.","tags":["discovery"],"responses":{"200":{"description":"Sitemap.","content":{"application/xml":{"schema":{"type":"string","contentMediaType":"application/xml"}}}}}}},"/feed/":{"get":{"operationId":"getRssFeed","summary":"RSS feed of the 50 most recent posts","description":"RSS 2.0 feed with titles, links, publication dates and meta descriptions of the newest content.","tags":["discovery"],"responses":{"200":{"description":"RSS 2.0 feed.","content":{"application/rss+xml":{"schema":{"type":"string","contentMediaType":"application/rss+xml"}}}}}}},"/api/v1/venues":{"get":{"operationId":"listVenues","summary":"Open venues as structured JSON","description":"Every currently-open partner venue with its area, music genre, opening hours, club nights, capacity, dress code, street address, nearest tube and booking links. The authoritative machine-readable venue list.","tags":["json"],"responses":{"200":{"description":"The venue list.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VenueList"}}}},"429":{"description":"Rate limit exceeded (120 requests/minute per client). Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/categories":{"get":{"operationId":"listCategories","summary":"Content categories as structured JSON","description":"All non-empty content categories with page counts and archive URLs (HTML and markdown).","tags":["json"],"responses":{"200":{"description":"The category list.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CategoryList"}}}},"429":{"description":"Rate limit exceeded (120 requests/minute per client). Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/events":{"get":{"operationId":"listEvents","summary":"Upcoming club nights as structured JSON","description":"Every upcoming club night across the partner venues, sorted by date: event name, description, start/end datetimes and the venue with its address and guestlist URL. Regenerated daily.","tags":["json"],"responses":{"200":{"description":"The upcoming events, soonest first.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EventList"}}}},"429":{"description":"Rate limit exceeded (120 requests/minute per client). Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/pages/{slug}":{"get":{"operationId":"getPageData","summary":"Post or page metadata as structured JSON","description":"Structured metadata for any post or page: title, meta description, publish/modified dates, categories, venue facts and FAQs. The full body is available at the markdownUrl in the response.","tags":["json"],"parameters":[{"name":"slug","in":"path","required":true,"description":"Post or page slug, e.g. tape-club-guestlist","schema":{"type":"string"},"example":"tape-club-guestlist"}],"responses":{"200":{"description":"The page metadata.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PageData"}}}},"404":{"description":"No post or page with that slug.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limit exceeded (120 requests/minute per client). Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api":{"get":{"operationId":"getApiIndex","summary":"API index","description":"JSON directory of the public endpoints, documentation and discovery files.","tags":["meta"],"responses":{"200":{"description":"Index of the API surface.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiIndex"}}}},"405":{"description":"Any method other than GET/HEAD.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limit exceeded (120 requests/minute per client). Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/{path}":{"get":{"operationId":"getUnknownApiPath","summary":"Unknown API path","description":"Any path under /api/ that is not a documented endpoint returns a structured JSON error with a resolution hint (all methods).","tags":["meta"],"parameters":[{"name":"path","in":"path","required":true,"description":"Any unrecognised API path.","schema":{"type":"string"}}],"responses":{"404":{"description":"Structured JSON error envelope.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limit exceeded (120 requests/minute per client). Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}}},"components":{"headers":{"RateLimitLimit":{"description":"Requests allowed per window (120).","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"RateLimitReset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}},"RateLimitPolicy":{"description":"The rate-limit policy, e.g. 120;w=60 (120 requests per 60-second window).","schema":{"type":"string"}}},"schemas":{"Venue":{"type":"object","required":["name","area","guestlistUrl"],"properties":{"name":{"type":"string","description":"Venue name.","examples":["Tape London"]},"area":{"type":"string","description":"London area.","examples":["Mayfair"]},"genre":{"type":"string","description":"Music policy.","examples":["Hip Hop, RnB & Afrobeats"]},"hours":{"type":"string","description":"Opening hours.","examples":["23:00-05:45"]},"nights":{"type":"array","items":{"type":"string"},"description":"Nights of the week the venue opens."},"capacity":{"type":"string","description":"Approximate capacity."},"dressCode":{"type":"string","description":"Door dress code."},"address":{"type":"string","description":"Street address."},"tubeStation":{"type":"string","description":"Nearest tube station."},"description":{"type":"string"},"guestlistUrl":{"type":"string","format":"uri","description":"Guestlist page (HTML/markdown negotiated)."},"guestlistMarkdownUrl":{"type":"string","format":"uri"},"tableBookingUrl":{"type":["string","null"],"format":"uri","description":"VIP table page, if the venue takes table bookings."}}},"VenueList":{"type":"object","required":["version","count","venues"],"properties":{"version":{"type":"string","const":"v1"},"count":{"type":"integer"},"venues":{"type":"array","items":{"$ref":"#/components/schemas/Venue"}}}},"Event":{"type":"object","required":["name","startDate"],"properties":{"name":{"type":"string","examples":["Tape London — Fridays"]},"description":{"type":["string","null"]},"startDate":{"type":"string","format":"date-time"},"endDate":{"type":["string","null"],"format":"date-time"},"venue":{"type":["object","null"],"properties":{"name":{"type":"string"},"url":{"type":["string","null"],"format":"uri","description":"Guestlist page for the venue."},"address":{"type":["string","null"]}}}}},"EventList":{"type":"object","required":["version","count","events"],"properties":{"version":{"type":"string","const":"v1"},"count":{"type":"integer"},"events":{"type":"array","items":{"$ref":"#/components/schemas/Event"}}}},"Category":{"type":"object","required":["slug","title","pageCount","url"],"properties":{"slug":{"type":"string","examples":["clubs"]},"title":{"type":"string"},"pageCount":{"type":"integer"},"url":{"type":"string","format":"uri"},"markdownUrl":{"type":"string","format":"uri"}}},"CategoryList":{"type":"object","required":["version","count","categories"],"properties":{"version":{"type":"string","const":"v1"},"count":{"type":"integer"},"categories":{"type":"array","items":{"$ref":"#/components/schemas/Category"}}}},"PageData":{"type":"object","required":["version","type","slug","title","canonicalUrl","markdownUrl"],"properties":{"version":{"type":"string","const":"v1"},"type":{"type":"string","enum":["post","page"]},"slug":{"type":"string"},"title":{"type":"string"},"h1":{"type":"string"},"metaDescription":{"type":["string","null"]},"canonicalUrl":{"type":"string","format":"uri"},"markdownUrl":{"type":"string","format":"uri","description":"Full page body as markdown."},"datePublished":{"type":["string","null"]},"dateModified":{"type":["string","null"]},"categories":{"type":"array","items":{"type":"string"}},"wordCount":{"type":["integer","null"]},"venue":{"type":["object","null"],"description":"Venue facts when the page is about a venue.","properties":{"name":{"type":"string"},"area":{"type":["string","null"]},"status":{"type":["string","null"],"enum":["open","closed",null]},"details":{"type":["object","null"]}}},"faqs":{"type":["array","null"],"items":{"type":"object","required":["question","answer"],"properties":{"question":{"type":"string"},"answer":{"type":"string"}}}}}},"ApiError":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["status","code","message","hint"],"properties":{"status":{"type":"integer","description":"HTTP status code.","example":404},"code":{"type":"string","description":"Machine-readable error code.","example":"not_found"},"message":{"type":"string","description":"Human-readable explanation.","example":"No API endpoint exists at /api/example."},"hint":{"type":"string","description":"How to resolve the error.","example":"GET https://londonnightguide.com/api for an endpoint index."}}}}},"ApiIndex":{"type":"object","required":["name","documentation","openapi","endpoints"],"properties":{"name":{"type":"string"},"description":{"type":"string"},"documentation":{"type":"string","format":"uri"},"openapi":{"type":"string","format":"uri"},"llms_txt":{"type":"string","format":"uri"},"sitemap":{"type":"string","format":"uri"},"authentication":{"type":"string","const":"none"},"version":{"type":"string","const":"v1"},"rate_limit":{"type":"string","description":"Human-readable rate-limit policy."},"endpoints":{"type":"array","items":{"type":"object","required":["method","path"],"properties":{"method":{"type":"string"},"path":{"type":"string"},"description":{"type":"string"}}}},"booking":{"type":"string"}}}}}}