{"openapi":"3.1.0","info":{"title":"Typersguild AI Integration API","version":"1.0.0","summary":"Public read-only Typersguild book catalog","description":"Public read-only access to the Typersguild book catalog. No authentication required.\n\n## Versioning\nCurrent version: **v1**. Canonical base URL: `https://typersguild.com/api/v1`.\nThe unversioned alias `https://typersguild.com/api/integrations/ai` is the same v1 surface and stays stable until a later version ships.\nBreaking changes are released as a new path prefix (`/api/v2/`). Additive, backwards-compatible fields may appear in v1 without a new version.\nClients may also send `API-Version: 1`; the server echoes `API-Version` on every catalog JSON response.\n\n## Deprecation\nA version is never removed without notice. At least **90 days** before removal, every response for that version includes:\n- `Deprecation: true` ([RFC 9745](https://www.rfc-editor.org/rfc/rfc9745))\n- `Sunset: <HTTP-date>` ([RFC 8594](https://www.rfc-editor.org/rfc/rfc8594))\nAfter the Sunset date the version returns HTTP 410 with the typed error object (`code`, `error`, `hint`). Docs: https://typersguild.com/developers#versioning\n\n## Rate limits\nCatalog JSON endpoints allow **90 requests per 60 seconds per IP**. HTTP 429 and other origin error responses include IETF `RateLimit`, `RateLimit-Policy`, `RateLimit-Limit`, `RateLimit-Remaining`, and `RateLimit-Reset`. Cached 200 responses omit per-IP remaining/reset so a CDN hit does not advertise another client's quota. HTTP 429 also includes `Retry-After`. ChatGPT and other trusted assistant user-agents are not throttled.\n\n## Errors\nEvery 4xx and 5xx response uses the `ErrorResponse` object: `error` (human-readable), `code` (machine-readable), and `hint` (what to do next).","contact":{"name":"Typersguild","email":"contact@typersguild.com","url":"https://typersguild.com/developers"}},"servers":[{"url":"https://typersguild.com/api/v1","description":"Version 1 (current)"},{"url":"https://typersguild.com/api/integrations/ai","description":"Unversioned alias of v1"}],"paths":{"/":{"get":{"operationId":"getCatalogIndex","summary":"Catalog API index","description":"Lists public catalog endpoints, version, OpenAPI URL, and error format.","parameters":[{"name":"API-Version","in":"header","required":false,"schema":{"type":"string","enum":["1"],"default":"1"},"description":"Optional. v1 is the current catalog API. Unversioned `/api/integrations/ai` URLs are aliases of `/api/v1`."}],"responses":{"200":{"description":"Catalog index","headers":{"API-Version":{"description":"Catalog API version served by this response.","schema":{"type":"string","enum":["1"],"example":"1"}},"Deprecation":{"description":"RFC 9745. Present and `true` when this API version has been deprecated. Absent on the current version.","schema":{"type":"string","example":"true"}},"Sunset":{"description":"RFC 8594 HTTP-date when a deprecated version will be removed. Sent only after deprecation is announced, with at least 90 days of notice.","schema":{"type":"string","example":"Thu, 01 Jan 2027 00:00:00 GMT"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CatalogIndex"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/search-books":{"get":{"operationId":"searchBooks","summary":"Search the Typersguild book catalog","description":"Search public books by title or author.","parameters":[{"name":"API-Version","in":"header","required":false,"schema":{"type":"string","enum":["1"],"default":"1"},"description":"Optional. v1 is the current catalog API. Unversioned `/api/integrations/ai` URLs are aliases of `/api/v1`."},{"name":"q","in":"query","required":true,"schema":{"type":"string","minLength":1},"description":"Search query (title or author)"},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":20,"default":10}}],"responses":{"200":{"description":"Search results","headers":{"API-Version":{"description":"Catalog API version served by this response.","schema":{"type":"string","enum":["1"],"example":"1"}},"Deprecation":{"description":"RFC 9745. Present and `true` when this API version has been deprecated. Absent on the current version.","schema":{"type":"string","example":"true"}},"Sunset":{"description":"RFC 8594 HTTP-date when a deprecated version will be removed. Sent only after deprecation is announced, with at least 90 days of notice.","schema":{"type":"string","example":"Thu, 01 Jan 2027 00:00:00 GMT"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BooksResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/popular-books":{"get":{"operationId":"getPopularBooks","summary":"List popular Typersguild books","description":"Returns popular public books users can practice typing on Typersguild.","parameters":[{"name":"API-Version","in":"header","required":false,"schema":{"type":"string","enum":["1"],"default":"1"},"description":"Optional. v1 is the current catalog API. Unversioned `/api/integrations/ai` URLs are aliases of `/api/v1`."},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":50,"default":20}}],"responses":{"200":{"description":"Popular books","headers":{"API-Version":{"description":"Catalog API version served by this response.","schema":{"type":"string","enum":["1"],"example":"1"}},"Deprecation":{"description":"RFC 9745. Present and `true` when this API version has been deprecated. Absent on the current version.","schema":{"type":"string","example":"true"}},"Sunset":{"description":"RFC 8594 HTTP-date when a deprecated version will be removed. Sent only after deprecation is announced, with at least 90 days of notice.","schema":{"type":"string","example":"Thu, 01 Jan 2027 00:00:00 GMT"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BooksResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/openapi.json":{"get":{"operationId":"getOpenApiSpec","summary":"OpenAPI 3.1 document","description":"Machine-readable OpenAPI description of this API.","parameters":[{"name":"API-Version","in":"header","required":false,"schema":{"type":"string","enum":["1"],"default":"1"},"description":"Optional. v1 is the current catalog API. Unversioned `/api/integrations/ai` URLs are aliases of `/api/v1`."}],"responses":{"200":{"description":"OpenAPI document","headers":{"API-Version":{"description":"Catalog API version served by this response.","schema":{"type":"string","enum":["1"],"example":"1"}},"Deprecation":{"description":"RFC 9745. Present and `true` when this API version has been deprecated. Absent on the current version.","schema":{"type":"string","example":"true"}},"Sunset":{"description":"RFC 8594 HTTP-date when a deprecated version will be removed. Sent only after deprecation is announced, with at least 90 days of notice.","schema":{"type":"string","example":"Thu, 01 Jan 2027 00:00:00 GMT"}}},"content":{"application/json":{"schema":{"type":"object"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}}},"components":{"responses":{"BadRequest":{"description":"Invalid request parameters","headers":{"RateLimit-Limit":{"description":"Maximum requests allowed in the current window (IETF RateLimit-Limit).","schema":{"type":"integer","example":90}},"RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer","example":89}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer","example":58}},"RateLimit-Policy":{"description":"Quota policy as `limit;w=windowSeconds` (IETF RateLimit-Policy).","schema":{"type":"string","example":"90;w=60"}},"RateLimit":{"description":"Combined IETF RateLimit field: `limit=…, remaining=…, reset=…`.","schema":{"type":"string","example":"limit=90, remaining=89, reset=58"}},"API-Version":{"description":"Catalog API version served by this response.","schema":{"type":"string","enum":["1"],"example":"1"}},"Deprecation":{"description":"RFC 9745. Present and `true` when this API version has been deprecated. Absent on the current version.","schema":{"type":"string","example":"true"}},"Sunset":{"description":"RFC 8594 HTTP-date when a deprecated version will be removed. Sent only after deprecation is announced, with at least 90 days of notice.","schema":{"type":"string","example":"Thu, 01 Jan 2027 00:00:00 GMT"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"NotFound":{"description":"No public API endpoint at this path","headers":{"RateLimit-Limit":{"description":"Maximum requests allowed in the current window (IETF RateLimit-Limit).","schema":{"type":"integer","example":90}},"RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer","example":89}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer","example":58}},"RateLimit-Policy":{"description":"Quota policy as `limit;w=windowSeconds` (IETF RateLimit-Policy).","schema":{"type":"string","example":"90;w=60"}},"RateLimit":{"description":"Combined IETF RateLimit field: `limit=…, remaining=…, reset=…`.","schema":{"type":"string","example":"limit=90, remaining=89, reset=58"}},"API-Version":{"description":"Catalog API version served by this response.","schema":{"type":"string","enum":["1"],"example":"1"}},"Deprecation":{"description":"RFC 9745. Present and `true` when this API version has been deprecated. Absent on the current version.","schema":{"type":"string","example":"true"}},"Sunset":{"description":"RFC 8594 HTTP-date when a deprecated version will be removed. Sent only after deprecation is announced, with at least 90 days of notice.","schema":{"type":"string","example":"Thu, 01 Jan 2027 00:00:00 GMT"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"MethodNotAllowed":{"description":"HTTP method is not allowed","headers":{"RateLimit-Limit":{"description":"Maximum requests allowed in the current window (IETF RateLimit-Limit).","schema":{"type":"integer","example":90}},"RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer","example":89}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer","example":58}},"RateLimit-Policy":{"description":"Quota policy as `limit;w=windowSeconds` (IETF RateLimit-Policy).","schema":{"type":"string","example":"90;w=60"}},"RateLimit":{"description":"Combined IETF RateLimit field: `limit=…, remaining=…, reset=…`.","schema":{"type":"string","example":"limit=90, remaining=89, reset=58"}},"API-Version":{"description":"Catalog API version served by this response.","schema":{"type":"string","enum":["1"],"example":"1"}},"Deprecation":{"description":"RFC 9745. Present and `true` when this API version has been deprecated. Absent on the current version.","schema":{"type":"string","example":"true"}},"Sunset":{"description":"RFC 8594 HTTP-date when a deprecated version will be removed. Sent only after deprecation is announced, with at least 90 days of notice.","schema":{"type":"string","example":"Thu, 01 Jan 2027 00:00:00 GMT"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"RateLimited":{"description":"Too many requests","headers":{"RateLimit-Limit":{"description":"Maximum requests allowed in the current window (IETF RateLimit-Limit).","schema":{"type":"integer","example":90}},"RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer","example":89}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer","example":58}},"RateLimit-Policy":{"description":"Quota policy as `limit;w=windowSeconds` (IETF RateLimit-Policy).","schema":{"type":"string","example":"90;w=60"}},"RateLimit":{"description":"Combined IETF RateLimit field: `limit=…, remaining=…, reset=…`.","schema":{"type":"string","example":"limit=90, remaining=89, reset=58"}},"API-Version":{"description":"Catalog API version served by this response.","schema":{"type":"string","enum":["1"],"example":"1"}},"Deprecation":{"description":"RFC 9745. Present and `true` when this API version has been deprecated. Absent on the current version.","schema":{"type":"string","example":"true"}},"Sunset":{"description":"RFC 8594 HTTP-date when a deprecated version will be removed. Sent only after deprecation is announced, with at least 90 days of notice.","schema":{"type":"string","example":"Thu, 01 Jan 2027 00:00:00 GMT"}},"Retry-After":{"description":"Seconds to wait before retrying. Sent on HTTP 429 together with RateLimit headers.","schema":{"type":"integer","example":32}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"InternalError":{"description":"Unexpected server error","headers":{"RateLimit-Limit":{"description":"Maximum requests allowed in the current window (IETF RateLimit-Limit).","schema":{"type":"integer","example":90}},"RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer","example":89}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer","example":58}},"RateLimit-Policy":{"description":"Quota policy as `limit;w=windowSeconds` (IETF RateLimit-Policy).","schema":{"type":"string","example":"90;w=60"}},"RateLimit":{"description":"Combined IETF RateLimit field: `limit=…, remaining=…, reset=…`.","schema":{"type":"string","example":"limit=90, remaining=89, reset=58"}},"API-Version":{"description":"Catalog API version served by this response.","schema":{"type":"string","enum":["1"],"example":"1"}},"Deprecation":{"description":"RFC 9745. Present and `true` when this API version has been deprecated. Absent on the current version.","schema":{"type":"string","example":"true"}},"Sunset":{"description":"RFC 8594 HTTP-date when a deprecated version will be removed. Sent only after deprecation is announced, with at least 90 days of notice.","schema":{"type":"string","example":"Thu, 01 Jan 2027 00:00:00 GMT"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"schemas":{"ErrorResponse":{"type":"object","additionalProperties":false,"required":["error","code","hint"],"properties":{"error":{"type":"string","description":"Human-readable error message"},"code":{"type":"string","description":"Machine-readable error code (e.g. missing_query, not_found, rate_limited)"},"hint":{"type":"string","description":"How to resolve the error or which URL to call next"}}},"CatalogIndex":{"type":"object","properties":{"name":{"type":"string"},"version":{"type":"string","example":"1.0.0"},"documentation":{"type":"string","format":"uri"},"openapi":{"type":"string","format":"uri"}}},"CatalogBook":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"author":{"type":"string"},"slug":{"type":"string","nullable":true},"isPremium":{"type":"boolean"},"totalPages":{"type":"integer","nullable":true},"coverUrl":{"type":"string","format":"uri","nullable":true,"description":"Sized cover thumbnail URL via /covers/{slug}.jpg?w= (default 120px wide) for markdown images"},"bookUrl":{"type":"string","format":"uri","description":"Canonical book page URL (https://typersguild.com/books/{slug})"}}},"BooksResponse":{"type":"object","properties":{"total":{"type":"integer"},"books":{"type":"array","items":{"$ref":"#/components/schemas/CatalogBook"}}}}}}}