HannahOutThere Connector API
Travel discovery and personal trip lists, exposed for AI assistants.
Base URL: https://hannahoutthere.com/api/connector/v1
Version: v1
Contact: hello@hannahoutthere.com
HannahOutThere is a travel discovery site built around big umbrella questions — where in the world to see whales, the most beautiful beaches, the best October trips — answered with curated stories and a growing library of places, each with an honest firsthand, research, or discovery pick, five-dimension scores, and the best months to go.
Authentication
- Discovery search (
GET /search) is public. No account, no key, no token. - My World endpoints require OAuth 2.0. The user connects their free HannahOutThere account (email + password or Google sign-in) and grants the
my-worldscope. Calls useAuthorization: Bearer <access_token>.
Endpoints
GET /api/connector/v1/search
Search stories and places with a plain-English travel question. The query is parsed for months, seasons, and locations.
Query parameters
| Parameter | Required | Default | Description |
|---|---|---|---|
q | yes | — | Plain-English question, e.g. where to go in October |
limit | no | 5 | Max results per section, 1–10 |
type | no | both | story, world-item, or both |
Example: GET /api/connector/v1/search?q=where+to+go+in+October&limit=3
Response (200): JSON with the original query, what was parsed from it (months, seasons, locations), and two result lists:
stories:title,hook,url,image,bestMonths(1–12)worldItems:name,url,type,description,whyItMatters,hannahTake(her personal take, when available),image,scores(distinctiveness, impact, depth, experienceQuality, effortReward),hannahStatus(Firsthand Pick / Research Pick / Discovery Pick),bestMonths
Errors: 400 missing/invalid q or type; 429 rate limit exceeded (see below).
GET /api/connector/v1/my-world
List the authenticated user's saved places, in their display order.
Auth: Bearer token required. Example response item: name, url, type, image, hannahStatus, plus the user's own status, rating, and visitedDate.
Errors: 401 missing or invalid token.
POST /api/world-items/{slug}/save
Save a place to the user's My World. No request body needed; the place is identified by its slug (the last part of its world-item URL).
Auth: Bearer token required. Response (200): the saved record. Errors: 401 not signed in; 404 unknown slug.
PATCH /api/world-items/{slug}/save
Update the user's entry for a saved place.
Auth: Bearer token required. Request body (JSON, all fields optional): status, rating, privateNote, visitedDate (date string).
Response (200): the updated record. Errors: 400 unable to update; 401 not signed in.
DELETE /api/world-items/{slug}/save
Remove a place from the user's My World.
Auth: Bearer token required. Response (200): {"ok": true}. Errors: 400 unable to remove; 401 not signed in; 404 unknown slug.
OAuth 2.0
- Authorization URL:
https://hannahoutthere.com/api/oauth/authorize - Token URL:
https://hannahoutthere.com/api/oauth/token - Revocation URL:
https://hannahoutthere.com/api/oauth/revoke(RFC 7009) - Flow: authorization code with PKCE (S256).
client_secretis also accepted for confidential clients. - Scope:
my-world(read + write together). - Token lifetimes: access tokens 24 hours; refresh tokens 1 year and rotated on each use (the old refresh token is revoked when a new one is issued).
Rate limits
60 requests per minute per IP across connector routes (in-memory sliding window). Exceeding it returns 429 with a Retry-After header (seconds).
Error format
Errors are JSON: {"error": "<human-readable message>"} with the HTTP status code indicating the kind of failure (400 bad request, 401 authentication required, 404 not found, 429 rate limited).