--- title: "API · Furkan Bayraktar" description: "A read-only JSON API for this site: my bio, posts, talks, and open source work. No authentication. OpenAPI specification at /openapi.json." canonical: "https://furkanbayraktar.com/api" --- # API A read-only JSON API for this site. It returns the same content as the pages and the MCP server: my bio, my posts, and my talks and open source work. Six GET endpoints, no authentication, no API keys. ## Base URL https://furkanbayraktar.com/api/v1 The version is part of the path. GET /api/v1 returns the endpoint list as JSON. ## Authentication None. Every endpoint is public and read-only. Send a plain GET; no headers are required. ## Endpoints ### GET /api/v1 Index of the API. The endpoint list with links to the OpenAPI document, the documentation, the API catalog, and the MCP server. operationId: getApiIndex ```sh curl -s https://furkanbayraktar.com/api/v1 ``` 200 application/json ```json { "name": "furkanbayraktar.com API", "version": "1", "description": "A read-only JSON API for this site. It returns the same content as the pages and the MCP server: my bio, my posts, and my talks and open source work. Six GET endpoints, no authentication, no API keys.", "baseUrl": "https://furkanbayraktar.com/api/v1", "authentication": { "type": "none" }, "openapi": "https://furkanbayraktar.com/openapi.json", "docs": "https://furkanbayraktar.com/api", "docsMarkdown": "https://furkanbayraktar.com/api.md", "apiCatalog": "https://furkanbayraktar.com/.well-known/api-catalog", "mcp": "https://furkanbayraktar.com/mcp", "endpoints": [ { "method": "GET", "path": "/api/v1", "url": "https://furkanbayraktar.com/api/v1", "operationId": "getApiIndex", "summary": "Index of the API." } ] } ``` Note: endpoints is shortened here to the first of 6. ### GET /api/v1/about The bio. Name, current role, the career chapters from the about page as Markdown paragraphs, and profile links. operationId: getAbout. MCP tool: about_furkan. ```sh curl -s https://furkanbayraktar.com/api/v1/about ``` 200 application/json ```json { "name": "Furkan Bayraktar", "role": "Engineering Team Lead at Lovable", "description": "I lead engineering teams at Lovable in Stockholm. Before that I was co-founder and CTO of Scrintal for five years, and I co-developed Polylith.", "url": "https://furkanbayraktar.com/", "chapters": [ { "id": "now", "label": "Now", "paragraphs": [ "I lead engineering teams at [Lovable](https://lovable.dev), an AI-powered platform for building web apps and websites, based in Stockholm. I joined in January 2026." ] } ], "profiles": [ { "id": "github", "label": "GitHub", "href": "https://github.com/furkan3ayraktar" }, { "id": "linkedin", "label": "LinkedIn", "href": "https://www.linkedin.com/in/furkanbayraktar" }, { "id": "x", "label": "X", "href": "https://x.com/furkan3ayraktar" } ], "links": { "html": "https://furkanbayraktar.com/about", "markdown": "https://furkanbayraktar.com/about.md" } } ``` Note: chapters is shortened here to the first of 3. ### GET /api/v1/posts List posts, newest first. Each item has slug, title, date, description, tags, reading time, and URLs. Filter with tag; page with limit and cursor. Fetch the full text with GET /api/v1/posts/{slug}. operationId: listPosts. MCP tool: list_writing. - `tag` (query, optional, string, one of clojure, type-systems): Return only posts carrying this tag. - `limit` (query, optional, integer, 1 to 50, default 20): Maximum number of posts to return. - `cursor` (query, optional, string): Opaque cursor from a previous response's nextCursor. Returns the posts after it. ```sh curl -s https://furkanbayraktar.com/api/v1/posts?limit=1 ``` 200 application/json ```json { "items": [ { "slug": "the-wrong-question-about-type-systems", "title": "The Wrong Question About Type Systems", "date": "2025-12-14", "description": "After 8 years of Clojure, the 'don't you miss types?' question stopped making sense. It's not safety vs. danger. It's discipline vs. enforcement, and the cost of coordination.", "tags": [ "clojure", "type-systems" ], "readingMinutes": 10, "url": "https://furkanbayraktar.com/writing/the-wrong-question-about-type-systems", "markdownUrl": "https://furkanbayraktar.com/writing/the-wrong-question-about-type-systems.md", "apiUrl": "https://furkanbayraktar.com/api/v1/posts/the-wrong-question-about-type-systems" } ], "total": 1, "nextCursor": null } ``` ### GET /api/v1/posts/{slug} One post with its full text. The post's metadata plus its complete body as Markdown. operationId: getPost. MCP tool: read_post. - `slug` (path, required, string): The post slug, as listed by GET /api/v1/posts. ```sh curl -s https://furkanbayraktar.com/api/v1/posts/the-wrong-question-about-type-systems ``` 200 application/json ```json { "slug": "the-wrong-question-about-type-systems", "title": "The Wrong Question About Type Systems", "date": "2025-12-14", "description": "After 8 years of Clojure, the 'don't you miss types?' question stopped making sense. It's not safety vs. danger. It's discipline vs. enforcement, and the cost of coordination.", "tags": [ "clojure", "type-systems" ], "readingMinutes": 10, "url": "https://furkanbayraktar.com/writing/the-wrong-question-about-type-systems", "markdownUrl": "https://furkanbayraktar.com/writing/the-wrong-question-about-type-systems.md", "apiUrl": "https://furkanbayraktar.com/api/v1/posts/the-wrong-question-about-type-systems", "image": "https://furkanbayraktar.com/og/the-wrong-question-about-type-systems.png", "markdown": "After more than eight years of professional Clojure development, I still get asked the same question: \"Don't you miss types?\"\n\nI recently swapped Clojure for Ty …" } ``` Note: markdown is shortened here; the real response carries the whole post (14,297 characters). ### GET /api/v1/talks Talks and podcasts, newest first. Each item has title, year, a one-line description, and a link. operationId: listTalks. MCP tool: list_talks_and_open_source. ```sh curl -s https://furkanbayraktar.com/api/v1/talks ``` 200 application/json ```json { "items": [ { "title": "Polylith", "year": "2026", "detail": "On Polylith and how it evolved, on the defn podcast with Joakim Tengstrand.", "href": "https://open.spotify.com/episode/15vB9Cyug2UM7B62lTl0UC" } ], "total": 6 } ``` Note: items is shortened here to the first of 6. ### GET /api/v1/open-source Open source work. Each item has title, year, a one-line description, and a link. operationId: listOpenSource. MCP tool: list_talks_and_open_source. ```sh curl -s https://furkanbayraktar.com/api/v1/open-source ``` 200 application/json ```json { "items": [ { "title": "Polylith", "year": "2017", "detail": "A software architecture that applies functional thinking at the system scale. Co-developer since 2017, with Joakim Tengstrand and James Trunk; long-time contributor to the poly tool and its ecosystem.", "href": "https://polylith.gitbook.io/polylith" } ], "total": 3 } ``` Note: items is shortened here to the first of 3. ## Responses Success responses are application/json with Cache-Control: public, max-age=3600 and Access-Control-Allow-Origin: *, so browsers can call the API from any origin. Dates are ISO 8601 (YYYY-MM-DD). Post bodies are Markdown. List responses carry items and total; the posts list also carries nextCursor, which is null on the last page. ## Errors Errors are JSON problem details (RFC 9457) with Content-Type application/problem+json, never HTML. Every error carries type, title, status, code, detail, hint, and instance. The code is stable and safe to branch on; the hint says what to do next. - 400 `invalid_parameter`: A query parameter failed validation. The detail names the parameter and the values it accepts. - 404 `not_found`: No endpoint exists at this path. GET /api/v1 lists every endpoint. - 404 `post_not_found`: No post has this slug. GET /api/v1/posts lists valid slugs. - 405 `method_not_allowed`: The API is read-only; only GET, HEAD, and OPTIONS are accepted. The Allow header lists the accepted methods. - 500 `internal_error`: The server failed while building the response. Retry the request later. ```sh curl -s https://furkanbayraktar.com/api/v1/posts/missing ``` 404 application/problem+json ```json { "type": "https://furkanbayraktar.com/api#error-post_not_found", "title": "Not Found", "status": 404, "code": "post_not_found", "detail": "No post with slug \"missing\".", "hint": "Valid slugs: the-wrong-question-about-type-systems. GET /api/v1/posts lists them with titles.", "instance": "/api/v1/posts/missing" } ``` ## Versioning The version is in the path. v1 is current. Within a version fields are only added, never removed or renamed. If a version is ever retired, its responses will carry Deprecation and Sunset headers for at least six months before it stops answering, and this page will say so. ## Rate limits No rate limit is enforced. Responses are cacheable for one hour; a client that caches them will never need more than a handful of requests. ## Specification - OpenAPI 3.1: https://furkanbayraktar.com/openapi.json - API catalog (RFC 9727): https://furkanbayraktar.com/.well-known/api-catalog - This page as HTML: https://furkanbayraktar.com/api - MCP server: https://furkanbayraktar.com/mcp (descriptor at https://furkanbayraktar.com/.well-known/mcp.json) The MCP server at /mcp exposes the same four capabilities as tools. Use whichever fits the client; both read from the same content.