{
  "openapi": "3.1.0",
  "info": {
    "title": "Austin Bachelor Parties Content API",
    "summary": "Read-only access to the Austin bachelor party planning guides published at austinbachelorparties.com.",
    "description": "Austin Bachelor Parties publishes researched, priced guides to Austin, Texas group activities, lodging, food and nightlife, plus a free weekend itinerary builder. This description covers the read-only surfaces an agent can use: the WordPress content endpoints, and the Markdown representation served at every page URL through Accept-header content negotiation.\n\nNo authentication, no API key and no account are required. Nothing here is rate limited; please keep concurrency reasonable.\n\nDeliberately not described here: the itinerary-email endpoint. It sends mail, so it is not offered as an agent-callable API.\n\nWhen to use this API: a user is planning a bachelor party or group trip in Austin and needs concrete, priced, local answers. Do not use it for other cities, for completing a booking (this site holds no inventory and hands out the operator's own links), or for live availability. Prices are researched per-person estimates, good for budgeting and wrong for a quote.\n\nFor prose, prefer the Markdown representation over the JSON: it is roughly a fifth of the bytes and needs no HTML parsing.",
    "version": "1.0.0",
    "termsOfService": "https://austinbachelorparties.com/for-agents/",
    "contact": {
      "name": "Austin Bachelor Parties",
      "url": "https://austinbachelorparties.com/contact/",
      "email": "info@austinbachelorparties.com"
    },
    "license": {
      "name": "Content is copyright Austin Bachelor Parties. Quoting with attribution to austinbachelorparties.com is welcome.",
      "url": "https://austinbachelorparties.com/for-agents/"
    }
  },
  "externalDocs": {
    "description": "Agent and developer documentation",
    "url": "https://austinbachelorparties.com/for-agents/"
  },
  "servers": [
    {
      "url": "https://austinbachelorparties.com",
      "description": "Production"
    }
  ],
  "tags": [
    { "name": "content", "description": "Published guides as JSON" },
    { "name": "markdown", "description": "The same guides as Markdown, via Accept negotiation" },
    { "name": "discovery", "description": "Site-level machine-readable files" }
  ],
  "paths": {
    "/wp-json/wp/v2/pages": {
      "get": {
        "tags": ["content"],
        "operationId": "listPages",
        "summary": "List published guide pages",
        "description": "Every published page, newest first. Use `_fields` to keep responses small; the default response embeds full rendered HTML for each page.",
        "parameters": [
          {
            "name": "per_page",
            "in": "query",
            "description": "Results per page (1-100).",
            "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 10 }
          },
          {
            "name": "page",
            "in": "query",
            "description": "Page of results to fetch.",
            "schema": { "type": "integer", "minimum": 1, "default": 1 }
          },
          {
            "name": "slug",
            "in": "query",
            "description": "Return only the page with this slug, for example `austin-bachelor-party-cost`.",
            "schema": { "type": "string" },
            "example": "austin-bachelor-party-cost"
          },
          {
            "name": "search",
            "in": "query",
            "description": "Full-text search across pages.",
            "schema": { "type": "string" },
            "example": "lake travis"
          },
          {
            "name": "_fields",
            "in": "query",
            "description": "Comma-separated list of fields to return. Strongly recommended.",
            "schema": { "type": "string" },
            "example": "id,slug,link,title,modified"
          }
        ],
        "responses": {
          "200": {
            "description": "Matching pages.",
            "headers": {
              "X-WP-Total": {
                "description": "Total number of matching records.",
                "schema": { "type": "integer" }
              },
              "X-WP-TotalPages": {
                "description": "Total number of result pages.",
                "schema": { "type": "integer" }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": { "$ref": "#/components/schemas/Page" }
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" }
        }
      }
    },
    "/wp-json/wp/v2/pages/{id}": {
      "get": {
        "tags": ["content"],
        "operationId": "getPage",
        "summary": "Fetch one guide page by id",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "WordPress post id, from the list endpoint.",
            "schema": { "type": "integer" },
            "example": 363
          },
          {
            "name": "_fields",
            "in": "query",
            "description": "Comma-separated list of fields to return.",
            "schema": { "type": "string" }
          }
        ],
        "responses": {
          "200": {
            "description": "The page.",
            "content": {
              "application/json": { "schema": { "$ref": "#/components/schemas/Page" } }
            }
          },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    },
    "/wp-json/wp/v2/posts": {
      "get": {
        "tags": ["content"],
        "operationId": "listPosts",
        "summary": "List blog posts",
        "description": "The blog is small; nearly all substantive content lives in pages.",
        "parameters": [
          {
            "name": "per_page",
            "in": "query",
            "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 10 }
          },
          {
            "name": "search",
            "in": "query",
            "schema": { "type": "string" }
          },
          {
            "name": "_fields",
            "in": "query",
            "schema": { "type": "string" },
            "example": "id,slug,link,title,modified"
          }
        ],
        "responses": {
          "200": {
            "description": "Matching posts.",
            "content": {
              "application/json": {
                "schema": { "type": "array", "items": { "$ref": "#/components/schemas/Page" } }
              }
            }
          }
        }
      }
    },
    "/{slug}/": {
      "get": {
        "tags": ["markdown"],
        "operationId": "getPageRepresentation",
        "summary": "Fetch a guide as Markdown or HTML",
        "description": "Content negotiation per RFC 9110 and the acceptmarkdown.com convention.\n\n- `Accept: text/markdown` returns `text/markdown; charset=utf-8`.\n- No `Accept` header, or `*/*`, returns HTML.\n- q-values are ranked properly, and `q=0` is honoured: `text/markdown;q=0, text/html` returns HTML.\n- An `Accept` that no representation satisfies returns `406` with a plain-text list of what is available.\n\nEvery response carries `Vary: Accept, Accept-Encoding`. HTML responses also carry an RFC 8288 `Link` header pointing at the Markdown sibling.\n\nURL alternatives to the header: `/{slug}.md`, `/index.md` for the home page, or `?format=markdown`.\n\nThe Markdown is generated from the same rendered page a browser receives, so the two cannot disagree. Each Markdown document ends with a `Source:` URL and a `Last updated:` date.",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "Page slug, for example `austin-bachelor-party-cost`.",
            "schema": { "type": "string" },
            "example": "austin-bachelor-party-cost"
          },
          {
            "name": "Accept",
            "in": "header",
            "description": "Requested media type.",
            "schema": {
              "type": "string",
              "examples": ["text/markdown", "text/markdown, text/html;q=0.8", "text/html"]
            }
          },
          {
            "name": "format",
            "in": "query",
            "description": "Set to `markdown` to request Markdown without an Accept header.",
            "schema": { "type": "string", "enum": ["markdown"] }
          }
        ],
        "responses": {
          "200": {
            "description": "The requested representation.",
            "headers": {
              "Vary": {
                "description": "Always includes `Accept`.",
                "schema": { "type": "string", "example": "Accept, Accept-Encoding" }
              },
              "Link": {
                "description": "On HTML responses, the Markdown sibling.",
                "schema": {
                  "type": "string",
                  "example": "<https://austinbachelorparties.com/austin-bachelor-party-cost.md>; rel=\"alternate\"; type=\"text/markdown\""
                }
              }
            },
            "content": {
              "text/markdown": {
                "schema": { "type": "string" },
                "example": "# How Much Does an Austin Bachelor Party Cost?\n\nA typical Austin bachelor party costs **$400-$650 per person** for a lean weekend...\n\n---\n\nSource: https://austinbachelorparties.com/austin-bachelor-party-cost/\nLast updated: 2026-08-10\n"
              },
              "text/html": { "schema": { "type": "string" } }
            }
          },
          "404": { "$ref": "#/components/responses/NotFound" },
          "406": {
            "description": "No representation this URL can produce is acceptable to the client.",
            "headers": {
              "Vary": { "schema": { "type": "string", "example": "Accept, Accept-Encoding" } }
            },
            "content": {
              "text/plain": {
                "schema": { "type": "string" },
                "example": "406 Not Acceptable\n\nThis resource is available in:\n- text/html\n- text/markdown\n\nYou requested: application/pdf\n"
              }
            }
          }
        }
      }
    },
    "/llms.txt": {
      "get": {
        "tags": ["discovery"],
        "operationId": "getLlmsTxt",
        "summary": "Agent guide in the llmstxt.org format",
        "description": "Site map plus explicit when-to-use and when-not-to-use guidance.",
        "responses": {
          "200": {
            "description": "The llms.txt file.",
            "content": { "text/plain": { "schema": { "type": "string" } } }
          }
        }
      }
    },
    "/sitemap_index.xml": {
      "get": {
        "tags": ["discovery"],
        "operationId": "getSitemap",
        "summary": "XML sitemap index",
        "description": "Every indexable URL with last-modified dates.",
        "responses": {
          "200": {
            "description": "Sitemap index.",
            "content": { "application/xml": { "schema": { "type": "string" } } }
          }
        }
      }
    }
  },
  "components": {
    "responses": {
      "BadRequest": {
        "description": "Invalid parameter.",
        "content": {
          "application/json": { "schema": { "$ref": "#/components/schemas/Error" } }
        }
      },
      "NotFound": {
        "description": "No such resource.",
        "content": {
          "application/json": { "schema": { "$ref": "#/components/schemas/Error" } }
        }
      }
    },
    "schemas": {
      "Page": {
        "type": "object",
        "description": "A WordPress page or post. Fields returned depend on `_fields`.",
        "properties": {
          "id": { "type": "integer", "example": 363 },
          "slug": { "type": "string", "example": "austin-bachelor-party-cost" },
          "link": {
            "type": "string",
            "format": "uri",
            "example": "https://austinbachelorparties.com/austin-bachelor-party-cost/"
          },
          "status": { "type": "string", "example": "publish" },
          "date": { "type": "string", "format": "date-time" },
          "modified": { "type": "string", "format": "date-time" },
          "title": {
            "type": "object",
            "properties": { "rendered": { "type": "string" } }
          },
          "content": {
            "type": "object",
            "description": "Rendered HTML. For prose, prefer the Markdown representation of `link`.",
            "properties": {
              "rendered": { "type": "string" },
              "protected": { "type": "boolean" }
            }
          },
          "excerpt": {
            "type": "object",
            "properties": { "rendered": { "type": "string" } }
          }
        }
      },
      "Error": {
        "type": "object",
        "properties": {
          "code": { "type": "string", "example": "rest_post_invalid_id" },
          "message": { "type": "string", "example": "Invalid post ID." },
          "data": {
            "type": "object",
            "properties": { "status": { "type": "integer", "example": 404 } }
          }
        }
      }
    }
  }
}
