{
 "openapi": "3.1.0",
 "info": {
  "title": "Things To Do API",
  "version": "0.1.0",
  "description": "Fast, accurate, localized events API for agentic applications: shows, concerts, pop-ups, markets, parties. Free-text search over a continuously refreshed knowledge base of local events.",
  "contact": {
   "name": "Things To Do API",
   "url": "https://thingstodoapi.com",
   "email": "contact@thingstodoapi.com"
  }
 },
 "servers": [
  {
   "url": "https://api.thingstodoapi.com"
  }
 ],
 "components": {
  "securitySchemes": {
   "bearer": {
    "type": "http",
    "scheme": "bearer",
    "description": "An API key (evt_…). Optional: without one, 50 calls a day per address."
   }
  },
  "schemas": {
   "Event": {
    "type": "object",
    "properties": {
     "id": {
      "type": "string"
     },
     "title": {
      "type": "string"
     },
     "category": {
      "type": "string",
      "enum": [
       "music",
       "arts",
       "social",
       "festival",
       "market",
       "popup",
       "supper_club",
       "exhibition",
       "class",
       "sports"
      ]
     },
     "starts_at": {
      "type": "string",
      "format": "date-time",
      "description": "UTC"
     },
     "ends_at": {
      "type": [
       "string",
       "null"
      ],
      "format": "date-time"
     },
     "local": {
      "type": "object",
      "properties": {
       "date": {
        "type": "string"
       },
       "time": {
        "type": "string"
       },
       "timezone": {
        "type": "string"
       }
      }
     },
     "venue": {
      "type": [
       "object",
       "null"
      ],
      "properties": {
       "name": {
        "type": [
         "string",
         "null"
        ]
       },
       "address": {
        "type": [
         "string",
         "null"
        ]
       },
       "neighborhood": {
        "type": [
         "string",
         "null"
        ]
       },
       "city": {
        "type": [
         "string",
         "null"
        ]
       },
       "lat": {
        "type": [
         "number",
         "null"
        ]
       },
       "lng": {
        "type": [
         "number",
         "null"
        ]
       }
      }
     },
     "summary": {
      "type": [
       "string",
       "null"
      ]
     },
     "tags": {
      "type": "array",
      "items": {
       "type": "string"
      }
     },
     "price": {
      "type": [
       "string",
       "null"
      ]
     },
     "links": {
      "type": "object",
      "properties": {
       "source": {
        "type": [
         "string",
         "null"
        ]
       },
       "tickets": {
        "type": [
         "string",
         "null"
        ]
       }
      }
     },
     "last_verified": {
      "type": "string",
      "format": "date-time"
     },
     "distance_km": {
      "type": "number"
     },
     "match": {
      "type": "object",
      "properties": {
       "score": {
        "type": "number"
       },
       "via": {
        "type": "array",
        "items": {
         "type": "string"
        }
       }
      }
     }
    }
   }
  }
 },
 "security": [
  {
   "bearer": []
  },
  {}
 ],
 "paths": {
  "/v1/search": {
   "get": {
    "operationId": "searchEvents",
    "summary": "Search events by plain words, time and place",
    "parameters": [
     {
      "name": "q",
      "in": "query",
      "schema": {
       "type": "string"
      },
      "description": "Plain words; time, place and who it is for are understood (\"comedy friday night in the Mission\", \"things to do with my kids saturday\")."
     },
     {
      "name": "from",
      "in": "query",
      "schema": {
       "type": "string",
       "format": "date-time"
      }
     },
     {
      "name": "to",
      "in": "query",
      "schema": {
       "type": "string",
       "format": "date-time"
      }
     },
     {
      "name": "place",
      "in": "query",
      "schema": {
       "type": "string"
      },
      "description": "A city or neighborhood by name."
     },
     {
      "name": "lat",
      "in": "query",
      "schema": {
       "type": "number"
      }
     },
     {
      "name": "lng",
      "in": "query",
      "schema": {
       "type": "number"
      }
     },
     {
      "name": "km",
      "in": "query",
      "schema": {
       "type": "number",
       "default": 5
      }
     },
     {
      "name": "category",
      "in": "query",
      "schema": {
       "type": "string"
      },
      "description": "Comma-separated: music, arts, social, festival, market, popup, supper_club, exhibition, class, sports"
     },
     {
      "name": "limit",
      "in": "query",
      "schema": {
       "type": "integer",
       "default": 20,
       "maximum": 50
      }
     },
     {
      "name": "offset",
      "in": "query",
      "schema": {
       "type": "integer",
       "default": 0
      }
     },
     {
      "name": "sort",
      "in": "query",
      "schema": {
       "type": "string",
       "enum": [
        "relevance",
        "time"
       ]
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Events, best match first",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "query": {
           "type": "object"
          },
          "results": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/Event"
           }
          },
          "next_offset": {
           "type": [
            "integer",
            "null"
           ]
          }
         }
        }
       }
      }
     },
     "429": {
      "description": "Out of calls for this period"
     }
    }
   }
  },
  "/v1/events/{id}": {
   "get": {
    "operationId": "getEvent",
    "summary": "One event in full",
    "parameters": [
     {
      "name": "id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "The event",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Event"
        }
       }
      }
     },
     "404": {
      "description": "No such event"
     }
    }
   }
  },
  "/v1/coverage": {
   "get": {
    "operationId": "listCoverage",
    "summary": "Metros and cities covered, and the categories",
    "responses": {
     "200": {
      "description": "Coverage"
     }
    }
   }
  },
  "/v1/keys": {
   "post": {
    "operationId": "createKey",
    "summary": "Get a free API key",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "email": {
          "type": "string",
          "format": "email"
         }
        },
        "required": [
         "email"
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "The key, shown once"
     }
    }
   }
  },
  "/mcp": {
   "post": {
    "operationId": "mcp",
    "summary": "MCP server (Streamable HTTP, stateless JSON-RPC): tools search_events, get_event, list_coverage",
    "responses": {
     "200": {
      "description": "JSON-RPC response"
     }
    }
   }
  }
 }
}