For AI agents
This page is the compact reference for using the Close API from an LLM or agent. Humans: the Quickstart is friendlier.
Basics
Section titled “Basics”- Base URL:
https://api.close.city - Auth:
Authorization: Bearer <key>, where the key is ack_live_API key. - Machine contract:
/openapi.json(OpenAPI 3.1, with aserversblock and a bearersecurityScheme). Also mirrored atdocs.close.city/openapi.json. - Free, keyless routes:
GET /v1/health,/v1/last-updated,/v1/meta/modes,/v1/meta/destination-types,/v1/meta/vintage,/v1/places,/v1/isochrone/meta. Everything else needs a key.
Metering
Section titled “Metering”- 1 token per returned row, minimum 1 per request. Isochrones cost 10 tokens per
contour instead. A
304 Not Modifiedis free. - Every metered reply carries
X-Tokens-ChargedandX-Tokens-Remaining— read them to track spend. - Out of tokens returns
429with the slugtokens-exhausted.
Getting a key programmatically
Section titled “Getting a key programmatically”An agent can complete sign-up on a user’s behalf. The user must read a six-digit code from their own inbox — that human-in-the-loop step is deliberate. Keep a cookie jar across the calls:
# 1. Request a sign-in code for the user's email.curl -s -X POST https://account.close.city/auth/request \ -H "Content-Type: application/json" \ -d '{"email": "user@example.com"}'
# 2. The user reads the six-digit code from their email and gives it to you.
# 3. Verify it; this sets the session cookie in the jar.curl -s -c jar.txt -X POST https://account.close.city/auth/verify-code \ -H "Content-Type: application/json" \ -d '{"email": "user@example.com", "code": "123456"}'
# 4. Read the CSRF token for the session.CSRF=$(curl -s -b jar.txt https://account.close.city/auth/csrf \ | python3 -c "import json,sys; print(json.load(sys.stdin)['csrf'])")
# 5. Mint an API key (shown once).curl -s -b jar.txt -X POST https://account.close.city/keys \ -H "Content-Type: application/json" -H "X-CSRF-Token: $CSRF" \ -d '{"label": "my-agent"}'# -> {"key": "ck_live_...", "prefix": "ck_live_xxxx"}The new account receives 5,000 free tokens. Store the key; it is shown only once.
Errors
Section titled “Errors”Errors are RFC 9457 application/problem+json. Match on the slug (the last
path segment of type), never the human title:
{ "type": "https://api.close.city/problems/invalid-mode", "title": "Unknown mode 'drive'.", "status": 400, "detail": "Valid modes: bike, transit, walk." }Common slugs: missing-key, invalid-key (401); tokens-exhausted,
rate-limited (429, with Retry-After); invalid-parameters, invalid-cursor
(400); block-not-found, no-isochrone-data (404).
Footguns
Section titled “Footguns”- Modes are
walk,bike,transit— there is no drive mode. - GEOIDs are strings with leading zeros. Do not coerce them to integers.
- Areal routes return a numeric
mode_id, not the stringmode. Join/v1/meta/modesto label it. The block and point routes return the string. - A missing (block, type) row means “not reachable within 30 minutes”, not zero. See The data.
- Pagination is by opaque cursor. Follow
next_cursoruntil it is null. The Python SDK does this automatically; the R SDK does too, unless you passpaginate = FALSE. - Neither SDK retries. On
429/5xx, back off usingRetry-Afteryourself.
MCP server
Section titled “MCP server”Close runs a remote Model Context Protocol
server at https://mcp.close.city/mcp, so agents can call the API as tools
instead of writing HTTP requests. Tools: find_place and get_catalog (free),
walkability_summary, nearby_pois, isochrone, blocks_in_area (metered),
and request_sign_in / redeem_code to get the user a free key in-chat.
Add it in Claude Code:
claude mcp add --transport http close https://mcp.close.city/mcp \ --header "Authorization: Bearer ck_live_your_key"Or in a client that reads a JSON config (Cursor, Claude Desktop):
{ "mcpServers": { "close": { "type": "http", "url": "https://mcp.close.city/mcp", "headers": { "Authorization": "Bearer ck_live_your_key" } } }}Without the key header, the free tools still work and the metered ones tell the agent to sign up.
Machine-readable files
Section titled “Machine-readable files”/openapi.json— the full contract./llms.txt— this documentation, indexed for LLMs, with/llms-full.txtfor the complete text.