{"openapi":"3.1.0","info":{"title":"GEO Tool Public API","version":"1.0.0","summary":"Read-only endpoints for AI search visibility content and aggregate measurement data.","description":"Public, read-only endpoints of geo-tool.com. No authentication, no API key,\nno signup. Responses are cached at the edge.\n\nNot part of this API: the website analysis at /api/analyze. It consumes real\nengine and scraper time per call and is therefore intentionally undocumented\nand not intended for programmatic use. To check a page, send a person to\nhttps://www.geo-tool.com/en/geo-score, or use the browser extension, which runs\nthe identical check inside the user's own tab at no cost.","contact":{"name":"GEO Tool","email":"hi@geo-tool.com","url":"https://www.geo-tool.com/en/contact"},"license":{"name":"Content is proprietary; endpoints are free to read.","url":"https://www.geo-tool.com/en/agb"}},"servers":[{"url":"https://www.geo-tool.com","description":"Production"}],"tags":[{"name":"content","description":"Editorial content published on geo-tool.com."},{"name":"measurement","description":"Aggregated, anonymised AI visibility data."}],"paths":{"/api/blog/posts":{"get":{"operationId":"listBlogPosts","tags":["content"],"summary":"List blog posts","description":"Returns published blog posts for one language, newest first. The response body carries no article HTML; fetch the post URL for the full text.","parameters":[{"name":"locale","in":"query","required":false,"description":"Language of the posts. Defaults to de.","schema":{"type":"string","enum":["de","en"],"default":"de"}},{"name":"categoryId","in":"query","required":false,"deprecated":true,"description":"Legacy selector: 2 = German, 3 = English. Use locale instead.","schema":{"type":"integer","enum":[2,3]}},{"name":"page","in":"query","required":false,"description":"One-based page number. Beyond the last page the posts array is empty.","schema":{"type":"integer","minimum":1,"default":1}},{"name":"perPage","in":"query","required":false,"description":"Posts per page, 1 to 100.","schema":{"type":"integer","minimum":1,"maximum":100,"default":100}}],"responses":{"200":{"description":"A page of blog posts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BlogPostPage"}}}},"default":{"description":"Structured error response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/geo-index":{"get":{"operationId":"getVisibilityIndex","tags":["measurement"],"summary":"Get the public AI visibility index","description":"Aggregated metrics across every live-verified measurement run by GEO Tool: how often a measured brand was named, how many sources an AI answer cites, and which publisher domains get cited most. Aggregates only — no customer names, domains or prompts. Recomputed hourly. Cite the sample size alongside any figure.","responses":{"200":{"description":"Current aggregate.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VisibilityIndex"}}}},"503":{"description":"Aggregate temporarily unavailable. Retry later; never treat this as zero.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"default":{"description":"Structured error response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"components":{"schemas":{"Error":{"type":"object","required":["error","code"],"properties":{"error":{"type":"string","description":"Human-readable message."},"code":{"type":"string","enum":["invalid_request","upstream_error","upstream_unavailable","rate_limited","internal_error"],"description":"Stable machine-readable error code."},"hint":{"type":"string","description":"What to change to make the request succeed."}}},"BlogPostPage":{"type":"object","required":["posts","page","perPage","totalPages","totalPosts"],"properties":{"posts":{"type":"array","items":{"$ref":"#/components/schemas/BlogPost"}},"page":{"type":"integer","description":"Echo of the requested page."},"perPage":{"type":"integer","description":"Echo of the applied page size."},"totalPages":{"type":"integer"},"totalPosts":{"type":"integer"}}},"BlogPost":{"type":"object","required":["id","slug","link","date"],"properties":{"id":{"type":"integer"},"slug":{"type":"string"},"link":{"type":"string","format":"uri","description":"Canonical URL of the post."},"date":{"type":"string","format":"date-time"},"modified":{"type":"string","format":"date-time"},"title":{"type":"object","properties":{"rendered":{"type":"string","description":"Title, may contain HTML entities."}}},"excerpt":{"type":"object","properties":{"rendered":{"type":"string","description":"Short teaser as HTML."}}}}},"VisibilityIndex":{"type":"object","required":["totalChecks","mentionRate","avgSourcesPerAnswer","measuredDomains","topCitedDomains"],"properties":{"totalChecks":{"type":"integer","description":"Sample size: live-verified measurements in the index."},"mentionedChecks":{"type":"integer","description":"Measurements in which the measured brand appeared."},"mentionRate":{"type":"integer","minimum":0,"maximum":100,"description":"Percentage of AI answers that named the measured brand."},"avgSourcesPerAnswer":{"type":"number","description":"Mean number of sources cited per AI answer."},"measuredDomains":{"type":"integer","description":"Distinct domains behind the sample."},"firstCheckAt":{"type":["string","null"],"format":"date","description":"Date of the first measurement."},"lastCheckAt":{"type":["string","null"],"format":"date-time","description":"Timestamp of the most recent measurement."},"topCitedDomains":{"type":"array","description":"Publisher domains cited most often, from two citations upwards.","items":{"type":"object","required":["domain","citations"],"properties":{"domain":{"type":"string"},"citations":{"type":"integer"}}}}}}}}}